CRC処理をロジック化
CRCをVerilogでLogic化しました。
通信系や制御系でCRCを設計していましたが、特定用途だったため、生成多項式も固定、bit幅も固定、右/左送りも固定だったため、汎用性の高いCRCを作成してみました。
1クロックで処理するため、半導体の設計では定番に近い構成になると思います。
汎用性が高いと言っても、検証は入力データ8bit、CRCは8,16,32で行い、合成/マッピングは、入力データ8bit、CRC16bitで行いました。
機能仕様
- 入力データは最大32bit*1の入力が可能。
- CRC結果は最大32bit*2の出力が可能。
- 生成多項式はレジスタで設定可能。
- CRCの初期値はレジスタで設定可能。
- CRC結果の反転が可能。
- 入力データのMSB/LSBのbit反転が設定可能。
- CRC結果のMSB/LSBのbit反転が可能。
- 各レジスタ(上記3~7のレジスタ)のリセット時の初期値は、parameterで設定可能。
- parameter PRM_FIXが"0"の場合、レジスタが有効となり、"1"の場合はレジスタ(上記3~7のレジスタ)はAvalon-MMからの設定は不可となり、パラメータ設定のみ有効となります。(ただし、リードは可能)
操作仕様
【parameter PRM_FIX=0】
1、レジスタに初期設定を行います。(アドレス=0x10以降)
設定するレジスタは以下の通りです。
0x10:CRC初期値
0x14:生成多項式
0x18:CRC値反転
0x1C:bit反転([0]=入力データ、[1]=CRC結果)
※CRC初期値、生成多項式、CRC値反転のレジスタbit幅は、
parameter CRC_WIDTHの設定値に依存します。
2、アドレス0x00にライトを行います。
これにより、CRC結果がアドレス0x10で設定した値(CRC初期値)に
初期化します。
尚、アドレス0x00にライトするデータは任意の値で構いません。
3、アドレス0x04に入力データをライトします。
ライトする度にライトデータをCRC処理します。
尚、有効bit幅は、parameter DAT_WIDTHの設定値に依存します。
4、アドレス0x08をリードします。
上記3でライトした分のデータのCRC結果がリードできます。
尚、有効bit幅は、parameter CRC_WIDTHの設定値に依存します。
【parameter PRM_FIX=1】
CRC処理で必要な設定値はparameterで設定した値に固定されるため、
上記「【parameter PRM_FIX=0】」の場合の2~4の手順でCRC結果が得られます。
IOタイミング
Avalon-MM I/F信号
処理内容
入力データ(複数ビット)からCRC結果を1クロックで求めるため、一般的なCRC回路を並列化します。
以下に一般的なCRC回路例を示します。(1bit毎に処理を行う回路)
尚、例として使用する「一般的なCRC回路」の生成多項式は、以下の通りです。
上記一般的なCRC回路を入力ビット毎に展開し、使用します。
以下が、上記回路を並列で処理した場合の回路構成となります。
尚、図中では、入力データ(in_dat[7:0])=0x82を入力することにより、CRC結果(res_crc[7:0])=0x87が得られます。
CRC結果=0x87が得られる計算式は以下の通りです。
※上記回路の演算方法と下記計算方法は少し違います。
上記回路では生成多項式の"1"の時だけXORを行い、
下記計算式は入力データ又はXOR結果の上位ビットが"1"の時だけXORを行います。
補足)
回路構成の場合、8bit分のパディングは不要となります。
(演算回数を増やす必要はありません。)
ソースコード
verilog-HDLのソースコードは以下の通りです。
モジュールトップ名は、「NML_CRC」となります。
module NML_CRC #( parameter P_IND_BIT_WIDTH = 8 , // 入力データのbit幅 parameter P_CRC_BIT_WIDTH = 16 , // CRCのbit幅 (32まで) parameter P_INI_CRCV = 16'h0000 , // レジスタ初期値 CRC初期値 parameter P_INI_POLY = 16'h1021 , // レジスタ初期値 生成多項式 parameter P_INI_XOROUT = 16'h0000 , // レジスタ初期値 CRC値反転 parameter P_INI_REFI = 1'b0 , // レジスタ初期値 入力bit反転(0:正転 1:反転) parameter P_INI_REFO = 1'b0 , // レジスタ初期値 CRC bit反転(0:正転 1:反転) parameter P_PRM_FIX = 1'b0 // モード レジスタ固定(1:上記P_INI_*のみ有効[書き込みできない]) ) ( input wire CLK , // システムクロック input wire RESET_N , // システムリセット input wire AVA_WEN , // Avalon-MM(Slave側) ライトイネーブル input wire AVA_REN , // Avalon-MM(Slave側) リードイネーブル input wire [ 5:0] AVA_ADD , // Avalon-MM(Slave側) リード/ライトアドレス input wire [31:0] AVA_WDT , // Avalon-MM(Slave側) ライトデータ output wire AVA_WIT , // Avalon-MM(Slave側) ウェイトリクエスト output reg AVA_RVL , // Avalon-MM(Slave側) リードデータイネーブル output reg [31:0] AVA_RDT , // Avalon-MM(Slave側) リードデータ output reg [31:0] MON_CRC // CRC結果 ); // @@ // @@ Avalon-MM I/F // @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ reg in_ava_wen; reg in_ava_ren; reg [ 5:0] in_ava_add; reg [31:0] in_ava_wdt; wire ot_ava_wit; wire ot_ava_rvl; wire [31:0] ot_ava_rdt; always @( posedge CLK or negedge RESET_N ) begin if( ~RESET_N ) begin in_ava_wen <= 1'b0; in_ava_ren <= 1'b0; in_ava_add <= 6'h00; in_ava_wdt <= 32'd0; end else begin in_ava_wen <= AVA_WEN; in_ava_ren <= AVA_REN; in_ava_add <= AVA_ADD; in_ava_wdt <= AVA_WDT; end end always @( posedge CLK or negedge RESET_N ) begin if( ~RESET_N ) begin AVA_RVL <= 1'b0; AVA_RDT <= 32'd0; end else begin AVA_RVL <= ot_ava_rvl; AVA_RDT <= ot_ava_rdt; end end assign AVA_WIT = ( AVA_REN & ot_ava_wit ); // ++ アクセス制御 // +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ wire acc_wen = in_ava_wen; reg acc_ren; wire [ 3:0] acc_add = in_ava_add[5:2]; wire [31:0] acc_wdt = in_ava_wdt; always @( posedge CLK or negedge RESET_N ) begin if( ~RESET_N ) acc_ren <= 1'b0; else acc_ren <= ( AVA_REN & (~in_ava_ren) ) ? 1'b1 : 1'b0; end // ++ リード/ライト制御 (デコード) // +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ wire ade_ini = ( acc_add==4'h0 ); // [R/W] 初期化 wire ade_dat = ( acc_add==4'h1 ); // [R/W] 入力データ wire ade_crc = ( acc_add==4'h2 ); // [ R ] CRCデータ wire ade_prm_info = ( acc_add==4'h3 ); // [ R ] パラメータ情報 wire ade_crc_init = ( acc_add==4'h4 ); // [R/W] CRC初期値 wire ade_crc_poly = ( acc_add==4'h5 ); // [R/W] 生成多項式 wire ade_crc_xoro = ( acc_add==4'h6 ); // [R/W] CRC値反転 wire ade_crc_refl = ( acc_add==4'h7 ); // [R/W] bit反転 wire set_ini = ( acc_wen & ade_ini ); wire set_dat = ( acc_wen & ade_dat ); wire set_crc = 1'b0; wire set_prm_info = 1'b0; wire set_crc_init = ( acc_wen & ade_crc_init & (P_PRM_FIX[0]==1'b0) ); wire set_crc_poly = ( acc_wen & ade_crc_poly & (P_PRM_FIX[0]==1'b0) ); wire set_crc_xoro = ( acc_wen & ade_crc_xoro & (P_PRM_FIX[0]==1'b0) ); wire set_crc_refl = ( acc_wen & ade_crc_refl & (P_PRM_FIX[0]==1'b0) ); wire get_ini = ( acc_ren & ade_ini ); wire get_dat = ( acc_ren & ade_dat ); wire get_crc = ( acc_ren & ade_crc ); wire get_prm_info = ( acc_ren & ade_prm_info ); wire get_crc_init = ( acc_ren & ade_crc_init ); wire get_crc_poly = ( acc_ren & ade_crc_poly ); wire get_crc_xoro = ( acc_ren & ade_crc_xoro ); wire get_crc_refl = ( acc_ren & ade_crc_refl ); // ++ レジスタ // +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ // ----- [制御系レジスタ] ----- reg reg_ini_enb; // CRC結果初期化用イネーブル reg reg_dat_enb; // 入力データ用イネーブル reg [31:0] reg_dat; // 入力データ用レジスタ wire [31:0] bit_dat = { {32-P_IND_BIT_WIDTH{1'b0}} , {P_IND_BIT_WIDTH{1'b1}} }; always @( posedge CLK or negedge RESET_N ) begin if( ~RESET_N ) reg_ini_enb <= 1'b0; else reg_ini_enb <= set_ini; end always @( posedge CLK or negedge RESET_N ) begin if( ~RESET_N ) reg_dat_enb <= 1'b0; else reg_dat_enb <= set_dat; end always @( posedge CLK or negedge RESET_N ) begin if( ~RESET_N ) reg_dat <= 32'd0; else if( set_dat ) reg_dat <= acc_wdt & bit_dat; else reg_dat <= reg_dat; end // ----- [パラメータ系レジスタ] ----- reg [31:0] reg_prm_crcv ; reg [31:0] reg_prm_poly ; reg [31:0] reg_prm_xorout; reg [ 1:0] reg_prm_refl; wire [31:0] bit_crc = { {32-P_CRC_BIT_WIDTH{1'b0}} , {P_CRC_BIT_WIDTH{1'b1}} }; always @( posedge CLK or negedge RESET_N ) begin if( ~RESET_N ) begin reg_prm_crcv <= { {32-P_CRC_BIT_WIDTH{1'b0}} , P_INI_CRCV[P_CRC_BIT_WIDTH-1:0] }; reg_prm_poly <= { {32-P_CRC_BIT_WIDTH{1'b0}} , P_INI_POLY[P_CRC_BIT_WIDTH-1:0] }; reg_prm_xorout <= { {32-P_CRC_BIT_WIDTH{1'b0}} , P_INI_XOROUT[P_CRC_BIT_WIDTH-1:0] }; reg_prm_refl[0] <= P_INI_REFI[0]; reg_prm_refl[1] <= P_INI_REFO[0]; end else begin reg_prm_crcv <= (set_crc_init) ? acc_wdt & bit_crc : reg_prm_crcv ; reg_prm_poly <= (set_crc_poly) ? acc_wdt & bit_crc : reg_prm_poly ; reg_prm_xorout <= (set_crc_xoro) ? acc_wdt & bit_crc : reg_prm_xorout; reg_prm_refl <= (set_crc_refl) ? acc_wdt[1:0] : reg_prm_refl ; end end // ----- [リードオンリー系レジスタ] ----- wire [31:0] res_crc; reg [31:0] reg_crc; wire [31:0] reg_prm_info = { 15'd0 , P_PRM_FIX[0] // モード , P_CRC_BIT_WIDTH[7:0] // CRCのbit幅 , P_IND_BIT_WIDTH[7:0]}; // 入力データのbit幅 always @( posedge CLK or negedge RESET_N ) begin if( ~RESET_N ) reg_crc <= { {32-P_CRC_BIT_WIDTH{1'b0}} , P_INI_CRCV[P_CRC_BIT_WIDTH-1:0] }; else reg_crc <= { {32-P_CRC_BIT_WIDTH{1'b0}} , res_crc[ P_CRC_BIT_WIDTH-1:0] }; end always @( posedge CLK or negedge RESET_N ) begin if( ~RESET_N ) MON_CRC <= { {32-P_CRC_BIT_WIDTH{1'b0}} , P_INI_CRCV[P_CRC_BIT_WIDTH-1:0] }; else MON_CRC <= reg_crc; end // ++ レジスタリード制御 // +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ assign ot_ava_rvl = ( acc_ren ); assign ot_ava_rdt = ( ( get_ini ) ? { {31{1'b0}}, reg_ini_enb } : 32'd0 ) | ( ( get_dat ) ? reg_dat : 32'd0 ) | ( ( get_crc ) ? reg_crc : 32'd0 ) | ( ( get_prm_info ) ? reg_prm_info : 32'd0 ) | ( ( get_crc_init ) ? reg_prm_crcv : 32'd0 ) | ( ( get_crc_poly ) ? reg_prm_poly : 32'd0 ) | ( ( get_crc_xoro ) ? reg_prm_xorout: 32'd0 ) | ( ( get_crc_refl ) ? { {30{1'b0}}, reg_prm_refl} : 32'd0 ) ; assign ot_ava_wit = ~ot_ava_rvl; // @@ // @@ CRC calculation core // @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ NML_CRC_CORE #( .P_IND_BIT_WIDTH(P_IND_BIT_WIDTH ), // parameter P_IND_BIT_WIDTH = 8 , // 入力データのbit幅 .P_CRC_BIT_WIDTH(P_CRC_BIT_WIDTH ), // parameter P_CRC_BIT_WIDTH = 16 , // CRCのbit幅 .P_INI_CRCV (P_INI_CRCV ) // parameter P_INI_CRCV = 16'h0000 // レジスタ初期値 CRC初期値 ) UnCORE( .CLK (CLK ), // input wire CLK .RESET_N (RESET_N ), // input wire RESET_N .INIT (reg_ini_enb ), // input wire INIT .ENABLE (reg_dat_enb ), // input wire ENABLE .DAT_REFL (reg_prm_refl[ 0]), // input wire DAT_REFL .CRC_INIT (reg_prm_crcv[ P_CRC_BIT_WIDTH-1:0]), // input wire [P_CRC_BIT_WIDTH-1:0] CRC_INIT .CRC_POLY (reg_prm_poly[ P_CRC_BIT_WIDTH-1:0]), // input wire [P_CRC_BIT_WIDTH-1:0] CRC_POLY .CRC_XORO (reg_prm_xorout[P_CRC_BIT_WIDTH-1:0]), // input wire [P_CRC_BIT_WIDTH-1:0] CRC_XORO .CRC_REFL (reg_prm_refl[ 1]), // input wire CRC_REFL .DAT (reg_dat[ P_IND_BIT_WIDTH-1:0]), // input wire [P_IND_BIT_WIDTH-1:0] DAT .CRC (res_crc[ P_CRC_BIT_WIDTH-1:0]) // output wire [P_CRC_BIT_WIDTH-1:0] CRC ); endmodule // ** // ** CRC Core // ** // ***************************************************************************** module NML_CRC_CORE #( parameter P_IND_BIT_WIDTH = 8 , // 入力データのbit幅 parameter P_CRC_BIT_WIDTH = 16 , // CRCのbit幅(32まで) parameter P_INI_CRCV = 16'h0000 // レジスタ初期値 CRC初期値 ) ( input wire CLK , // システムクロック input wire RESET_N , // システムリセット input wire INIT , // CRC結果初期化イネーブル input wire ENABLE , // 入力データイネーブル input wire DAT_REFL , // 入力データbit反転 input wire [P_CRC_BIT_WIDTH-1:0] CRC_INIT , // CRC結果初期値 input wire [P_CRC_BIT_WIDTH-1:0] CRC_POLY , // 生成多項式 input wire [P_CRC_BIT_WIDTH-1:0] CRC_XORO , // CRC値反転 input wire CRC_REFL , // CRC結果bit反転 input wire [P_IND_BIT_WIDTH-1:0] DAT , // 入力データ output wire [P_CRC_BIT_WIDTH-1:0] CRC // CRC結果 ); // ++ // ++ Input FF // +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ reg in_enb; reg [P_IND_BIT_WIDTH-1:0] in_dat; always @( posedge CLK or negedge RESET_N ) begin if( ~RESET_N ) in_enb <= 1'b0; else in_enb <= ENABLE; end wire [P_IND_BIT_WIDTH-1:0] dat_inv; NML_CRC_INVERT #(.WIDTH(P_IND_BIT_WIDTH)) UnDAT_INVERT(.DI(DAT),.DO(dat_inv)); always @( posedge CLK or negedge RESET_N ) begin if( ~RESET_N ) in_dat <= {P_IND_BIT_WIDTH{1'b0}}; else if( ENABLE ) in_dat <= ( DAT_REFL ) ? dat_inv : DAT; else in_dat <= in_dat; end // ++ // ++ CRC calculation // +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ logic [P_CRC_BIT_WIDTH-1:0] pre_crc; logic [P_CRC_BIT_WIDTH-1:0] out_crc; logic [P_CRC_BIT_WIDTH-1:0] res_crc[0:P_IND_BIT_WIDTH]; generate genvar p; assign res_crc[(P_IND_BIT_WIDTH-1)-0+1] = pre_crc; for(p=0;p<P_IND_BIT_WIDTH;p=p+1) begin : GenDatBit NML_CRC_CALC #( .P_CRC_BIT_WIDTH(P_CRC_BIT_WIDTH) // parameter P_CRC_BIT_WIDTH = 16 // CRCビット幅 ) UnCRC_CALC( .POL_CRC (CRC_POLY ), // input logic [P_CRC_BIT_WIDTH-1:0] POL_CRC , // 生成多項式 .PRE_CRC (res_crc[(P_IND_BIT_WIDTH-1)-p+1] ), // input logic [P_CRC_BIT_WIDTH-1:0] PRE_CRC , // 前回のCRC結果 .BIT_DAT ( in_dat[(P_IND_BIT_WIDTH-1)-p] ), // input logic BIT_DAT , // 入力値 .RES_CRC (res_crc[(P_IND_BIT_WIDTH-1)-p] ) // output logc [P_CRC_BIT_WIDTH-1:0] RES_CRC // CRC結果 ); end endgenerate always @( posedge CLK or negedge RESET_N ) begin if( ~RESET_N ) pre_crc <= P_INI_CRCV; else if( INIT ) pre_crc <= CRC_INIT; else if( in_enb ) pre_crc <= res_crc[0]; else pre_crc <= pre_crc; end wire [P_CRC_BIT_WIDTH-1:0] crc_inv; NML_CRC_INVERT #(.WIDTH(P_CRC_BIT_WIDTH)) UnCRC_INVERT(.DI(pre_crc),.DO(crc_inv)); always @( posedge CLK or negedge RESET_N ) begin if( ~RESET_N ) out_crc <= P_INI_CRCV; else if( CRC_REFL ) out_crc <= crc_inv ^ CRC_XORO; else out_crc <= pre_crc ^ CRC_XORO; end assign CRC = out_crc; endmodule // ** // ** CRC generation // ** CRC bit毎に計算 // ***************************************************************************** module NML_CRC_CALC #( parameter P_CRC_BIT_WIDTH = 16 // CRCビット幅 ) ( input logic [P_CRC_BIT_WIDTH-1:0] POL_CRC , // 生成多項式 input logic [P_CRC_BIT_WIDTH-1:0] PRE_CRC , // 前回のCRC結果 input logic BIT_DAT , // 入力値 output logic [P_CRC_BIT_WIDTH-1:0] RES_CRC // CRC結果 ); logic [P_CRC_BIT_WIDTH :0] cyc_crc; logic [P_CRC_BIT_WIDTH-1:0] pre_xor; assign cyc_crc = {PRE_CRC[P_CRC_BIT_WIDTH-1:0],1'b0}; generate genvar p; for(p=0;p<P_CRC_BIT_WIDTH;p=p+1) begin : GenCrcCalc assign pre_xor[p] = BIT_DAT ^ cyc_crc[P_CRC_BIT_WIDTH] ^ cyc_crc[p]; assign RES_CRC[p] = ( POL_CRC[p] ) ? pre_xor[p] : cyc_crc[p] ; end endgenerate endmodule // ** // ** Bit反転 // ** // ***************************************************************************** module NML_CRC_INVERT #( parameter WIDTH = 16 // Bit Width ) ( input logic [WIDTH-1:0] DI , output logic [WIDTH-1:0] DO ); generate genvar p; for(p=0;p<WIDTH;p=p+1) begin : GenInvert assign DO[p] = DI[(WIDTH-1)-p]; end endgenerate endmodule
上記ソースは、Avalon-MMでI/Fを行っていますが、CRCの演算のコアな部分は、モジュール「NML_CRC_CORE」となります。
従って、別Logicに実装したい場合は、モジュール「NML_CRC_CORE」を使用するだけで良いようになっています。
単体検証
モジュールNML_CRCをトップとしたシミュレーション環境を作成し、検証を行いました。
検証方法
入力データは8bitデータとし、0x31~0x39の9Byte入力します。
CRC結果は、8bit、16bit、32bitの3ケースで検証を行います。
得られたCRC結果は、期待値と比較します。
期待値は、ネット上にあったサイト(CRCプログラム)から得た値と比較します。
検証パターン
検証パターン(CRC処理の設定値と期待値)は以下の通りとなります。
8bitCRC:13パターン
16bitCRC:24パターン
32bitCRC:9パターン
検証結果
期待値と一致しました。
ModelSimでシミュレーションを行い、得られたCRC結果と期待値とを比較し、Log表示させたものを以下に示します。(抜粋したもの)
※Log中の「Error」が期待値との不一致数を示します。
Log中では"0"なので、不一致数は0です。
CRC32の時のシミュレーション波形は以下の通りです。
尚、入力データは連続で入力しています。
テストベンチソース
単体検証でのテストベンチソースは以下の通りです。
`timescale 1ns/1ps module TB_TOP; parameter P_IND_BIT_WIDTH = 8 ; // 入力データのbit幅 parameter P_CRC_BIT_WIDTH = 16 ; // CRCのbit幅 (32まで) parameter P_INI_CRCV = 16'h0000 ; // レジスタ初期値 CRC初期値 parameter P_INI_POLY = 16'h1021 ; // レジスタ初期値 生成多項式 parameter P_INI_XOROUT = 16'h0000 ; // レジスタ初期値 CRC値反転 parameter P_INI_REFI = 1'b0 ; // レジスタ初期値 入力bit反転(0:正転 1:反転) parameter P_INI_REFO = 1'b0 ; // レジスタ初期値 CRC bit反転(0:正転 1:反転) parameter P_PRM_FIX = 1'b0 ; // モード レジスタ固定(1:上記P_INI_*のみ有効[書き込みできない]) integer PERIOD = 0; reg CLK = 0; // システムクロック reg RESET_N = 0; // システムリセット reg AVA_WEN = 0; // Avalon-MM ライトイネーブル reg AVA_REN = 0; // Avalon-MM リードイネーブル reg [ 5:0] AVA_ADD = 0; // Avalon-MM リード/ライトアドレス reg [31:0] AVA_WDT = 0; // Avalon-MM ライトデータ wire AVA_WIT ; // Avalon-MM ウェイトリクエスト wire AVA_RVL ; // Avalon-MM リードデータイネーブル wire [31:0] AVA_RDT ; // Avalon-MM リードデータ wire [31:0] MON_CRC ; // CRC結果 // ++ タイミング設定 // +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ // Clock波形 real tcCLK = 20.000; real tpCLK = 5.000; real tnCLK = 15.000; // -- // -- テストシナリオ // ------------------------------------------------------------------- //`define CRC_8BIT `define CRC_32BIT //`define CRC_16BIT `include "TB_EXP.sv" integer TypeNum = 0; T_EXP CRC_TYPE; integer error = 0; `ifdef CRC_8BIT defparam TARGET.P_CRC_BIT_WIDTH = 8; `elsif CRC_32BIT defparam TARGET.P_CRC_BIT_WIDTH = 32; `else defparam TARGET.P_CRC_BIT_WIDTH = 16; `endif integer BurstAdd; reg [31:0] BurstMem[0:256]; reg [31:0] RdData; initial begin #0; repeat(10) @(PERIOD); @(posedge CLK); #1 RESET_N = 1; repeat(10) @(PERIOD); // ** 初期値リード // ***************************************************** RdCPU('h00,RdData); // [R/W] 初期化 RdCPU('h04,RdData); // [R/W] 入力データ RdCPU('h08,RdData); // [ R ] CRCデータ RdCPU('h0C,RdData); // [ R ] パラメータ固定モード RdCPU('h10,RdData); // [R/W] CRC初期値 RdCPU('h14,RdData); // [R/W] 生成多項式 RdCPU('h18,RdData); // [R/W] CRC値反転 RdCPU('h1C,RdData); // [R/W] bit反転 // ** CRC処理 // ***************************************************** for(BurstAdd=0;BurstAdd<9;BurstAdd=BurstAdd+1) BurstMem[BurstAdd] = 'h31+BurstAdd; `ifdef CRC_8BIT $display(" <<<<< CRC 8bit >>>>> "); for(TypeNum=0;TypeNum<CRC08_NUM;TypeNum=TypeNum+1) begin CRC_TYPE = CRC08_TYP[TypeNum]; `elsif CRC_32BIT $display(" <<<<< CRC 32bit >>>>> "); for(TypeNum=0;TypeNum<CRC32_NUM;TypeNum=TypeNum+1) begin CRC_TYPE = CRC32_TYP[TypeNum]; `else $display(" <<<<< CRC 16bit >>>>> "); for(TypeNum=0;TypeNum<CRC16_NUM;TypeNum=TypeNum+1) begin CRC_TYPE = CRC16_TYP[TypeNum]; `endif WrCPU('h10,CRC_TYPE.INIT); // [R/W] CRC初期値 WrCPU('h14,CRC_TYPE.POLY); // [R/W] 生成多項式 WrCPU('h18,CRC_TYPE.XORO); // [R/W] CRC値反転 WrCPU('h1C,CRC_TYPE.REFL); // [R/W] bit反転 WrCPU('h00,'h0000_0000); // [R/W] 初期化 WrCPU_BURST('h04, 9); // [R/W] 入力データ repeat(20) @(PERIOD); RdCPU('h08,RdData); // [ R ] CRCデータ if( CRC_TYPE.RSLT !== RdData ) error = error + 1; $display(" >>> Type[%d](Error=%d) : %h(Exp) %h(Rd) [Parameter %p]",TypeNum[7:0],error[7:0],CRC_TYPE.RSLT,RdData,CRC_TYPE); repeat(20) @(PERIOD); end if( error != 0 ) $display(" ##### Error : %d",error); else $display(" >>>>> Error : %d",error); // ** 終了 // ***************************************************** repeat(20) @(PERIOD); $finish(); end // -- // -- Clock生成 // ------------------------------------------------------------------- initial begin forever begin fork #(tpCLK) CLK = 1; #(tnCLK) CLK = 0; #(tcCLK) PERIOD = PERIOD+1; join end end // -- // -- ターゲットのインスタンス // ------------------------------------------------------------------- NML_CRC #( .P_IND_BIT_WIDTH(P_IND_BIT_WIDTH), // parameter P_IND_BIT_WIDTH = 8 , // 入力データのbit幅 .P_CRC_BIT_WIDTH(P_CRC_BIT_WIDTH), // parameter P_CRC_BIT_WIDTH = 16 , // CRCのbit幅 (32まで) .P_INI_CRCV (P_INI_CRCV ), // parameter P_INI_CRCV = 16'h0000 , // レジスタ初期値 CRC初期値 .P_INI_POLY (P_INI_POLY ), // parameter P_INI_POLY = 16'h1021 , // レジスタ初期値 生成多項式 .P_INI_XOROUT (P_INI_XOROUT ), // parameter P_INI_XOROUT = 16'h0000 , // レジスタ初期値 CRC値反転 .P_INI_REFI (P_INI_REFI ), // parameter P_INI_REFI = 1'b0 , // レジスタ初期値 入力bit反転(0:正転 1:反転) .P_INI_REFO (P_INI_REFO ), // parameter P_INI_REFO = 1'b0 , // レジスタ初期値 CRC bit反転(0:正転 1:反転) .P_PRM_FIX (P_PRM_FIX ) // parameter P_PRM_FIX = 1'b0 // モード レジスタ固定(1:上記P_INI_*のみ有効[書き込みできない]) ) TARGET ( .CLK (CLK ), // input wire CLK , // システムクロック .RESET_N (RESET_N ), // input wire RESET_N , // システムリセット .AVA_WEN (AVA_WEN ), // input wire AVA_WEN , // Avalon-MM(Slave側) ライトイネーブル .AVA_REN (AVA_REN ), // input wire AVA_REN , // Avalon-MM(Slave側) リードイネーブル .AVA_ADD (AVA_ADD ), // input wire [ 5:0] AVA_ADD , // Avalon-MM(Slave側) リード/ライトアドレス .AVA_WDT (AVA_WDT ), // input wire [31:0] AVA_WDT , // Avalon-MM(Slave側) ライトデータ .AVA_WIT (AVA_WIT ), // output wire AVA_WIT , // Avalon-MM(Slave側) ウェイトリクエスト .AVA_RVL (AVA_RVL ), // output reg AVA_RVL , // Avalon-MM(Slave側) リードデータイネーブル .AVA_RDT (AVA_RDT ), // output reg [31:0] AVA_RDT , // Avalon-MM(Slave側) リードデータ .MON_CRC (MON_CRC ) // output reg [31:0] MON_CRC // CRC結果 ); // -- // -- Task // ------------------------------------------------------------------- task WrCPU( input [31:0] Addr, input [31:0] Data ); reg Comp; begin @(posedge CLK) begin AVA_WEN<=1; AVA_ADD<=Addr; AVA_WDT<=Data; end @(posedge CLK) begin AVA_WEN<=0; AVA_ADD<=0; AVA_WDT<=0; end end endtask task WrCPU_BURST( input [31:0] Addr, input integer Size ); integer addr; begin addr = 0; #1; repeat(Size) begin @(posedge CLK) begin AVA_WEN<=1; AVA_ADD<=Addr; AVA_WDT<=BurstMem[addr]; addr<=addr+1; end end @(posedge CLK) begin AVA_WEN<=0; AVA_ADD<=0; AVA_WDT<=0; end end endtask task RdCPU( input [31:0] Addr, output [31:0] Data ); reg Comp; begin Comp = 0; @(posedge CLK) begin AVA_REN<=1; AVA_ADD<=Addr; end while( Comp == 0 ) begin @(posedge CLK) begin if( AVA_WIT===1'b0 ) begin AVA_REN<=0; AVA_ADD<=0; end if( AVA_RVL===1'b1 ) begin Data <= AVA_RDT; Comp <= 1; end end #1; end end endtask endmodule
インクルードファイル"TB_EXP.sv"の内容は以下の通りです。
設定値と期待値を構造体に設定しています。
typedef struct packed { reg [31:0] INIT; reg [31:0] POLY; reg [31:0] XORO; reg [31:0] REFL; reg [31:0] RSLT; } T_EXP; integer CRC08_NUM = 13; integer CRC16_NUM = 24; integer CRC32_NUM = 9; T_EXP CRC08_TYP[0:12]; T_EXP CRC16_TYP[0:23]; T_EXP CRC32_TYP[0: 8]; initial begin CRC08_TYP[ 0] = {32'h00000000,32'h00000007,32'h00000000,32'h00000000,32'h000000F4}; CRC08_TYP[ 1] = {32'h000000FF,32'h0000001D,32'h000000FF,32'h00000000,32'h0000004B}; CRC08_TYP[ 2] = {32'h00000000,32'h0000001D,32'h00000000,32'h00000000,32'h00000037}; CRC08_TYP[ 3] = {32'h000000FF,32'h0000002F,32'h000000FF,32'h00000000,32'h000000DF}; CRC08_TYP[ 4] = {32'h000000FF,32'h0000009B,32'h00000000,32'h00000000,32'h000000DA}; CRC08_TYP[ 5] = {32'h00000000,32'h00000039,32'h00000000,32'h00000003,32'h00000015}; CRC08_TYP[ 6] = {32'h00000000,32'h000000D5,32'h00000000,32'h00000000,32'h000000BC}; CRC08_TYP[ 7] = {32'h000000FF,32'h0000001D,32'h00000000,32'h00000003,32'h00000097}; CRC08_TYP[ 8] = {32'h000000FD,32'h0000001D,32'h00000000,32'h00000000,32'h0000007E}; CRC08_TYP[ 9] = {32'h00000000,32'h00000007,32'h00000055,32'h00000000,32'h000000A1}; CRC08_TYP[10] = {32'h00000000,32'h00000031,32'h00000000,32'h00000003,32'h000000A1}; CRC08_TYP[11] = {32'h000000FF,32'h00000007,32'h00000000,32'h00000003,32'h000000D0}; CRC08_TYP[12] = {32'h00000000,32'h0000009B,32'h00000000,32'h00000003,32'h00000025}; CRC16_TYP[ 0] = {32'h00000000,32'h00001021,32'h00000000,32'h00000000,32'h000031C3}; CRC16_TYP[ 1] = {32'h00000000,32'h00008005,32'h00000000,32'h00000003,32'h0000BB3D}; CRC16_TYP[ 2] = {32'h00001D0F,32'h00001021,32'h00000000,32'h00000000,32'h0000E5CC}; CRC16_TYP[ 3] = {32'h00000000,32'h00008005,32'h00000000,32'h00000000,32'h0000FEE8}; CRC16_TYP[ 4] = {32'h0000FFFF,32'h00001021,32'h00000000,32'h00000000,32'h000029B1}; CRC16_TYP[ 5] = {32'h0000FFFF,32'h0000C867,32'h00000000,32'h00000000,32'h00004C06}; CRC16_TYP[ 6] = {32'h0000800D,32'h00008005,32'h00000000,32'h00000000,32'h00009ECF}; CRC16_TYP[ 7] = {32'h00000000,32'h00000589,32'h00000001,32'h00000000,32'h0000007E}; CRC16_TYP[ 8] = {32'h00000000,32'h00000589,32'h00000000,32'h00000000,32'h0000007F}; CRC16_TYP[ 9] = {32'h00000000,32'h00003D65,32'h0000FFFF,32'h00000003,32'h0000EA82}; CRC16_TYP[10] = {32'h00000000,32'h00003D65,32'h0000FFFF,32'h00000000,32'h0000C2B7}; CRC16_TYP[11] = {32'h0000FFFF,32'h00001021,32'h0000FFFF,32'h00000000,32'h0000D64E}; CRC16_TYP[12] = {32'h00000000,32'h00008005,32'h0000FFFF,32'h00000003,32'h000044C2}; CRC16_TYP[13] = {32'h0000FFFF,32'h00001021,32'h00000000,32'h00000003,32'h00006F91}; CRC16_TYP[14] = {32'h0000B2AA,32'h00001021,32'h00000000,32'h00000003,32'h000063D0}; CRC16_TYP[15] = {32'h00000000,32'h00008BB7,32'h00000000,32'h00000000,32'h0000D0DB}; CRC16_TYP[16] = {32'h00000000,32'h0000A097,32'h00000000,32'h00000000,32'h00000FB3}; CRC16_TYP[17] = {32'h000089EC,32'h00001021,32'h00000000,32'h00000003,32'h000026B1}; CRC16_TYP[18] = {32'h0000FFFF,32'h00008005,32'h0000FFFF,32'h00000003,32'h0000B4C8}; CRC16_TYP[19] = {32'h0000C6C6,32'h00001021,32'h00000000,32'h00000003,32'h0000BF05}; CRC16_TYP[20] = {32'h00000000,32'h00001021,32'h00000000,32'h00000003,32'h00002189}; CRC16_TYP[21] = {32'h0000FFFF,32'h00008005,32'h00000000,32'h00000003,32'h00004B37}; CRC16_TYP[22] = {32'h0000FFFF,32'h00001021,32'h0000FFFF,32'h00000003,32'h0000906E}; CRC16_TYP[23] = {32'h00000000,32'h00001021,32'h00000000,32'h00000000,32'h000031C3}; CRC32_TYP[ 0] = {32'hFFFFFFFF,32'h04C11DB7,32'hFFFFFFFF,32'h00000003,32'hCBF43926}; CRC32_TYP[ 1] = {32'hFFFFFFFF,32'h04C11DB7,32'hFFFFFFFF,32'h00000000,32'hFC891918}; CRC32_TYP[ 2] = {32'hFFFFFFFF,32'h1EDC6F41,32'hFFFFFFFF,32'h00000003,32'hE3069283}; CRC32_TYP[ 3] = {32'hFFFFFFFF,32'hA833982B,32'hFFFFFFFF,32'h00000003,32'h87315576}; CRC32_TYP[ 4] = {32'hFFFFFFFF,32'h04C11DB7,32'h00000000,32'h00000000,32'h0376E6E7}; CRC32_TYP[ 5] = {32'h00000000,32'h04C11DB7,32'hFFFFFFFF,32'h00000000,32'h765E7680}; CRC32_TYP[ 6] = {32'h00000000,32'h814141AB,32'h00000000,32'h00000000,32'h3010BF7F}; CRC32_TYP[ 7] = {32'hFFFFFFFF,32'h04C11DB7,32'h00000000,32'h00000003,32'h340BC6D9}; CRC32_TYP[ 8] = {32'h00000000,32'h000000AF,32'h00000000,32'h00000000,32'hBD0BE338}; end
Excel VBAでCRC期待値作成
今後の用途で利用できるよう、Excel VBAでも期待値となるプログラムを作成しましたので、記載します。
記載は2通りです。
1つはLogicの処理をVBAで表したものとなり、もう一つは一般的なプログラムになります。
Logicの処理をVBAで表したもの
'// '// Verilog記述をプログラム化した場合のCRCトップ関数 '// '// @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ Function NML_CRC( _ ByVal LngCRC_Bits As LongLong, _ ByVal LngCRC_Init As LongLong, _ ByVal LngCRC_Poly As LongLong, _ ByVal LngCRC_Xoro As LongLong, _ ByVal LngCRC_Refi As LongLong, _ ByVal LngCRC_Refo As LongLong, _ ByVal LngDAT_Bits As LongLong, _ ByVal LngDAT_Size As LongLong, _ ByVal DatArea As Range _ ) As LongLong Dim ad As LongLong '// 入力データ数 Dim DAT As LongLong '// 入力データ Dim in_dat As LongLong '// 入力データ(bit正転/反転後) Dim pre_crc As LongLong '// 1つ前のCRC結果 Dim res_crc As LongLong '// CRC結果 Dim out_crc As LongLong '// CRC結果(bit正転/反転後) pre_crc = LngCRC_Init '// CRCの初期化 ad = 0 '// データ取得用変数 While (ad < LngDAT_Size) '// DAT = NML_GET_ARRAY(DatArea, ad) '// データを取得 If (LngCRC_Refi = 1) Then '// 入力データbit反転 in_dat = NML_CRC_INVERT(LngDAT_Bits, DAT) '// 反転 Else '// in_dat = DAT '// 正転 End If '// '// ===== CRC計算コア ===== res_crc = NML_CRC_CORE(LngDAT_Bits, LngCRC_Bits, LngCRC_Poly, pre_crc, in_dat) pre_crc = res_crc ad = ad + 1 Wend If (LngCRC_Refo = 1) Then '// CRC結果bit反転 out_crc = NML_CRC_INVERT(LngCRC_Bits, pre_crc) '// 反転 Else '// out_crc = pre_crc '// 正転 End If '// '// CRC結果値の反転 NML_CRC = WorksheetFunction.Bitxor(out_crc, LngCRC_Xoro) End Function '// 入力データをbit毎 CRC計算 '// ================================================================= Function NML_CRC_CORE( _ ByVal DAT_BITS As LongLong, _ ByVal CRC_BITS As LongLong, _ ByVal CRC_POLY As LongLong, _ ByVal CRC_PRE As LongLong, _ ByVal DAT As LongLong _ ) As LongLong Dim p As LongLong '// bit位置 Dim bit_dat As LongLong '// bit値 Dim pre_crc As LongLong '// 1bit前のCRC結果 Dim res_crc As LongLong '// 1bitのCRC結果 pre_crc = CRC_PRE p = 0 While (p < DAT_BITS) bit_dat = NML_BIT(DAT, (DAT_BITS - 1) - p) res_crc = NML_CRC_CALC(CRC_BITS, CRC_POLY, pre_crc, bit_dat) pre_crc = res_crc p = p + 1 Wend NML_CRC_CORE = res_crc End Function '// CRC bit毎に計算 '// ================================================================= Function NML_CRC_CALC( _ ByVal CRC_BITS As LongLong, _ ByVal POL_CRC As LongLong, _ ByVal pre_crc As LongLong, _ ByVal bit_dat As LongLong _ ) As LongLong Dim p As LongLong Dim cyc_crc As LongLong Dim cyc_crc_p As LongLong Dim cyc_crc_m As LongLong Dim pre_xor_p As LongLong Dim POL_CRC_p As LongLong Dim RES_CRC_p As LongLong Dim res_crc As LongLong cyc_crc = pre_crc * 2 '// {PRE_CRC[CRC_BITS-1:0],1'b0} '// ----- GenCrcCalc ----- res_crc = 0 p = 0 While (p < CRC_BITS) cyc_crc_p = NML_BIT(cyc_crc, p) '// cyc_crc[p] cyc_crc_m = NML_BIT(cyc_crc, CRC_BITS) '// cyc_crc[CRC_BITS] pre_xor_p = cyc_crc_p Xor cyc_crc_m Xor bit_dat '// cyc_crc[p] ^ cyc_crc[CRC_BITS] ^ BIT_DAT POL_CRC_p = NML_BIT(POL_CRC, p) '// POL_CRC[p] If (POL_CRC_p = 1) Then '// ( POL_CRC[p] ) ? pre_xor[p] : RES_CRC_p = pre_xor_p '// Else '// RES_CRC_p = cyc_crc_p '// cyc_crc[p] ; End If '// res_crc = res_crc + NML_VAL(RES_CRC_p, p) '// RES_CRC[p] p = p + 1 Wend NML_CRC_CALC = res_crc End Function '// Bit反転 '// ================================================================= Function NML_CRC_INVERT( _ ByVal WIDTH As LongLong, _ ByVal DI As LongLong _ ) As LongLong Dim p As LongLong Dim Value As LongLong Value = 0 p = 0 While (p < WIDTH) Value = Value + NML_VAL(NML_BIT(DI, (WIDTH - 1) - p), p) p = p + 1 Wend NML_CRC_INVERT = Value End Function '// 部品 '// ================================================================= Function NML_GET_ARRAY( _ ByVal Area As Range, _ ByRef Addr As LongLong _ ) As LongLong NML_GET_ARRAY = WorksheetFunction.Index(Area, Addr + 1) End Function Function NML_BIT( _ ByVal Data As LongLong, _ ByVal Bits As LongLong _ ) As LongLong Dim aaa As LongLong aaa = WorksheetFunction.RoundDown(Data / (2 ^ Bits), 0) NML_BIT = aaa Mod 2 End Function Function NML_VAL( _ ByVal Data As LongLong, _ ByVal Bits As LongLong _ ) As LongLong NML_VAL = Data * (2 ^ Bits) End Function
一般的なプログラム
Function CRC( _ ByVal LngCRC_Bits As LongLong, _ ByVal LngCRC_Init As LongLong, _ ByVal LngCRC_Poly As LongLong, _ ByVal LngCRC_Xoro As LongLong, _ ByVal LngCRC_Refi As LongLong, _ ByVal LngCRC_Refo As LongLong, _ ByVal LngDAT_Bits As LongLong, _ ByVal LngDAT_Size As LongLong, _ ByVal DatArea As Range _ ) As LongLong Dim Base As LongLong Dim Poly As LongLong Dim Mask As LongLong Dim Jdge As LongLong Dim Refi As LongLong Dim Refo As LongLong Dim Data As LongLong Dim CRCx As LongLong Dim ad As LongLong Dim j As LongLong Base = LngCRC_Bits - LngDAT_Bits Poly = LngCRC_Poly Mask = (2 ^ LngCRC_Bits) - 1 Jdge = (2 ^ (LngCRC_Bits - 1)) Refi = LngCRC_Refi Refo = LngCRC_Refo CRCx = LngCRC_Init '// CRC初期値 ad = 0 While (ad < LngDAT_Size) Data = GetArray(DatArea, ad) '// データ取得 If (Refi = 1) Then Data = Invert(LngDAT_Bits, Data) '// 反転 End If Data = WorksheetFunction.Bitlshift(Data, Base) '// Shift CRCx = WorksheetFunction.Bitxor(CRCx, Data) j = 0 While (j < LngDAT_Bits) If (WorksheetFunction.Bitand(CRCx, Jdge) = Jdge) Then CRCx = WorksheetFunction.Bitlshift(CRCx, 1) CRCx = WorksheetFunction.Bitxor(CRCx, Poly) Else CRCx = WorksheetFunction.Bitlshift(CRCx, 1) End If CRCx = WorksheetFunction.Bitand(CRCx, Mask) j = j + 1 Wend ad = ad + 1 Wend If (Refo = 1) Then CRCx = Invert(LngCRC_Bits, CRCx) End If CRC = WorksheetFunction.Bitxor(CRCx, LngCRC_Xoro) End Function Function Invert( _ ByVal WIDTH As LongLong, _ ByVal DI As LongLong _ ) As LongLong Dim p As LongLong Dim Value As LongLong Value = 0 p = 0 While (p < WIDTH) Value = Value + NML_VAL(NML_BIT(DI, (WIDTH - 1) - p), p) p = p + 1 Wend Invert = Value End Function Function GetArray( _ ByVal Area As Range, _ ByRef Addr As LongLong _ ) As LongLong GetArray = WorksheetFunction.Index(Area, Addr + 1) End Function
実装
Veilogで作成したCRCブロックを前回のFRAM I/Fが実装されているFPGAに実装しました。
前回の構成は以下を参照ください。
nao-milk.hatenablog.com
トップソースは、以下の通りです。
module TOP( input wire RESET_N , // @@ リセット input wire CLOCK , // @@ クロック output wire FRAM_WP_N , // @@ FRAM ライトプロテクト[0:プロテクト] output wire FRAM_CS_N , // @@ FRAM SPI チップセレクト output wire FRAM_SCLK , // @@ FRAM SPI シリアルクロック output wire FRAM_MOSI , // @@ FRAM SPI シリアル出力 input wire FRAM_MISO , // @@ FRAM SPI シリアル入力 input wire SW_SLAVE , // @@ Master/Slave切替 [0:Master] input wire SW_GO , // @@ 処理イネーブル output wire LED_LOCK // @@ LED Lock ); // @@ // @@ クロック/リセット生成 // @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ wire cpu_rst_n; wire cpu_clk ; wire sys_rst_n; wire sys_clk ; NML_CLKRST UnCLKRST( .PAD_RST_N (RESET_N ), // input wire PAD_RST_N , // @@ 入力リセット .PAD_CLK (CLOCK ), // input wire PAD_CLK , // @@ 入力クロック .CPU_RST_N (cpu_rst_n ), // output reg CPU_RST_N , // @@ CPU用 リセット .CPU_CLK (cpu_clk ), // output wire CPU_CLK , // @@ CPU用 クロック[50MHz] .SYS_RST_N (sys_rst_n ), // output reg SYS_RST_N , // @@ System用 リセット .SYS_CLK (sys_clk ), // output wire SYS_CLK , // @@ System用 クロック[200MHz] .PLL_LOCK (LED_LOCK ) // output wire PLL_LOCK // @@ PLL LOCK ); // @@ // @@ CPU // @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ wire fram_ami_wen; wire [31:0] fram_ami_add; wire [ 3:0] fram_ami_ben; wire [ 7:0] fram_ami_wdt; wire fram_asi_wen; wire fram_asi_ren; wire [ 5:0] fram_asi_add; wire [31:0] fram_asi_wdt; wire fram_asi_wit; wire fram_asi_rvl; wire [31:0] fram_asi_rdt; wire fram_cpu_irq; wire crc_asi_wen; wire crc_asi_ren; wire [ 5:0] crc_asi_add; wire [31:0] crc_asi_wdt; wire crc_asi_wit; wire crc_asi_rvl; wire [31:0] crc_asi_rdt; NML_CPU UnCPU( .clk_clk (cpu_clk ), // input wire clk_clk, // clk.clk .irq_irq (fram_cpu_irq ), // input wire [0:0] irq_irq, // irq.irq .fram_write (fram_asi_wen ), // output wire fram_write, // .write .fram_read (fram_asi_ren ), // output wire fram_read, // .read .fram_address (fram_asi_add ), // output wire [5:0] fram_address, // .address .fram_writedata (fram_asi_wdt ), // output wire [31:0] fram_writedata, // .writedata .fram_waitrequest (fram_asi_wit ), // input wire fram_waitrequest, // fram.waitrequest .fram_readdatavalid (fram_asi_rvl ), // input wire fram_readdatavalid, // .readdatavalid .fram_readdata (fram_asi_rdt ), // input wire [31:0] fram_readdata, // .readdata .fram_burstcount (/** OPEN **/ ), // output wire [0:0] fram_burstcount, // .burstcount .fram_byteenable (/** OPEN **/ ), // output wire [3:0] fram_byteenable, // .byteenable .fram_debugaccess (/** OPEN **/ ), // output wire fram_debugaccess, // .debugaccess .fram_wp_export (FRAM_WP_N ), // output wire fram_wp_export, // fram_wp.export .param_clken (1'b1 ), // input wire param_clken, // .clken .param_chipselect (1'b1 ), // input wire param_chipselect, // .chipselect .param_write ( fram_ami_wen ), // input wire param_write, // .write .param_address ( fram_ami_add[11: 2] ), // input wire [9:0] param_address, // param.address .param_byteenable ( fram_ami_ben ), // input wire [3:0] param_byteenable, // .byteenable .param_writedata ({4{fram_ami_wdt}} ), // input wire [31:0] param_writedata, // .writedata .param_readdata (/** OPEN **/ ), // output wire [31:0] param_readdata, // .readdata .port_a_export ( ), // output wire [7:0] port_a_export, // port_a.export .port_b_export ( ), // output wire [7:0] port_b_export, // port_b.export .crc_write (crc_asi_wen ), // output wire fram_write, // .write .crc_read (crc_asi_ren ), // output wire fram_read, // .read .crc_address (crc_asi_add ), // output wire [5:0] fram_address, // .address .crc_writedata (crc_asi_wdt ), // output wire [31:0] fram_writedata, // .writedata .crc_waitrequest (crc_asi_wit ), // input wire fram_waitrequest, // fram.waitrequest .crc_readdatavalid (crc_asi_rvl ), // input wire fram_readdatavalid, // .readdatavalid .crc_readdata (crc_asi_rdt ), // input wire [31:0] fram_readdata, // .readdata .crc_burstcount (/** OPEN **/ ), // output wire [0:0] fram_burstcount, // .burstcount .crc_byteenable (/** OPEN **/ ), // output wire [3:0] fram_byteenable, // .byteenable .crc_debugaccess (/** OPEN **/ ), // output wire fram_debugaccess, // .debugaccess .reset_reset_n (cpu_rst_n ) // input wire reset_reset_n // reset.reset_n ); // @@ // @@ FRAM I/F // @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ NML_FRAMIF UnFRAMIF( .CPU_RST_N (cpu_rst_n ), // input wire CPU_RST_N , // @@ CPU I/F用 リセット .CPU_CLK (cpu_clk ), // input wire CPU_CLK , // @@ CPU I/F用 クロック .SYS_RST_N (sys_rst_n ), // input wire SYS_RST_N , // @@ システム動作用 リセット .SYS_CLK (sys_clk ), // input wire SYS_CLK , // @@ システム動作用 クロック .MST_SLV (SW_SLAVE ), // input wire MST_SLV , // @@ Avalon-MM Master/Slave切替 [0:Master] .PRM_ENB (SW_GO ), // input wire PRM_ENB , // @@ 処理イネーブル [1→0:OFF 0→1:ON] .PRM_DIV (4'd0 ), // input wire [ 3:0] PRM_DIV , // @@ シリアルクロック周波数設定 .PRM_POL (1'b0 ), // input wire PRM_POL , // @@ シリアルクロック極性設定 .PRM_TAK (2'd0 ), // input wire [ 1:0] PRM_TAK , // @@ シリアルデータ取り込みタイミング設定 .PRM_ACS (21'd8192 ), // input wire [20:0] PRM_ACS , // @@ アクセスバイト数 (最大値:1,048,579) .PRM_OTC (2'd3 ), // input wire [ 1:0] PRM_OTC , // @@ 出力バイト数 (最大値:3) .PRM_MSK (1'b0 ), // input wire PRM_MSK , // @@ 割り込みマスク .PRM_CLR (1'b0 ), // input wire PRM_CLR , // @@ 割り込みクリア .PRM_STA (32'h0000_0000 ), // input wire [31:0] PRM_STA , // @@ Avalon-MM(Master側) ライトベースアドレス .PRM_CMD (8'h03 ), // input wire [ 7:0] PRM_CMD , // @@ RAMコマンド .PRM_ADD (24'h00_0000 ), // input wire [23:0] PRM_ADD , // @@ FRAMアドレス .SAV_WEN (fram_asi_wen ), // input wire SAV_WEN , // @@ [CPU_CLK↑同期] Avalon-MM(Slave側) ライトイネーブル .SAV_REN (fram_asi_ren ), // input wire SAV_REN , // @@ [CPU_CLK↑同期] Avalon-MM(Slave側) リードイネーブル .SAV_ADD (fram_asi_add ), // input wire [ 5:0] SAV_ADD , // @@ [CPU_CLK↑同期] Avalon-MM(Slave側) リード/ライトイネーブル .SAV_WDT (fram_asi_wdt ), // input wire [31:0] SAV_WDT , // @@ [CPU_CLK↑同期] Avalon-MM(Slave側) ライトデータ .SAV_WIT (fram_asi_wit ), // output wire SAV_WIT , // @@ [CPU_CLK↑同期] Avalon-MM(Slave側) ウェイトリクエスト .SAV_RVL (fram_asi_rvl ), // output wire SAV_RVL , // @@ [CPU_CLK↑同期] Avalon-MM(Slave側) リードデータイネーブル .SAV_RDT (fram_asi_rdt ), // output wire [31:0] SAV_RDT , // @@ [CPU_CLK↑同期] Avalon-MM(Slave側) リードデータ .MAV_WEN (fram_ami_wen ), // output wire MAV_WEN , // @@ [CPU_CLK↑同期] Avalon-MM(Master側) ライトイネーブル .MAV_ADD (fram_ami_add ), // output wire [31:0] MAV_ADD , // @@ [CPU_CLK↑同期] Avalon-MM(Master側) ライトアドレス .MAV_BEN (fram_ami_ben ), // output wire [ 3:0] MAV_BEN , // @@ [CPU_CLK↑同期] Avalon-MM(Master側) バイトイネーブル .MAV_WDT (fram_ami_wdt ), // output wire [ 7:0] MAV_WDT , // @@ [CPU_CLK↑同期] Avalon-MM(Master側) ライトデータ .MAV_WIT (1'b0 ), // input wire MAV_WIT , // @@ [CPU_CLK↑同期] Avalon-MM(Master側) ウェイトリクエスト .CPU_IRQ (fram_cpu_irq ), // output wire CPU_IRQ , // @@ [CPU_CLK↑同期] 割り込み信号 .MON_ENB (/** OPEN **/ ), // output wire MON_ENB , // @@ [CPU_CLK↑同期] 動作モニタ [0:停止中 1:動作中] .FRAM_CS_N (FRAM_CS_N ), // output wire FRAM_CS_N , // @@ [SYS_CLK↑同期] FRAM SPI チップセレクト .FRAM_SCLK (FRAM_SCLK ), // output wire FRAM_SCLK , // @@ [SYS_CLK↑同期] FRAM SPI シリアルクロック .FRAM_MOSI (FRAM_MOSI ), // output wire FRAM_MOSI , // @@ [SYS_CLK↑同期] FRAM SPI シリアル出力 .FRAM_MISO (FRAM_MISO ), // input wire FRAM_MISO , // @@ [SYS_CLK↑同期] FRAM SPI シリアル入力 .FRAM_OUTE (/** OPEN **/ ) // output wire FRAM_OUTE // @@ [SYS_CLK↑同期] FRAM SPI 出力イネーブル[1:出力 0:Hi-Z] ); // @@ // @@ CRC // @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ parameter P_IND_BIT_WIDTH = 8 ; // 入力データのbit幅 parameter P_CRC_BIT_WIDTH = 16 ; // CRCのbit幅 (32まで) parameter P_INI_CRCV = 16'h0000 ; // レジスタ初期値 CRC初期値 parameter P_INI_POLY = 16'h1021 ; // レジスタ初期値 生成多項式 parameter P_INI_XOROUT = 16'h0000 ; // レジスタ初期値 CRC値反転 parameter P_INI_REFI = 1'b0 ; // レジスタ初期値 入力bit反転(0:正転 1:反転) parameter P_INI_REFO = 1'b0 ; // レジスタ初期値 CRC bit反転(0:正転 1:反転) parameter P_PRM_FIX = 1'b0 ; // モード レジスタ固定(1:上記P_INI_*のみ有効[書き込みできない]) NML_CRC #( .P_IND_BIT_WIDTH(P_IND_BIT_WIDTH), // parameter P_IND_BIT_WIDTH = 8 , // 入力データのbit幅 .P_CRC_BIT_WIDTH(P_CRC_BIT_WIDTH), // parameter P_CRC_BIT_WIDTH = 16 , // CRCのbit幅 (32まで) .P_INI_CRCV (P_INI_CRCV ), // parameter P_INI_CRCV = 16'h0000 , // レジスタ初期値 CRC初期値 .P_INI_POLY (P_INI_POLY ), // parameter P_INI_POLY = 16'h1021 , // レジスタ初期値 生成多項式 .P_INI_XOROUT (P_INI_XOROUT ), // parameter P_INI_XOROUT = 16'h0000 , // レジスタ初期値 CRC値反転 .P_INI_REFI (P_INI_REFI ), // parameter P_INI_REFI = 1'b0 , // レジスタ初期値 入力bit反転(0:正転 1:反転) .P_INI_REFO (P_INI_REFO ), // parameter P_INI_REFO = 1'b0 , // レジスタ初期値 CRC bit反転(0:正転 1:反転) .P_PRM_FIX (P_PRM_FIX ) // parameter P_PRM_FIX = 1'b0 // モード レジスタ固定(1:上記P_INI_*のみ有効[書き込みできない]) ) UnNML_CRC ( .CLK (cpu_clk ), // input wire CLK , // システムクロック .RESET_N (cpu_rst_n ), // input wire RESET_N , // システムリセット .AVA_WEN (crc_asi_wen ), // input wire AVA_WEN , // Avalon-MM(Slave側) ライトイネーブル .AVA_REN (crc_asi_ren ), // input wire AVA_REN , // Avalon-MM(Slave側) リードイネーブル .AVA_ADD (crc_asi_add ), // input wire [ 5:0] AVA_ADD , // Avalon-MM(Slave側) リード/ライトアドレス .AVA_WDT (crc_asi_wdt ), // input wire [31:0] AVA_WDT , // Avalon-MM(Slave側) ライトデータ .AVA_WIT (crc_asi_wit ), // output wire AVA_WIT , // Avalon-MM(Slave側) ウェイトリクエスト .AVA_RVL (crc_asi_rvl ), // output reg AVA_RVL , // Avalon-MM(Slave側) リードデータイネーブル .AVA_RDT (crc_asi_rdt ), // output reg [31:0] AVA_RDT , // Avalon-MM(Slave側) リードデータ .MON_CRC (/** OPEN **/ ) // output reg [31:0] MON_CRC // CRC結果 ); endmodule
Platform Designerは以下のような実装となります。
※CRCを追加しました。
Quartusの実行
FPGAに実装し、結果は以下の通りです。
STA結果は、50MHzクロックでMetしました。
尚、前回のFRAMI/Fのみ結果は以下を参照ください。
nao-milk.hatenablog.com
Summary
Resorce
ソフトウェア
NIOSでCRCブロックにアクセスができ、CRC結果が期待値と一致するか、確認するソースは以下の通りです。
ソース
関数main
mainのソースには、前回のFRAM I/Fのヘッダーファイルも含まれています。
nao-milk.hatenablog.com
// @@ // @@ Nios program // @@ // @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #define _MAIN_C_ #include <stdio.h> #include <stdlib.h> #include <string.h> #include "system.h" #include "io.h" #include "sys/alt_irq.h" #include "altera_avalon_pio_regs.h" #include "nml_framif.h" #include "nml_crc.h" //#define DBG_PRINT(...) printf(__VA_ARGS__) #define DBG_PRINT(...) // ** // ** Main program // *************************************************************************************** int main(void){ T_CRC_INFO crc_info; int test_crc_status = -1; // == // == Test // ========================================================================= // ++ // ++ CRC TEST // +++++++++++++++++++++++++++++++++++++++++++ IOWR_ALTERA_AVALON_PIO_DATA(PORT_A_BASE,0x01); // Step No crc_info = NML_CRC_INFO(); if( crc_info.fix_mode == NML_CRC_FIXMODE ){ IOWR_ALTERA_AVALON_PIO_DATA(PORT_A_BASE,0x7F); return 1; } IOWR_ALTERA_AVALON_PIO_DATA(PORT_A_BASE,0x02); // Step No test_crc_status = NML_CRC_TEST(); // ++++++ Final Message +++++ IOWR_ALTERA_AVALON_PIO_DATA(PORT_A_BASE,0xFF); if( test_crc_status == NML_CRC_TEST_OK ) printf(" >>>>> Test End : OK\n"); else if( test_crc_status == NML_CRC_TEST_SKIP ) printf(" >>>>> Test End : Skip\n"); else printf(" >>>>> Test End : %d Error\n",test_crc_status); return 0; }
nml_crc.h
// @@ // @@ Functions for CRC // @@ // @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #ifndef _NML_CRC_H_ #define _NML_CRC_H_ #include "system.h" #include "io.h" // -- // -- Define // ----------------------------------------------------------------------------- #define NML_CRC_FIXMODE (1) #define NML_CRC_TEST_OK (0) #define NML_CRC_TEST_SKIP (-1) #define NML_CRC_TEST_SRCON // @@ CRC test source switch // -- // -- Register for CRC // ----------------------------------------------------------------------------- #define NML_CRC_SETREG_INI(ini) (IOWR(CRC_BASE,0x00, (ini ))) #define NML_CRC_SETREG_DAT(dat) (IOWR(CRC_BASE,0x01, (dat ))) #define NML_CRC_SETREG_INIT(init) (IOWR(CRC_BASE,0x04, (init))) #define NML_CRC_SETREG_POLY(poly) (IOWR(CRC_BASE,0x05, (poly))) #define NML_CRC_SETREG_XORO(xoro) (IOWR(CRC_BASE,0x06, (xoro))) #define NML_CRC_SETREG_REFL(refl) (IOWR(CRC_BASE,0x07, (refl))) #define NML_CRC_GETREG_INI(void) (IORD(CRC_BASE,0x00)) #define NML_CRC_GETREG_DAT(void) (IORD(CRC_BASE,0x01)) #define NML_CRC_GETREG_CRC(void) (IORD(CRC_BASE,0x02)) #define NML_CRC_GETREG_INFO(void) (IORD(CRC_BASE,0x03)) #define NML_CRC_GETREG_INIT(void) (IORD(CRC_BASE,0x04)) #define NML_CRC_GETREG_POLY(void) (IORD(CRC_BASE,0x05)) #define NML_CRC_GETREG_XORO(void) (IORD(CRC_BASE,0x06)) #define NML_CRC_GETREG_REFL(void) (IORD(CRC_BASE,0x07)) // -- // -- typdef for CRC // ----------------------------------------------------------------------------- typedef struct { unsigned char dat_bit_width; // 入力データのbit幅 unsigned char crc_bit_width; // CRC結果のbit幅 unsigned char fix_mode; // パラメータ固定モード (1:固定モード) } T_CRC_INFO; // -- // -- Function for CRC // ----------------------------------------------------------------------------- #ifdef _NML_CRC_C_ T_CRC_INFO NML_CRC_INFO(void); // ** CRC information function void NML_CRC_SETUP( // ** Initial setting unsigned int Poly , // Polynomial unsigned int Init , // Initial Value unsigned int Xoro , // Final Xor Value unsigned char Iref , // Input reflected unsigned char Oref // Result reflected ); int NML_CRC_TEST(void); // ** CRC test function #else // --------------------------------------------------------------------- extern T_CRC_INFO NML_CRC_INFO(void); // ** CRC information function extern void NML_CRC_SETUP( // ** Initial setting unsigned int Poly , // Polynomial unsigned int Init , // Initial Value unsigned int Xoro , // Final Xor Value unsigned char Iref , // Input reflected unsigned char Oref // Result reflected ); extern int NML_CRC_TEST(void); // ** CRC test function #endif #endif /* _NML_CRC_H_ */
nml_crc.c
関数NML_CRC_TESTがテスト用の関数になります。
ここでは、単体検証で行った設定値と期待値を変数に保存しておき、CRCブロックにデータを与えてCRC結果と比較します。
不一致数は変数errとなり、最後にエラー数を返します。
// @@ // @@ Functions for CRC // @@ // @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ #ifndef _NML_CRC_C_ #define _NML_CRC_C_ #include <stdio.h> #include <stdlib.h> #include "system.h" #include "io.h" #include "altera_avalon_pio_regs.h" #include "nml_crc.h" // -- // -- CRC information function // ----------------------------------------------------------------------------- T_CRC_INFO NML_CRC_INFO(void){ T_CRC_INFO info; unsigned int temp; // ++ Get Information // +++++++++++++++++++++++++++++++++++++++++++ temp = NML_CRC_GETREG_INFO(); info.fix_mode = (temp>>16)&0x01; info.crc_bit_width = (temp>> 8)&0xFF; info.dat_bit_width = (temp>> 0)&0xFF; return info; } // -- // -- Initial setting // ----------------------------------------------------------------------------- void NML_CRC_SETUP( unsigned int Poly , // Polynomial unsigned int Init , // Initial Value unsigned int Xoro , // Final Xor Value unsigned char Iref , // Input reflected unsigned char Oref // Result reflected ){ T_CRC_INFO info; unsigned int temp; info = NML_CRC_INFO(); if( info.fix_mode != NML_CRC_FIXMODE ){ NML_CRC_SETREG_INIT(Init); NML_CRC_SETREG_POLY(Poly); NML_CRC_SETREG_XORO(Xoro); temp = (unsigned int)((Oref&0x01)<<1) | (unsigned int)((Iref&0x01)<<0); NML_CRC_SETREG_REFL(temp); } NML_CRC_SETREG_DAT(0x00000000); NML_CRC_SETREG_INI(0x00000000); } // -- // -- CRC test function // ----------------------------------------------------------------------------- int NML_CRC_TEST(void){ #ifdef NML_CRC_TEST_SRCON #define CRC08_TEST_NUM (13) #define CRC16_TEST_NUM (24) #define CRC32_TEST_NUM ( 9) #define CRC_TEST_NUM (CRC08_TEST_NUM+CRC16_TEST_NUM+CRC32_TEST_NUM) typedef struct { unsigned int INIT; unsigned int POLY; unsigned int XORO; unsigned int REFL; unsigned int RSLT; } T_EXP; T_EXP CRC_SET_EXP[CRC_TEST_NUM] = { // ** CRC setting and expected value /***** CRC 8bit *****/ {0x00000000,0x00000007,0x00000000,0x00000000,0x000000F4} ,{0x000000FF,0x0000001D,0x000000FF,0x00000000,0x0000004B} ,{0x00000000,0x0000001D,0x00000000,0x00000000,0x00000037} ,{0x000000FF,0x0000002F,0x000000FF,0x00000000,0x000000DF} ,{0x000000FF,0x0000009B,0x00000000,0x00000000,0x000000DA} ,{0x00000000,0x00000039,0x00000000,0x00000003,0x00000015} ,{0x00000000,0x000000D5,0x00000000,0x00000000,0x000000BC} ,{0x000000FF,0x0000001D,0x00000000,0x00000003,0x00000097} ,{0x000000FD,0x0000001D,0x00000000,0x00000000,0x0000007E} ,{0x00000000,0x00000007,0x00000055,0x00000000,0x000000A1} ,{0x00000000,0x00000031,0x00000000,0x00000003,0x000000A1} ,{0x000000FF,0x00000007,0x00000000,0x00000003,0x000000D0} ,{0x00000000,0x0000009B,0x00000000,0x00000003,0x00000025} /***** CRC 16bit *****/ ,{0x00000000,0x00001021,0x00000000,0x00000000,0x000031C3} ,{0x00000000,0x00008005,0x00000000,0x00000003,0x0000BB3D} ,{0x00001D0F,0x00001021,0x00000000,0x00000000,0x0000E5CC} ,{0x00000000,0x00008005,0x00000000,0x00000000,0x0000FEE8} ,{0x0000FFFF,0x00001021,0x00000000,0x00000000,0x000029B1} ,{0x0000FFFF,0x0000C867,0x00000000,0x00000000,0x00004C06} ,{0x0000800D,0x00008005,0x00000000,0x00000000,0x00009ECF} ,{0x00000000,0x00000589,0x00000001,0x00000000,0x0000007E} ,{0x00000000,0x00000589,0x00000000,0x00000000,0x0000007F} ,{0x00000000,0x00003D65,0x0000FFFF,0x00000003,0x0000EA82} ,{0x00000000,0x00003D65,0x0000FFFF,0x00000000,0x0000C2B7} ,{0x0000FFFF,0x00001021,0x0000FFFF,0x00000000,0x0000D64E} ,{0x00000000,0x00008005,0x0000FFFF,0x00000003,0x000044C2} ,{0x0000FFFF,0x00001021,0x00000000,0x00000003,0x00006F91} ,{0x0000B2AA,0x00001021,0x00000000,0x00000003,0x000063D0} ,{0x00000000,0x00008BB7,0x00000000,0x00000000,0x0000D0DB} ,{0x00000000,0x0000A097,0x00000000,0x00000000,0x00000FB3} ,{0x000089EC,0x00001021,0x00000000,0x00000003,0x000026B1} ,{0x0000FFFF,0x00008005,0x0000FFFF,0x00000003,0x0000B4C8} ,{0x0000C6C6,0x00001021,0x00000000,0x00000003,0x0000BF05} ,{0x00000000,0x00001021,0x00000000,0x00000003,0x00002189} ,{0x0000FFFF,0x00008005,0x00000000,0x00000003,0x00004B37} ,{0x0000FFFF,0x00001021,0x0000FFFF,0x00000003,0x0000906E} ,{0x00000000,0x00001021,0x00000000,0x00000000,0x000031C3} /***** CRC 32bit *****/ ,{0xFFFFFFFF,0x04C11DB7,0xFFFFFFFF,0x00000003,0xCBF43926} ,{0xFFFFFFFF,0x04C11DB7,0xFFFFFFFF,0x00000000,0xFC891918} ,{0xFFFFFFFF,0x1EDC6F41,0xFFFFFFFF,0x00000003,0xE3069283} ,{0xFFFFFFFF,0xA833982B,0xFFFFFFFF,0x00000003,0x87315576} ,{0xFFFFFFFF,0x04C11DB7,0x00000000,0x00000000,0x0376E6E7} ,{0x00000000,0x04C11DB7,0xFFFFFFFF,0x00000000,0x765E7680} ,{0x00000000,0x814141AB,0x00000000,0x00000000,0x3010BF7F} ,{0xFFFFFFFF,0x04C11DB7,0x00000000,0x00000003,0x340BC6D9} ,{0x00000000,0x000000AF,0x00000000,0x00000000,0xBD0BE338} }; T_CRC_INFO info; unsigned int start; unsigned int limit; unsigned int num; unsigned int data; unsigned char err; info = NML_CRC_INFO(); if( (info.fix_mode != NML_CRC_FIXMODE) & (info.dat_bit_width == 8) & ( (info.crc_bit_width == 8) |(info.crc_bit_width == 16) |(info.crc_bit_width == 32) ) ); else goto SKIP; if( info.crc_bit_width == 8 ){ start = 0; limit = CRC08_TEST_NUM; } if( info.crc_bit_width == 16 ){ start = CRC08_TEST_NUM; limit = CRC16_TEST_NUM; } if( info.crc_bit_width == 32 ){ start = CRC08_TEST_NUM + CRC16_TEST_NUM; limit = CRC32_TEST_NUM; } err = 0; for(num=0;num<limit;num++){ IOWR_ALTERA_AVALON_PIO_DATA(PORT_B_BASE,start+num); // Step No NML_CRC_SETREG_INIT(CRC_SET_EXP[start+num].INIT); NML_CRC_SETREG_POLY(CRC_SET_EXP[start+num].POLY); NML_CRC_SETREG_XORO(CRC_SET_EXP[start+num].XORO); NML_CRC_SETREG_REFL(CRC_SET_EXP[start+num].REFL); NML_CRC_SETREG_INI(0x00000000); NML_CRC_SETREG_DAT(0x00000031); NML_CRC_SETREG_DAT(0x00000032); NML_CRC_SETREG_DAT(0x00000033); NML_CRC_SETREG_DAT(0x00000034); NML_CRC_SETREG_DAT(0x00000035); NML_CRC_SETREG_DAT(0x00000036); NML_CRC_SETREG_DAT(0x00000037); NML_CRC_SETREG_DAT(0x00000038); NML_CRC_SETREG_DAT(0x00000039); data = NML_CRC_GETREG_CRC(); if( data != CRC_SET_EXP[start+num].RSLT ) err++; } return err; SKIP: #endif // NML_CRC_TEST_SRCOFF return NML_CRC_TEST_SKIP; } #endif /* _NML_CRC_C_ */
協調シミュレーション
FPGAをトップとしたシミュレーションを行い、NIOSとCRCブロックの接続を確認しました。
テストベンチは FRAM I/Fの時と一緒になります。
※以下を参照してください。
nao-milk.hatenablog.com
シミュレーション波形
実行時のシミュレーション波形は以下の通りです。
シミュレーション結果
結果はError無しとなり、接続確認はOKとなります。
ModelSimでmain関数の最終行表示内容を以下に添付します。
【main関数の最終行】
// ++++++ Final Message +++++ IOWR_ALTERA_AVALON_PIO_DATA(PORT_A_BASE,0xFF); if( test_crc_status == NML_CRC_TEST_OK ) printf(" >>>>> Test End : OK\n"); else if( test_crc_status == NML_CRC_TEST_SKIP ) printf(" >>>>> Test End : Skip\n"); else printf(" >>>>> Test End : %d Error\n",test_crc_status); return 0;
【ModelSimの表示内容】
変数を別メモリに配置
特定の変数をプログラムメモリ(CPUが使用するメモリ)とは別のメモリに配置する場合について、説明します。
尚、Intel社製 NiosIIの場合になります。
以下の記事では設定手順などの説明がないと思いますので、記載しておきます。
(ネット上にもあまり無いかと思います)
大まかな設定手順は、
- BSP EditorのLinkerでセクションを作る。
- 変数とセクションを関連付ける。
です。
BSP EditorのLinkerでセクションを作る
1、Project Explorerの????_bspをクリックし、[Nios II] → [BSP Editor]を選択します。
2、Linker Scriptタブを選択します。
ここでセクションを作成します。
3、「Linker Memory Regions」の"Add"ボタンをクリックし、メモリとサイズとOffsetアドレスを指定し、"Linker Region Name"を作成します。
以下のような感じで、配置させたい変数分作ります。
必要な領域を指定するとPARAMETERが設定されており、アドレスが重複しているために赤文字でメッセージが表示されます。
"Linker Region Name"の「PARAMETER」選択し、"Remove"ボタンで削除します。
以下が削除後の表示内容になります。
4、「Linker Section Mappings」の"Add"ボタンをクリックし、"Linker Region Name"と"Linker Section Name"を関連付けします。
必要な領域分設定します。
"Generate"ボタンをクリックし、終了します。
変数とセクションを関連付ける。
ソースコード内(.cファイル)で「__attribute__」を使って、sectionと変数を関連付けます。
確認方法
プロジェクトをコンパイルすると「???.map」が出来上がり、変数とセクションが関連付けられたことを確認できます。
最後に
上記.mapで配置を確認できますが、Niosを含めたシミュレーションでも確認しています。
以下の記事をご参照ください。
以下はNiosソースコードを記載しています。
以下はシミュレーション波形を載せています。
FRAMにアクセス (協調シミュレーション編)
NIOSを含めたシミュレーションの実行になります。
シミュレーション環境の構築方法とNiosのシミュレーション用コードの生成方法、及びシミュレーション波形を添付します。
NIOSプログラムやテストベンチは、前回の記事をご参照ください。
シミュレーション環境構築
シミュレーション環境を構築しますが、必要なファイルなどのリストを作成するのが面倒なので、Platform Designer で環境を作成し、生成されたスクリプトファイルを改造して使用します。
実行スクリプトファイル生成
Quartusを起動し、「Platform Designer」を立ち上げて作成したQsysファイルを読み込みます。
その後、[Generate]→[Generate Tesetbench System]を選択します。
「Generation」Windowが表示され、"Generate"ボタンをクリックします。
以下の選択内容で十分です。(生成されたテストベンチは使用しないので。。。)
"Generate"ボタンをクリックすると、Quartus実行フォルダに以下のフォルダが出来上がります。
フォルダ
<Platform Designerで作成したモジュール名>/testbench/mentor
このフォルダに実行スクリプトファイル(ファイル名:msim_setup.tcl)が出来上がります。
このスクリプトファイルは、「Platform Designerで作成したモジュール」をトップとしたシミュレーション環境になるため、FPGA TOPからシミュレーションする場合は、改造が必要になります。
尚、Platform Designerで作成した必要なIPのリストはあるので、あとは、ユーザの作成したソースを追加するだけとなります。
スクリプトファイルの修正
msim_setup.tclの修正内容を説明します。
1、NIOSプログラムファイルのコピー元を変更
Nios II - Eclipseでコンパイルしたデータのコピー元を変更します。
2、テストベンチのコメントアウト
Platform Designerが生成したテストベンチをコメントアウトします。
3、ユーザー回路の追加
ユーザー回路を追加し、テストベンチや必要なファイルを追加します。
私の場合、alias comとは別に、新しいaliasを作成して追加します。
尚、TB_TOP.vとFRAM_SPI.v(FRAMモデル)は、シミュレーション実行フォルダにあります。
4、elab修正
alias elabでは、vsimを実行しています。
このままの実行では、Platform Designerが生成したテストベンチとなっているため、修正します。
TB_TOP.vがテストベンチとなるため、$TOP_LEVEL_NAMEを「TB_TOP」に修正します。
以上で、msim_setup.tclの修正は終了となります。
NIOSプログラム
NIOSのプログラムデータは、CPU_MEMに格納されるようになっています。
このファイルを作成する方法を説明します。
オプション設定
「Nios II - Eclipse」を起動し、プロジェクトを読み込みます。
シミュレーション用のコードにするため、オプションを設定します。
「BSP Editor」を起動し、"enable_sim_option"にチェックを入れます。
※入れない場合は、シミュレーションでNiosが動き出すまでに時間がかかります。
また、「Debug Level」と「Optimization level」を変更するともっと速くなります。
以下は、私の設定です。
コード生成
上記設定を行った後、再度コンパイルを行います。
その後、[Make Targets]→[Build]を選択し、「mem_init_generate」を選択して、"Build"ボタンをクリックします。
NiosのCソースを変更し、シミュレーションする場合は必ず実行する必要があります。
また、ここで生成したファイルは「msim_setup.tcl」のfile_copyでシミュレーション環境にコピーされます。
以上までが、シミュレーションを実行するまでの準備となります。
シミュレーションの実行
ModelSimを起動し、シミュレーションを実行します。
実行方法は、以下の通りです。
1、[File] → [Change Directory]で、
<Platform Designerで作成したモジュール名>/testbench/mentor
に移ります。
2、msim_setup.tclを実行します。
2通りあります。
[Tools] → [Tcl] → [Execute Macro]から実行する。
"Transcript"から実行する。
3、コンパイルと実行
msim_setup.tclを実行後、"Transcript"でコンパイルし、エラボレートします。
尚、msim_setup.tclを実行すると、file_copyが実行されています。
コマンドの順番は以下の通りです。
> com
> com_user
> com_tb
> elab
となります。
あとは、"run"してシミュレーションを実行するだけです。
シミュレーション結果
シミュレーション波形を添付します。
全体波形
Niosソース内で、port_aにステップ番号を出力しているので、それを目安にどの部分を実行しているか分かります。
波形の「port_a_export」がそれにあたります。
リセット解除後、約400usでNIOSの動作が開始します。(port_aが0x00 → 0x01に変化)
また、ソースの終了は、port_a=0xFFになり、FRAM_WP_N=1になると終了となります。
(0xFFの期間が長いのは、printfを実行しているためです。)
マスタモード Load
リセット解除後、マスタモードによるロード動作になります。
入力信号MST_SLVとPRM_???はテストベンチからforceで値を入力し、動作させています。(ブート制御回路を作成するのが面倒だったので、外部から動かしました。)
ここでは、FRAMのDevice ID、Serial No、特殊セクタ、通常メモリをメモリPARAMETERに保存しています。
ステータスリード
FRAMのステータスレジスタをリードし、メモリPARAMETERの指定した番地へ保存し、リードしていることが確認できます。(変数FramSTは、メモリPARAMETERの0x920番地に関連付け)
尚、データバス幅は32bitとなるため、アドレスは「÷4」した値になります。
Device ID及びSerial No確認
メモリPARAMETERの指定した番地をリードしています。
データ書き換え
メモリPARAMETERの指定した番地をリードし、リードしたデータを加工し書き戻しています。
尚、FramSS,FramDTも書き換えています。
メモリPARAMETERからFRAM I/Fへ転送
メモリPARAMETERの指定した番地をリードし、FRAM I/Fへライトしています。
FRAM I/FからメモリPARAMETERへ転送
FRAM I/Fからデータをリードし、メモリPARAMETERへ保存しています。
メモリPARAMETERの保存は、FramTMとなります。
割り込み動作
FRAMへのアクセスが完了し、割り込み(irq[0])が発生。
その後、割り込みクリアして割り込みが解除されます。
比較結果
Niosソース及びテストベンチでの比較結果は、動作が終了するとerror数を表示します。
以下が結果表示内容です。
比較結果のerror数が0を表示したため、データエラーは無しになります。
最後に
実機があれば、SignalTapでFPGAの内部波形を確認してデバッグが可能ですが、上記のように接続関係や基本動作を確認する上では、シミュレーションが速いと思います。
SignalTapでは、保存する信号と保存期間はRAMの空き容量に依存し、見たい所が見れない場合もあります。
また、クロックが何系統もあると、保存クロックに悩んでしまいます。
今回、「__attribute__」を使って別メモリに変数を関連付けました。
それが思い通りになっているかも、波形で確認ができます。
「FRAMにアクセス」をテーマに、半導体(FPGAですが)を実機で動かす手前までの工程をざっくりですがブログに記載しました。
ここまで2回のシミュレーションを行い、動作確認をしています。
FRAM I/Fブロックのシミュレーションでは、ブロック自体にバグが無いかの確認のため。
FPGA TOPからのシミュレーションでは、各ブロックの接続関係と矛盾点が無いかの確認のため。
このあとは、基板上のデバイス間接続を確認するため、Niosコードをそれ用に変更していきます。
もし上記シミュレーション無しで、基板で確認すると、どこが悪いのかの切り分けができず、余計に時間がかかります。
Niosを含めたシミュレーション環境構築は非常に簡単です。
もし実機で動かない場合は、シミュレーションで確認してみるのも良いかもしれません。
FRAMにアクセス (Niosコーディング編)
Quartusを実行し、リソースとタイミングもOKだったので、次はNiosのプログラムコーディングとシミュレーションについてになります。
NiosはC言語で記述し、シミュレーションはFPGA トップからの行います。
ここでは、シミュレーションを実行する前のNiosのプログラム作成とテストベンチ記述を記載しています。
トップ構成は、以下をご参照ください。
nao-milk.hatenablog.com
※Quartusを実行した時のリソース使用状況とタイミングに関しては、以下をご参照ください。
nao-milk.hatenablog.com
シミュレーション内容
目的
接続が正しく行われるかを確認するため、Niosを含めたシミュレーションを行います。
経路
接続が正しく行われているか各経路を確認します。
経路は以下の通りになります。
①マスタモード時、FRAMの内容をRAMに格納できること。
②RAMに格納されたデータをNIOSでリードできること。
③NIOSからRAMにデータを格納できること。
④RAMからFRAMにデータを格納できること。
⑤FRAMからリードしたデータをRAMに格納できること。
⑥NIOSからFRAMにデータを格納できること。
また、FRAMからNIOSでリードできること。
⑦割り込みが発生し、NIOSに届くこと。
⑧割り込み処理が実行され、割り込みをクリアできること。
メモリマップ
CPU(Nios)から見えるOnChipMemoryはCPU_MEMとPARAMETERになります。
CPU_MEMはNIOSが使用するプログラムや変数などとなり、PARAMETERはFRAMのデータを格納するために使用します。
※FRAMはパラメータ(USERブロックのレジスタ設定値など。。。)を保存するための用途と想定しており、起動時にFRAMからLoadし、終了時や設定変更時にSaveするイメージとなります。
メモリPARAMETER
「Device ID」「Serial Number」「Special Sector」「Data Memory」「その他(Temporary)」の領域に区切り、マスタモードでLoadする時に分割して保存して使用します。
領域が固定されるようLinkerで開始アドレスと領域を指定し、C言語で変数とリンク付けしています。
Linker設定
Linkerの設定内容は、以下通りです。
メモリマップ
メモリPARAMETERのアドレスマップは以下の通りです。
リンク方法
変数の関連付けは、main.cで設定します。
unsigned char FramID[ 16] __attribute__ ((section (".fram_id"))); // Device ID unsigned char FramSN[ 16] __attribute__ ((section (".fram_sn"))); // Serial Number unsigned char FramSS[ 256] __attribute__ ((section (".fram_ss"))); // Special Sector unsigned char FramDT[2048] __attribute__ ((section (".fram_dt"))); // Data Memory unsigned char FramTM[1024] __attribute__ ((section (".fram_tm"))); // Temporary unsigned char FramST __attribute__ ((section (".fram_tm"))); // Status
「その他(Temporary)」は自由な変数として使用できるようにしています。
従って、FramTM[1024]とFramSTはコンパイラ任せとなります。
リンク確認
コンパイル後、変数に指定したセクションが関連付けられているか、マップファイルで確認します。
マップファイルの抜粋を以下に示します。
Memory Configuration Name Origin Length Attributes reset 0x0000000000000000 0x0000000000000020 CPU_MEM 0x0000000000000020 0x0000000000003fe0 prm_ram_id 0x0000000000200000 0x0000000000000010 prm_ram_sn 0x0000000000200010 0x0000000000000010 prm_ram_ss 0x0000000000200020 0x0000000000000100 prm_ram_dt 0x0000000000200120 0x0000000000000800 prm_ram_tm 0x0000000000200920 0x00000000000006e0 *default* 0x0000000000000000 0xffffffffffffffff ===== 略 ===== Linker script and memory map ............. .fram_id 0x0000000000200000 0x10 [!provide] PROVIDE (_alt_partition_fram_id_start, ABSOLUTE (.)) *(.fram_id .fram_id.*) .fram_id 0x0000000000200000 0x10 obj/default/main.o 0x0000000000200000 FramID 0x0000000000200010 . = ALIGN (0x4) [!provide] PROVIDE (_alt_partition_fram_id_end, ABSOLUTE (.)) .fram_sn 0x0000000000200010 0x10 [!provide] PROVIDE (_alt_partition_fram_sn_start, ABSOLUTE (.)) *(.fram_sn .fram_sn.*) .fram_sn 0x0000000000200010 0x10 obj/default/main.o 0x0000000000200010 FramSN 0x0000000000200020 . = ALIGN (0x4) [!provide] PROVIDE (_alt_partition_fram_sn_end, ABSOLUTE (.)) .fram_ss 0x0000000000200020 0x100 [!provide] PROVIDE (_alt_partition_fram_ss_start, ABSOLUTE (.)) *(.fram_ss .fram_ss.*) .fram_ss 0x0000000000200020 0x100 obj/default/main.o 0x0000000000200020 FramSS 0x0000000000200120 . = ALIGN (0x4) [!provide] PROVIDE (_alt_partition_fram_ss_end, ABSOLUTE (.)) .fram_dt 0x0000000000200120 0x800 [!provide] PROVIDE (_alt_partition_fram_dt_start, ABSOLUTE (.)) *(.fram_dt .fram_dt.*) .fram_dt 0x0000000000200120 0x800 obj/default/main.o 0x0000000000200120 FramDT 0x0000000000200920 . = ALIGN (0x4) [!provide] PROVIDE (_alt_partition_fram_dt_end, ABSOLUTE (.)) .fram_tm 0x0000000000200920 0x404 [!provide] PROVIDE (_alt_partition_fram_tm_start, ABSOLUTE (.)) *(.fram_tm .fram_tm.*) .fram_tm 0x0000000000200920 0x401 obj/default/main.o 0x0000000000200920 FramST 0x0000000000200921 FramTM 0x0000000000200d24 . = ALIGN (0x4) *fill* 0x0000000000200d21 0x3 [!provide] PROVIDE (_alt_partition_fram_tm_end, ABSOLUTE (.)) .............
データ比較
データ転送(リード/ライト)が正常に行われたかNios内とテストベンチ内で確認します。
ソースコード
C言語(Nios)
メインプログラム(main.c)
上記経路確認と割り込み処理を記述しています。
また、波形で見た時にどの部分を実行中か分かるようにport_aを使ってステップ番号を出力しています。
#define _MAIN_C_ #include <stdio.h> #include <stdlib.h> #include <string.h> #include "system.h" #include "io.h" #include "sys/alt_irq.h" #include "altera_avalon_pio_regs.h" #include "nml_framif.h" //#define DBG_PRINT(...) printf(__VA_ARGS__) #define DBG_PRINT(...) // ** // ** Interrupt program // *************************************************************************************** int fram_irq_step = 0; static void fram_ir(void *context,unsigned int id){ unsigned int status = 0; status = NML_FRAMIF_GETREG_IRS(); // Interrupt status NML_FRAMIF_SETREG_IRS(status); // Interrupt clear fram_irq_step++; } // ** // ** Main program // *************************************************************************************** // // [ Dedicated memory] // Split PARAMETER memory with Linker unsigned char FramID[ 16] __attribute__ ((section (".fram_id"))); // Device ID unsigned char FramSN[ 16] __attribute__ ((section (".fram_sn"))); // Serial Number unsigned char FramSS[ 256] __attribute__ ((section (".fram_ss"))); // Special Sector unsigned char FramDT[2048] __attribute__ ((section (".fram_dt"))); // Data Memory unsigned char FramTM[1024] __attribute__ ((section (".fram_tm"))); // Temporary unsigned char FramST __attribute__ ((section (".fram_tm"))); // Status int main(void){ unsigned int addr = 0; unsigned char exp = 0; unsigned int err = 0; // == // == Test // ========================================================================= // ++ // ++ Status // +++++++++++++++++++++++++++++++++++++++++++ IOWR_ALTERA_AVALON_PIO_DATA(PORT_A_BASE,0x01); // Step No NML_FRAMIF_RDSR(&FramST); if( FramST != 0x40 ) err++; DBG_PRINT(" >>> Step%d : error[%d]\n",IORD_ALTERA_AVALON_PIO_DATA(PORT_A_BASE),err); // ++ // ++ Waiting for master load // +++++++++++++++++++++++++++++++++++++++++++ IOWR_ALTERA_AVALON_PIO_DATA(PORT_A_BASE,0x02); // Step No if( (FramID[0]==0x11) | (FramID[1]==0x22) | (FramID[2]==0x33) | (FramID[3]==0x44) | (FramID[4]==0x55) | (FramID[5]==0x66) | (FramID[6]==0x77) | (FramID[7]==0x88) | (FramID[8]==0x99) ); else err++; DBG_PRINT(" >>> Step%d : error[%d]\n",IORD_ALTERA_AVALON_PIO_DATA(PORT_A_BASE),err); // ++ // ++ Serial number confirmation // +++++++++++++++++++++++++++++++++++++++++++ IOWR_ALTERA_AVALON_PIO_DATA(PORT_A_BASE,0x03); // Step No if( (FramSN[0]==0x01) | (FramSN[1]==0x23) | (FramSN[2]==0x45) | (FramSN[3]==0x67) | (FramSN[4]==0x89) | (FramSN[5]==0xAB) | (FramSN[6]==0xCD) | (FramSN[7]==0xEF) ); else err++; DBG_PRINT(" >>> Step%d : error[%d]\n",IORD_ALTERA_AVALON_PIO_DATA(PORT_A_BASE),err); // ++ // ++ Initial setting for FRAM I/F // +++++++++++++++++++++++++++++++++++++++++++ IOWR_ALTERA_AVALON_PIO_DATA(PORT_A_BASE,0x04); // Step No NML_FRAMIF_SETUP(0,0,0); // ++ // ++ Data rewriting // +++++++++++++++++++++++++++++++++++++++++++ IOWR_ALTERA_AVALON_PIO_DATA(PORT_A_BASE,0x05); // Step No for(addr=0;addr<8;addr++) FramSN[addr] = (unsigned char)(0xFF-FramSN[addr]); for(addr=0;addr<64;addr++) FramSS[addr] = (unsigned char)(0x00-FramSS[addr]); for(addr=0;addr<128;addr++) FramDT[addr] = (unsigned char)(FramDT[addr]+0xFF); // ++ // ++ Save // +++++++++++++++++++++++++++++++++++++++++++ IOWR_ALTERA_AVALON_PIO_DATA(PORT_A_BASE,0x06); // Step No NML_FRAMIF_SETWP(NML_FRAMIF_WP_OFF); // ** Protect IOWR_ALTERA_AVALON_PIO_DATA(PORT_A_BASE,0x07); // Step No NML_FRAMIF_WRSN(FramSN); // Write Serial Number IOWR_ALTERA_AVALON_PIO_DATA(PORT_A_BASE,0x08); // Step No NML_FRAMIF_SSWR( 0x000000, 64,FramSS); // Special Sector Write IOWR_ALTERA_AVALON_PIO_DATA(PORT_A_BASE,0x09); // Step No NML_FRAMIF_WRITE(0x000000,128,FramDT); // Write Data to Memory IOWR_ALTERA_AVALON_PIO_DATA(PORT_A_BASE,0x0A); // Step No NML_FRAMIF_SETWP(NML_FRAMIF_WP_ON); // ** Protect // ++ // ++ Load // +++++++++++++++++++++++++++++++++++++++++++ IOWR_ALTERA_AVALON_PIO_DATA(PORT_A_BASE,0x0B); // Step No NML_FRAMIF_RDID(FramTM); // Read Device ID for(addr=0;addr<9;addr++){ exp = FramID[addr]+1; if( exp != FramTM[addr] ) err++; } IOWR_ALTERA_AVALON_PIO_DATA(PORT_A_BASE,0x0C); // Step No NML_FRAMIF_RDSN(FramTM); // Read Serial No for(addr=0;addr<8;addr++){ exp = FramSN[addr] + 1; if( exp != FramTM[addr] ) err++; } IOWR_ALTERA_AVALON_PIO_DATA(PORT_A_BASE,0x0D); // Step No NML_FRAMIF_SSRD( 0x000000, 64,FramTM); // Special Sector Read for(addr=0;addr< 64;addr++){ exp = FramSS[addr] + 1; if( exp != FramTM[addr] ) err++; } IOWR_ALTERA_AVALON_PIO_DATA(PORT_A_BASE,0x0E); // Step No NML_FRAMIF_READ( 0x000000,128,FramTM); // Read Data from Memory for(addr=0;addr<128;addr++){ exp = FramDT[addr] + 1; if( exp != FramTM[addr] ) err++; } IOWR_ALTERA_AVALON_PIO_DATA(PORT_A_BASE,0x0F); // Step No DBG_PRINT(" >>>>> FRAM Read/Write Test End : Error [%d]\n\n",err); NML_FRAMIF_SETWP(NML_FRAMIF_WP_OFF); // ** Protect // ========================================================================= // == // == Interrupt Test // ========================================================================= IOWR_ALTERA_AVALON_PIO_DATA(PORT_A_BASE,0x10); // Step No NML_FRAMIF_SETREG_IRS(1); // Clear NML_FRAMIF_SETREG_IRM(0); // Unmask alt_irq_register(0,0,fram_ir); // Interrupt processing registration while(1){ IOWR_ALTERA_AVALON_PIO_DATA(PORT_B_BASE,fram_irq_step); if( fram_irq_step == 0 ){ IOWR_ALTERA_AVALON_PIO_DATA(PORT_A_BASE,0x11); NML_FRAMIF_SETWP(NML_FRAMIF_WP_OFF); // ** Protect NML_FRAMIF_WREN(); // Enable Write NML_FRAMIF_SETREG_BAD(0x000000); // Buffer Address NML_FRAMIF_SETREG_BDT(0x02); // Buffer Data [Command] NML_FRAMIF_SETREG_BDT(0x00); // Buffer Data [Address] NML_FRAMIF_SETREG_BDT(0x80); // Buffer Data [Address] NML_FRAMIF_SETREG_BDT(0x00); // Buffer Data [Address] for(addr=0;addr<16;addr++){ // Buffer Data [Write Data] NML_FRAMIF_SETREG_BDT(addr+5); } NML_FRAMIF_SETREG_ACS(1+3+16-1); // Access NML_FRAMIF_SETREG_OTC(1+3+16-1); // Output fram_irq_step++; NML_FRAMIF_SETREG_IRM(0); // Unmask NML_FRAMIF_SETREG_ENB(1); // START }; if( fram_irq_step == 2 ){ IOWR_ALTERA_AVALON_PIO_DATA(PORT_A_BASE,0x12); NML_FRAMIF_SETWP(NML_FRAMIF_WP_ON); // ** Protect NML_FRAMIF_SETREG_BAD(0x000000); // Buffer Address NML_FRAMIF_SETREG_BDT(0x03); // Buffer Data [Command] NML_FRAMIF_SETREG_ACS(1+3+16-1); // Access NML_FRAMIF_SETREG_OTC(1+3 -1); // Output fram_irq_step++; NML_FRAMIF_SETREG_IRM(0); // Unmask NML_FRAMIF_SETREG_ENB(1); // START } if( fram_irq_step == 4 ){ IOWR_ALTERA_AVALON_PIO_DATA(PORT_A_BASE,0x13); NML_FRAMIF_SETREG_BAD(0x000004); // Buffer Address for(addr=0;addr<16;addr++){ // Buffer Data [Write Data] exp = addr+5+3; if( (unsigned char)(NML_FRAMIF_GETREG_BDT()) != exp ) err++; } break; } } // ++++++ Final Message +++++ IOWR_ALTERA_AVALON_PIO_DATA(PORT_A_BASE,0xFF); printf(" >>>>> FRAM Read/Write Test End : Error [%d]\n",err); NML_FRAMIF_SETWP(NML_FRAMIF_WP_OFF); return 0; }
FRAM I/F用プログラム(framif.c)
#ifndef _NML_FRAMIF_C_ #define _NML_FRAMIF_C_ #include <stdio.h> #include <stdlib.h> #include "system.h" #include "io.h" #include "altera_avalon_pio_regs.h" #include "nml_framif.h" // -- // -- Initial setting // ----------------------------------------------------------------------------- void NML_FRAMIF_SETUP( unsigned int Div , // Serial clock division [0 to 15] unsigned int Pol , // Serial clock polarity [0:Low 1:High] unsigned int Tak // Import timing [0 to 3] ){ NML_FRAMIF_SETREG_CNT(Tak,Pol,Div); NML_FRAMIF_SETREG_IRM(1); NML_FRAMIF_SETREG_IRS(1); } void NML_FRAMIF_BUFINIT(void){ unsigned int bad = 0; NML_FRAMIF_SETREG_BAD(bad); for(bad=0;bad<NML_FRAMIF_BUFSIZE;bad++){ NML_FRAMIF_SETREG_BDT(0x00); } } // -- // -- FRAM Access // ----------------------------------------------------------------------------- // ******************************************************************* // ** // ** Private Function // ** // @@ // @@ Transfer processing (Common) // @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ char nml_framif_access( unsigned char Polling , // End detection method [1:Register polling] unsigned char Command , // Command int Address , // Address (-1 or less and no address setting) unsigned int PutSize , // Storage size unsigned int GetSize , // Acquisition size unsigned char *Data // Data ){ unsigned char puttype = 0; unsigned int acs = 0; unsigned int otc = 0; unsigned int tmp = 0; // ++ Confirmation of operation // ++ Back if running // +++++++++++++++++++++++++++++++++++++++++++ if( NML_FRAMIF_GETREG_ENB() != 0 ) return NML_FRAMIF_STA_ERR; // ++ Parsing arguments // ++ (PutSize==0) & (GetSize==0) : Write system (command only) // ++ (PutSize!=0) & (GetSize==0) : Write system // ++ (PutSize==0) & (GetSize!=0) : Read system // ++ Others : Write system (command only) // +++++++++++++++++++++++++++++++++++++++++++ if( (PutSize==0) & (GetSize==0) ){ puttype = 1; acs = 1; otc = 1; } else if( (PutSize!=0) & (GetSize==0) ){ puttype = 1; acs = 1+PutSize; otc = 1+PutSize; } else if( (PutSize==0) & (GetSize!=0) ){ puttype = 0; acs = 1+GetSize; otc = 1; } else { puttype = 1; acs = 1; otc = 1; } // ++ Interrupt clear and mask // +++++++++++++++++++++++++++++++++++++++++++ NML_FRAMIF_SETREG_IRM(1); // Mask NML_FRAMIF_SETREG_IRS(1); // Clear // ++ Setting // +++++++++++++++++++++++++++++++++++++++++++ NML_FRAMIF_SETREG_BAD(0); NML_FRAMIF_SETREG_BDT(Command); if( Address >= 0 ){ acs = acs + 3; otc = otc + 3; NML_FRAMIF_SETREG_BDT(Address>>16); NML_FRAMIF_SETREG_BDT(Address>> 8); NML_FRAMIF_SETREG_BDT(Address>> 0); } NML_FRAMIF_SETREG_ACS((acs-1)); NML_FRAMIF_SETREG_OTC((otc-1)); // ++ Data storage // +++++++++++++++++++++++++++++++++++++++++++ if( puttype == 1 ){ for(tmp=0;tmp<PutSize;tmp++) NML_FRAMIF_SETREG_BDT(Data[tmp]); } // ++ Transfer start // +++++++++++++++++++++++++++++++++++++++++++ NML_FRAMIF_SETREG_ENB(1); // ++ Transfer complete // +++++++++++++++++++++++++++++++++++++++++++ if( Polling == 1 ){ while( NML_FRAMIF_GETREG_ENB() != 0 ); NML_FRAMIF_SETREG_IRS(1); if( puttype == 0 ){ if( Address < 0 ) NML_FRAMIF_SETREG_BAD(1); else NML_FRAMIF_SETREG_BAD(4); for(tmp=0;tmp<GetSize;tmp++) Data[tmp] = NML_FRAMIF_GETREG_BDT(); } return NML_FRAMIF_STA_FIN; } return NML_FRAMIF_STA_SET; } // @@ // @@ Write processing (Common) // @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ char nml_framif_acc_wr( unsigned char Type , // Write type unsigned int Address , // Start address unsigned int Length , // Storage size (Range:1 to NML_FRAMIF_BUFSIZE-4) unsigned char *Data // Data ){ char status = 0; // ++ Write Enable ON // +++++++++++++++++++++++++++++++++++++++++++ if( Type == 0 ) return nml_framif_access(1,0x06,-1,0,0,NULL); if( (status=nml_framif_access(1,0x06,-1,0,0,NULL)) != NML_FRAMIF_STA_FIN ) return status; // ++ Write operation // +++++++++++++++++++++++++++++++++++++++++++ if( Type == 1 ) return nml_framif_access(1,0x01, -1, 1,0,Data); else if( Type == 2 ) return nml_framif_access(1,0xC2, -1, 8,0,Data); else if( Type == 3 ) return nml_framif_access(1,0x02,(int)(Address),Length,0,Data); else if( Type == 4 ) return nml_framif_access(1,0x42,(int)(Address),Length,0,Data); else return nml_framif_access(1,0x04, -1, 0,0,NULL); } // ** // ******************************************************************* // ++ // ++ Write system (Supports CY15B102QN commands) // +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ char NML_FRAMIF_WREN(void) { return nml_framif_acc_wr(0,0,0,NULL); } char NML_FRAMIF_WRSR(unsigned char *Data) { return nml_framif_acc_wr(1,0,0,Data); } char NML_FRAMIF_WRSN(unsigned char *Data) { return nml_framif_acc_wr(2,0,0,Data); } char NML_FRAMIF_WRDI(void) { return nml_framif_acc_wr(5,0,0,NULL); } char NML_FRAMIF_WRITE( unsigned int Address , // Start address unsigned int Length , // Storage size (Range:1~NML_FRAMIF_BUFSIZE-4) unsigned char *Data // Data ){ return nml_framif_acc_wr(3,Address,Length,Data); } char NML_FRAMIF_SSWR( unsigned int Address , // Start address unsigned int Length , // Storage size (Range:1 to NML_FRAMIF_BUFSIZE-4) unsigned char *Data // Data ){ return nml_framif_acc_wr(4,Address,Length,Data); } // ++ // ++ Read system (Supports CY15B102QN commands) // +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ char NML_FRAMIF_RDSR(unsigned char *Data) { return nml_framif_access(1,0x05,-1,0,1,Data); } char NML_FRAMIF_RDID(unsigned char *Data) { return nml_framif_access(1,0x9F,-1,0,9,Data); } char NML_FRAMIF_RDSN(unsigned char *Data) { return nml_framif_access(1,0xC3,-1,0,8,Data); } char NML_FRAMIF_READ( unsigned int Address , // Start address unsigned int Length , // Storage size (Range:1 to NML_FRAMIF_BUFSIZE-4) unsigned char *Data // Data ){ return nml_framif_access(1,0x03,(int)(Address),0,Length,Data); } char NML_FRAMIF_SSRD( unsigned int Address , // Start address unsigned int Length , // Storage size (Range:1 to NML_FRAMIF_BUFSIZE-4) unsigned char *Data // Data ){ return nml_framif_access(1,0x4B,(int)(Address),0,Length,Data); } // -- // -- Write protect control // ----------------------------------------------------------------------------- void NML_FRAMIF_SETWP( unsigned char Flag ){ IOWR_ALTERA_AVALON_PIO_DATA(FRAM_WP_BASE, (unsigned int)(Flag)); } unsigned char NML_FRAMIF_GETWP(void){ return (unsigned char)(IORD_ALTERA_AVALON_PIO_DATA(FRAM_WP_BASE)); } #endif /* _NML_FRAMIF_C_ */
FRAM I/Fのヘッダファイル(framif.h)
#ifndef _NML_FRAMIF_H_ #define _NML_FRAMIF_H_ #include "system.h" #include "io.h" // -- // -- Define // ----------------------------------------------------------------------------- #define NML_FRAMIF_BUFSIZE (4096) // Buffer size in FRAM I / F #define NML_FRAMIF_STA_ERR (-1) // FRAM I/F is in operation #define NML_FRAMIF_STA_FIN ( 1) // Processing to FRAM I/F is completed #define NML_FRAMIF_STA_SET ( 2) // Finished setting process to FRAM I/F #define NML_FRAMIF_WP_ON (0) // Write protect ON #define NML_FRAMIF_WP_OFF (1) // Write protect OFF // -- // -- Register for FRAM I/F // ----------------------------------------------------------------------------- #define NML_FRAMIF_SETREG_ENB(enb) (IOWR(FRAM_BASE,0x00, ((enb)&0x00000001))) #define NML_FRAMIF_SETREG_CNT(tak,pol,div) (IOWR(FRAM_BASE,0x01,(((tak)&0x00000001)<<5)| \ (((pol)&0x00000001)<<4)| \ (((div)&0x0000000F)<<0))) #define NML_FRAMIF_SETREG_ACS(acs) (IOWR(FRAM_BASE,0x02, ((acs)&0x001FFFFF))) #define NML_FRAMIF_SETREG_OTC(otc) (IOWR(FRAM_BASE,0x03, ((otc)&0x001FFFFF))) #define NML_FRAMIF_SETREG_IRS(irs) (IOWR(FRAM_BASE,0x04, ((irs)&0x00000001))) #define NML_FRAMIF_SETREG_IRM(irm) (IOWR(FRAM_BASE,0x05, ((irm)&0x00000001))) #define NML_FRAMIF_SETREG_BAD(bad) (IOWR(FRAM_BASE,0x06, ((bad)&0x001FFFFF))) #define NML_FRAMIF_SETREG_BDT(bdt) (IOWR(FRAM_BASE,0x07, ((bdt)&0x000000FF))) #define NML_FRAMIF_GETREG_ENB(void) (IORD(FRAM_BASE,0x00)) #define NML_FRAMIF_GETREG_CNT(void) (IORD(FRAM_BASE,0x01)) #define NML_FRAMIF_GETREG_ACS(void) (IORD(FRAM_BASE,0x02)) #define NML_FRAMIF_GETREG_OTC(void) (IORD(FRAM_BASE,0x03)) #define NML_FRAMIF_GETREG_IRS(void) (IORD(FRAM_BASE,0x04)) #define NML_FRAMIF_GETREG_IRM(void) (IORD(FRAM_BASE,0x05)) #define NML_FRAMIF_GETREG_BAD(void) (IORD(FRAM_BASE,0x06)) #define NML_FRAMIF_GETREG_BDT(void) (IORD(FRAM_BASE,0x07)) #define NML_FRAMIF_GETREG_CNT_TAK(void) ((IORD(FRAM_BASE,0x01)>>5)&0x03) #define NML_FRAMIF_GETREG_CNT_POL(void) ((IORD(FRAM_BASE,0x01)>>4)&0x01) #define NML_FRAMIF_GETREG_CNT_DIV(void) ((IORD(FRAM_BASE,0x01)>>0)&0x0F) // -- // -- Function for FRAM I/F // ----------------------------------------------------------------------------- #ifdef _NML_FRAMIF_C_ void NML_FRAMIF_SETUP( // ** Initial setting unsigned int Div , // Serial clock division [0 to 15] unsigned int Pol , // Serial clock polarity [0:Low 1:High] unsigned int Tak // Import timing [0 to 3] ); void NML_FRAMIF_BUFINIT(void); // ** Buffer initialization // ***** Write protect control void NML_FRAMIF_SETWP(unsigned char); // ** Write protect settings unsigned char NML_FRAMIF_GETWP(void); // ** Get write protect status // ***** Write system(Supports CY15B102QN commands) char NML_FRAMIF_WREN(void) ; // ** Enable Write char NML_FRAMIF_WRDI(void) ; // ** Disable Write char NML_FRAMIF_WRSR(unsigned char *Data) ; // ** Write Status Register char NML_FRAMIF_WRSN(unsigned char *Data) ; // ** Write Serial Number char NML_FRAMIF_WRITE( // ** Write Data to Memory unsigned int Address , // Start address unsigned int Length , // Storage size (Range:1~NML_FRAMIF_BUFSIZE-4) unsigned char *Data // Data ); char NML_FRAMIF_SSWR( // ** Special Sector Write unsigned int Address , // Start address unsigned int Length , // Storage size (Range:1~NML_FRAMIF_BUFSIZE-4) unsigned char *Data // Data ); // ***** Read system(Supports CY15B102QN commands) char NML_FRAMIF_RDSR(unsigned char *Data) ; // ** Read Status Register char NML_FRAMIF_RDID(unsigned char *Data) ; // ** Read Device ID char NML_FRAMIF_RDSN(unsigned char *Data) ; // ** Read Serial No char NML_FRAMIF_READ( // ** Read Data from Memory unsigned int Address , // Start address unsigned int Length , // Storage size (Range:1~NML_FRAMIF_BUFSIZE-4) unsigned char *Data // Data ); char NML_FRAMIF_SSRD( // ** Special Sector Read unsigned int Address , // Start address unsigned int Length , // Storage size (Range:1~NML_FRAMIF_BUFSIZE-4) unsigned char *Data // Data ); #else // --------------------------------------------------------------------- extern void NML_FRAMIF_SETUP( // ** Initial setting unsigned int Div , // Serial clock division [0 to 15] unsigned int Pol , // Serial clock polarity [0:Low 1:High] unsigned int Tak // Import timing [0 to 3] ); extern void NML_FRAMIF_BUFINIT(void); // ** Buffer initialization // ***** Write protect control extern void NML_FRAMIF_SETWP(unsigned char); // ** Write protect settings extern unsigned char NML_FRAMIF_GETWP(void); // ** Get write protect status // ***** Write system(Supports CY15B102QN commands) extern char NML_FRAMIF_WREN(void) ; // ** Enable Write extern char NML_FRAMIF_WRDI(void) ; // ** Disable Write extern char NML_FRAMIF_WRSR(unsigned char *Data) ; // ** Write Status Register extern char NML_FRAMIF_WRSN(unsigned char *Data) ; // ** Write Serial Number extern char NML_FRAMIF_WRITE( // ** Write Data to Memory unsigned int Address , // Start address unsigned int Length , // Storage size (Range:1~NML_FRAMIF_BUFSIZE-4) unsigned char *Data // Data ); extern char NML_FRAMIF_SSWR( // ** Special Sector Write unsigned int Address , // Start address unsigned int Length , // Storage size (Range:1~NML_FRAMIF_BUFSIZE-4) unsigned char *Data // Data ); // ***** Read system(Supports CY15B102QN commands) extern char NML_FRAMIF_RDSR(unsigned char *Data) ; // ** Read Status Register extern char NML_FRAMIF_RDID(unsigned char *Data) ; // ** Read Device ID extern char NML_FRAMIF_RDSN(unsigned char *Data) ; // ** Read Serial No extern char NML_FRAMIF_READ( // ** Read Data from Memory unsigned int Address , // Start address unsigned int Length , // Storage size (Range:1~NML_FRAMIF_BUFSIZE-4) unsigned char *Data // Data ); extern char NML_FRAMIF_SSRD( // ** Special Sector Read unsigned int Address , // Start address unsigned int Length , // Storage size (Range:1~NML_FRAMIF_BUFSIZE-4) unsigned char *Data // Data ); #endif #endif /* _NML_FRAMIF_H_ */
verilog言語(テストベンチ)
テストベンチでは、リセット解除後にFRAM I/Fブロックをマスタモードで動作させるための記述(forceで入力信号PRM_???を変更して動作させています)と、FRAM内のメモリ配列を確認したり書き換えたりしています。
`timescale 1ns/1ps module TB_TOP; integer PERIOD = 0; reg RESET_N = 0; reg CLOCK = 0; reg SW_SLAVE = 1; reg SW_GO = 0; wire FRAM_CS_N ; // @@ [SYS_CLK↑同期] FRAM SPI チップセレクト wire FRAM_SCLK ; // @@ [SYS_CLK↑同期] FRAM SPI シリアルクロック wire FRAM_MOSI ; // @@ [SYS_CLK↑同期] FRAM SPI シリアル出力 wire FRAM_MISO ; // @@ [SYS_CLK↑同期] FRAM SPI シリアル入力 // ++ タイミング設定 // +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ // Clock波形 real tcCLOCK = 40.000; real tpCLOCK = 5.000; real tnCLOCK = 25.000; // FRAM モデル間遅延 defparam UnDlyFRAM_CS_N.NsStep= 0 ,UnDlyFRAM_CS_N.PsStep= 0; defparam UnDlyFRAM_SCLK.NsStep= 0 ,UnDlyFRAM_SCLK.PsStep= 0; defparam UnDlyFRAM_MOSI.NsStep= 0 ,UnDlyFRAM_MOSI.PsStep= 0; defparam UnDlyFRAM_MISO.NsStep= 0 ,UnDlyFRAM_MISO.PsStep= 0; // -- // -- Clock生成 // ------------------------------------------------------------------- initial begin forever begin fork #(tpCLOCK) CLOCK = 1; #(tnCLOCK) CLOCK = 0; #(tcCLOCK) PERIOD = PERIOD+1; join end end // -- // -- Reset // ------------------------------------------------------------------- reg [ 7:0] tmpID[0: 8]; reg [ 7:0] tmpSN[0: 7]; reg [ 7:0] tmpSS[0: 255]; reg [ 7:0] tmpDT[0:262143]; integer error = 0; reg [ 7:0] exp; initial begin : GenReset integer a; #0 RESET_N = 0; repeat(10) @(PERIOD); RESET_N = 1; FRAM.Device_ID[0] = 'h11; FRAM.Device_ID[1] = 'h22; FRAM.Device_ID[2] = 'h33; FRAM.Device_ID[3] = 'h44; FRAM.Device_ID[4] = 'h55; FRAM.Device_ID[5] = 'h66; FRAM.Device_ID[6] = 'h77; FRAM.Device_ID[7] = 'h88; FRAM.Device_ID[8] = 'h99; FRAM.Serial_No[0] = 'h01; FRAM.Serial_No[1] = 'h23; FRAM.Serial_No[2] = 'h45; FRAM.Serial_No[3] = 'h67; FRAM.Serial_No[4] = 'h89; FRAM.Serial_No[5] = 'hAB; FRAM.Serial_No[6] = 'hCD; FRAM.Serial_No[7] = 'hEF; for(a=0;a<256;a=a+1) FRAM.Smem[a] = $random; for(a=0;a<262144;a=a+1) FRAM.mem[a] = $random; /***** Copy *****/ for(a=0;a<9;a=a+1) tmpID[a] = FRAM.Device_ID[a]; for(a=0;a<8;a=a+1) tmpSN[a] = FRAM.Serial_No[a]; for(a=0;a<256;a=a+1) tmpSS[a] = FRAM.Smem[a]; for(a=0;a<262144;a=a+1) tmpDT[a] = FRAM.mem[a]; wait( TOP.FRAM_WP_N === 1 ) $display(" >>> FRAM Protect OFF"); wait( TOP.FRAM_WP_N === 0 ) $display(" >>> FRAM Protect ON"); for(a=0;a<9;a=a+1) begin if( FRAM.Device_ID[a] !== tmpID[a] ) error = error + 1; end $display(" >>> Device ID : Error = %d",error); for(a=0;a<8;a=a+1) begin exp = 'hFF - tmpSN[a]; if( exp !== FRAM.Serial_No[a] ) error = error + 1; end $display(" >>> Serial No : Error = %d",error); for(a=0;a<64;a=a+1) begin exp = 'h00 - tmpSS[a]; if( exp !== FRAM.Smem[a] ) error = error + 1; end $display(" >>> Special Sector : Error = %d",error); for(a=0;a<128;a=a+1) begin exp = 'hFF + tmpDT[a]; if( exp !== FRAM.mem[a] ) error = error + 1; end $display(" >>> Memory Sector : Error = %d",error); for(a=0;a<9;a=a+1) FRAM.Device_ID[a] = FRAM.Device_ID[a] + 1; for(a=0;a<8;a=a+1) FRAM.Serial_No[a] = FRAM.Serial_No[a] + 1; for(a=0;a<256;a=a+1) FRAM.Smem[a] = FRAM.Smem[a] + 1; for(a=0;a<128;a=a+1) FRAM.mem[a] = FRAM.mem[a] + 1; wait( TOP.FRAM_WP_N === 1 ) $display(" >>> FRAM Protect OFF"); wait( TOP.FRAM_WP_N === 0 ) $display(" >>> FRAM Protect ON"); for(a='h8000;a<'h8010;a=a+1) FRAM.mem[a] = FRAM.mem[a]+3; wait( TOP.FRAM_WP_N === 1 ); $display(" >>>>> FRAM Save Data Error : %d",error); end // -- // -- Mater Mode 起動 Force操作 // ------------------------------------------------------------------- parameter LOAD_STA_ID = 'h20_0000; parameter LOAD_STA_SN = 'h20_0010; parameter LOAD_STA_SS = 'h20_0020; parameter LOAD_STA_DT = 'h20_0120; parameter LOAD_STA_TM = 'h20_0920; initial begin : MasterBoot #0; wait( TOP.cpu_rst_n === 1'b1 ); $display(" >>> %m : Reset Off"); repeat(10) @(posedge TOP.cpu_clk); force TOP.UnFRAMIF.MST_SLV = 0; // Master Mode force TOP.UnFRAMIF.PRM_MSK = 1; // Mask force TOP.UnFRAMIF.PRM_CLR = 1; // Clear force TOP.UnFRAMIF.PRM_DIV = 0; force TOP.UnFRAMIF.PRM_POL = 0; force TOP.UnFRAMIF.PRM_TAK = 0; // ++ シリアルNo 取得 $display(" >>> %m : Read Serial No"); #1; force TOP.UnFRAMIF.PRM_CMD = 'hC3; force TOP.UnFRAMIF.PRM_ADD = 0; force TOP.UnFRAMIF.PRM_ACS = 1+8 -1; force TOP.UnFRAMIF.PRM_OTC = 1 -1; force TOP.UnFRAMIF.PRM_STA = LOAD_STA_SN; repeat(10) @(posedge TOP.cpu_clk); #1; $display(" >>> %m : CMD = %h",TOP.UnFRAMIF.PRM_CMD); $display(" >>> %m : ADD = %h",TOP.UnFRAMIF.PRM_ADD); $display(" >>> %m : ACS = %d",TOP.UnFRAMIF.PRM_ACS); $display(" >>> %m : OTC = %d",TOP.UnFRAMIF.PRM_OTC); $display(" >>> %m : STA = %h",TOP.UnFRAMIF.PRM_STA); $display(" >>> %m : START"); force TOP.UnFRAMIF.PRM_ENB = 1; repeat( 5) @(posedge TOP.cpu_clk); #1; wait( TOP.UnFRAMIF.MON_ENB === 1'b1 ); #1; wait( TOP.UnFRAMIF.MON_ENB === 1'b0 ); #1; $display(" >>> %m : FINISH"); repeat(20) @(posedge TOP.cpu_clk); #1; force TOP.UnFRAMIF.PRM_ENB = 0; repeat(10) @(posedge TOP.cpu_clk); #1; // ++ 特殊セクタ 取得 $display(" >>> %m : Special Sector Read"); #1; force TOP.UnFRAMIF.PRM_CMD = 'h4B; force TOP.UnFRAMIF.PRM_ADD = 0; force TOP.UnFRAMIF.PRM_ACS = 64+4 -1; force TOP.UnFRAMIF.PRM_OTC = 4 -1; force TOP.UnFRAMIF.PRM_STA = LOAD_STA_SS; repeat(10) @(posedge TOP.cpu_clk); #1; $display(" >>> %m : CMD = %h",TOP.UnFRAMIF.PRM_CMD); $display(" >>> %m : ADD = %h",TOP.UnFRAMIF.PRM_ADD); $display(" >>> %m : ACS = %d",TOP.UnFRAMIF.PRM_ACS); $display(" >>> %m : OTC = %d",TOP.UnFRAMIF.PRM_OTC); $display(" >>> %m : STA = %h",TOP.UnFRAMIF.PRM_STA); $display(" >>> %m : START"); force TOP.UnFRAMIF.PRM_ENB = 1; repeat( 5) @(posedge TOP.cpu_clk); #1; wait( TOP.UnFRAMIF.MON_ENB === 1'b1 ); #1; wait( TOP.UnFRAMIF.MON_ENB === 1'b0 ); #1; $display(" >>> %m : FINISH"); repeat(20) @(posedge TOP.cpu_clk); #1; force TOP.UnFRAMIF.PRM_ENB = 0; repeat(10) @(posedge TOP.cpu_clk); #1; // ++ メモリデータ 取得 $display(" >>> %m : Read Data from Memory"); #1; force TOP.UnFRAMIF.PRM_CMD = 'h03; force TOP.UnFRAMIF.PRM_ADD = 0; force TOP.UnFRAMIF.PRM_ACS = 128+4 -1; force TOP.UnFRAMIF.PRM_OTC = 4 -1; force TOP.UnFRAMIF.PRM_STA = LOAD_STA_DT; repeat(10) @(posedge TOP.cpu_clk); #1; $display(" >>> %m : CMD = %h",TOP.UnFRAMIF.PRM_CMD); $display(" >>> %m : ADD = %h",TOP.UnFRAMIF.PRM_ADD); $display(" >>> %m : ACS = %d",TOP.UnFRAMIF.PRM_ACS); $display(" >>> %m : OTC = %d",TOP.UnFRAMIF.PRM_OTC); $display(" >>> %m : STA = %h",TOP.UnFRAMIF.PRM_STA); $display(" >>> %m : START"); force TOP.UnFRAMIF.PRM_ENB = 1; repeat( 5) @(posedge TOP.cpu_clk); #1; wait( TOP.UnFRAMIF.MON_ENB === 1'b1 ); #1; wait( TOP.UnFRAMIF.MON_ENB === 1'b0 ); #1; $display(" >>> %m : FINISH"); repeat(20) @(posedge TOP.cpu_clk); #1; force TOP.UnFRAMIF.PRM_ENB = 0; repeat(10) @(posedge TOP.cpu_clk); #1; // ++ デバイスID 取得 $display(" >>> %m : Read Device ID"); #1; force TOP.UnFRAMIF.PRM_CMD = 'h9F; force TOP.UnFRAMIF.PRM_ADD = 0; force TOP.UnFRAMIF.PRM_ACS = 9+1 -1; force TOP.UnFRAMIF.PRM_OTC = 1 -1; force TOP.UnFRAMIF.PRM_STA = LOAD_STA_ID; repeat(10) @(posedge TOP.cpu_clk); #1; $display(" >>> %m : CMD = %h",TOP.UnFRAMIF.PRM_CMD); $display(" >>> %m : ADD = %h",TOP.UnFRAMIF.PRM_ADD); $display(" >>> %m : ACS = %d",TOP.UnFRAMIF.PRM_ACS); $display(" >>> %m : OTC = %d",TOP.UnFRAMIF.PRM_OTC); $display(" >>> %m : STA = %h",TOP.UnFRAMIF.PRM_STA); $display(" >>> %m : START"); force TOP.UnFRAMIF.PRM_ENB = 1; repeat( 5) @(posedge TOP.cpu_clk); #1; wait( TOP.UnFRAMIF.MON_ENB === 1'b1 ); #1; wait( TOP.UnFRAMIF.MON_ENB === 1'b0 ); #1; $display(" >>> %m : FINISH"); repeat(20) @(posedge TOP.cpu_clk); #1; force TOP.UnFRAMIF.PRM_ENB = 0; repeat(10) @(posedge TOP.cpu_clk); #1; // ======================================= $display(" >>> %m : Master Mode End"); #1; release TOP.UnFRAMIF.MST_SLV; release TOP.UnFRAMIF.PRM_ENB; release TOP.UnFRAMIF.PRM_DIV; release TOP.UnFRAMIF.PRM_POL; release TOP.UnFRAMIF.PRM_TAK; release TOP.UnFRAMIF.PRM_ACS; release TOP.UnFRAMIF.PRM_OTC; release TOP.UnFRAMIF.PRM_MSK; release TOP.UnFRAMIF.PRM_CLR; release TOP.UnFRAMIF.PRM_STA; release TOP.UnFRAMIF.PRM_CMD; release TOP.UnFRAMIF.PRM_ADD; end // -- // -- ターゲットのインスタンス // ------------------------------------------------------------------- wire dlyFRAM_CS_N; DlyBuf UnDlyFRAM_CS_N(.A(FRAM_CS_N),.Y(dlyFRAM_CS_N) ); wire dlyFRAM_SCLK; DlyBuf UnDlyFRAM_SCLK(.A(FRAM_SCLK),.Y(dlyFRAM_SCLK) ); wire dlyFRAM_MOSI; DlyBuf UnDlyFRAM_MOSI(.A(FRAM_MOSI),.Y(dlyFRAM_MOSI) ); wire dlyFRAM_MISO; DlyBuf UnDlyFRAM_MISO(.A(FRAM_MISO),.Y(dlyFRAM_MISO) ); TOP TOP( .RESET_N (RESET_N ), // input wire RESET_N , // @@ リセット .CLOCK (CLOCK ), // input wire CLOCK , // @@ クロック .FRAM_WP_N ( ), // output wire FRAM_WP_N , // @@ FRAM ライトプロテクト[0:プロテクト] .FRAM_CS_N ( FRAM_CS_N ), // output wire FRAM_CS_N , // @@ FRAM SPI チップセレクト .FRAM_SCLK ( FRAM_SCLK ), // output wire FRAM_SCLK , // @@ FRAM SPI シリアルクロック .FRAM_MOSI ( FRAM_MOSI ), // output wire FRAM_MOSI , // @@ FRAM SPI シリアル出力 .FRAM_MISO (dlyFRAM_MISO ), // input wire FRAM_MISO , // @@ FRAM SPI シリアル入力 .SW_SLAVE (SW_SLAVE ), // input wire SW_SLAVE , // @@ Master/Slave切替 [0:Master] .SW_GO (SW_GO ), // input wire SW_GO , // @@ 処理イネーブル .LED_LOCK ( ) // output wire LED_LOCK // @@ LED Lock ); reg FRAM_VDD = 0; initial begin #1; FRAM_VDD=1; end FRAM_SPI FRAM( .CSB (dlyFRAM_CS_N ), .SCK (dlyFRAM_SCLK ), .SI (dlyFRAM_MOSI ), .SO ( FRAM_MISO ), .WPB (1'b1 ), .VDD ( FRAM_VDD ) ); endmodule module DlyBuf #( parameter NsStep = 1, parameter PsStep = 1 ) ( input wire A , output wire Y ); wire [NsStep:0] SigNs; wire [PsStep:0] SigPs; assign SigNs[0] = A; generate genvar ins; if( NsStep != 0 ) begin for(ins=1;ins<=NsStep;ins=ins+1) begin DlyBufCoreNs UnDlyNs(.A(SigNs[ins-1]),.Y(SigNs[ins]) ); end end endgenerate assign SigPs[0] = SigNs[NsStep]; generate genvar ips; if( PsStep != 0 ) begin for(ips=1;ips<=PsStep;ips=ips+1) begin DlyBufCorePs UnDlyPs(.A(SigPs[ips-1]),.Y(SigPs[ips]) ); end end endgenerate assign Y = SigPs[PsStep]; endmodule module DlyBufCoreNs(input wire A, output wire Y); assign #(1.000) Y = A; endmodule module DlyBufCorePs(input wire A, output wire Y); assign #(0.001) Y = A; endmodule
最後に
上記で説明した内容は設計したブロック間が正しいか確認するためのNiosプログラムとなります。
経路を確認した後、実際の用途に合わせて、ソフトウェアを作り込むことになります。
FRAMにアクセス (Quartus実行編)
Quartusの論理合成とインプリメントとなります。
ここでは、単に実行するだけになるため、実行結果とSTA結果のみ記載します。
また、FRAM I/FブロックとFRAMデバイス間に遅延を付けて行ったシミュレーション波形も載せています。
TOP構成は、以下をご参照ください。
実行結果
リソース
Input Pin
クロックとリセット以外の入力端子は、「Input Register」を使用していることを確認します。これにより、端子とFF間の配線遅延を小さくすることにより、Setup/Holdをバランスよくします。
以下に入力端子FRAM_MISOとFF(in_miso)の接続関係を示します。
Output Pin
出力端子LED_LOCKを除く信号が「Output Register」を使用していることを確認します。
階層構造とメモリ使用状況
ブロックメモリ(M10K)の使用率は以下の通りです。
STA結果
クロック制約
最高動作周波数
CPU_CLKは50MHz、SYS_CLKは200MHz以上の結果が得られていることが確認できます。
タイミングレポート
各タイミングレポートを確認します。
CPU_CLK(50MHz) 間遅延
FF間が20nsの制約に対し、10ns以上の余裕があることが分かります。
SYS_CLK(200MHz)間遅延
FF間が5nsの制約に対し、0.2ns以上の余裕があることが分かります。
False Path (CPU_CLK → SYS_CLK)
False Path (SYS_CLK → CPU_CLK)
False Path (CPU_CLK間)
False Path (SYS_CLK間)
AC特性
Report Timingを実行し、FRAM制御信号の遅延時間を測定し、AC特性をまとめました。
上記AC特性からFRAMデバイスのAC特性に当てはめると以下となり、各Setup/Holdをカバーすることが分かります。
※tdrMISOの遅延時間 と 基板配線遅延 の 合計がFRAM_SCLKの周期を超えた場合は、基準取り込みタイミングを遅延させる調整が必要になります。(REG_TAK又はPRM_TAK)
シミュレーション
FRAM I/FブロックとFRAMデバイス間の信号に遅延を与えてシミュレーションを行いました。
各信号に上表(AC特性)の遅延値(tdo????,tdi????)を与えてRTLシミュレーションを行いました。
以下が波形となります。
※起点となるFRAM_SCLK↓ から in_misoまでの遅延が18.283nsとなります。
※遅延値の与え方は、前回のテストベンチ内のコメント「FRAM モデル間遅延」で調整できるようになっています。
遅延量18.283に+2nsして20.283nsにした場合の波形を以下に示します。
FRAM_SCLK周期を超えたため、1bitズレたパラレルデータになります。
20.283nsの遅延で取り込みタイミングをPRM_TAK又はREG_TAKで1クロックずらすことにより、周期越えした場合でも取り込むことができます。以下の波形が1クロックずらした場合の波形になります。
尚、同じ遅延量(20.283ns)にして、FRAM_SCLKを50MHzから25MHzにすると、周期越えを起こさないため、取り込みタイミングをずらす必要はなくなります。(以下の波形)
ちなみに、
FRAM_SCLKの周期を最大値(PRM_DIV=15又はREG_DIV=15)にすると、以下のような波形になります。
最後に
リソースの使用率及びSTAによるタイミング解析を行う必要があります。
この結果次第で、再設計など発生します。
本格的に検証を始める前に仮合成して確認しておく必要があります。
※シミュレーションした後に、FPGAに入らない(リソースをオーバーする)、タイミングが間に合わないなどがあれば、シミュレーションが無駄になるためです。
タイミングレポートのFalse Pathでは、遅延が大き過ぎないかを確認します。
特に、ラッチするタイミングまでに値が確定しているか、レジスタ値では、処理が始まるまでに確定しているかなど、遅延量を確認します。