nao-milkの経験ブログ

25年間の半導体エンジニア経験で知り得た内容を記載したブログです。

CRC処理をロジック化

f:id:nao-milk:20210430171442p:plain
CRCVerilogでLogic化しました。
通信系や制御系でCRCを設計していましたが、特定用途だったため、生成多項式も固定、bit幅も固定、右/左送りも固定だったため、汎用性の高いCRCを作成してみました。

1クロックで処理するため、半導体の設計では定番に近い構成になると思います。
汎用性が高いと言っても、検証は入力データ8bit、CRCは8,16,32で行い、合成/マッピングは、入力データ8bit、CRC16bitで行いました。

機能概要

Avalon-MM(Slave)でI/Fを行い、CRC(最大32bit)処理を行います。

機能仕様

  1. 入力データは最大32bit*1の入力が可能。
  2. CRC結果は最大32bit*2の出力が可能。
  3. 生成多項式レジスタで設定可能。
  4. CRCの初期値はレジスタで設定可能。
  5. CRC結果の反転が可能。
  6. 入力データのMSB/LSBのbit反転が設定可能。
  7. CRC結果のMSB/LSBのbit反転が可能。
  8. レジスタ(上記3~7のレジスタ)のリセット時の初期値は、parameterで設定可能。
  9. 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信号

f:id:nao-milk:20210502115204p:plain
IOタイミング (Avalon-MM I/F信号)

処理内容

入力データ(複数ビット)からCRC結果を1クロックで求めるため、一般的なCRC回路を並列化します。

以下に一般的なCRC回路例を示します。(1bit毎に処理を行う回路)
尚、例として使用する「一般的なCRC回路」の生成多項式は、以下の通りです。
f:id:nao-milk:20210502113428p:plain

f:id:nao-milk:20210502113253p:plain
一般的なCRC回路例

上記一般的なCRC回路を入力ビット毎に展開し、使用します。
以下が、上記回路を並列で処理した場合の回路構成となります。
尚、図中では、入力データ(in_dat[7:0])=0x82を入力することにより、CRC結果(res_crc[7:0])=0x87が得られます。

f:id:nao-milk:20210502113806p:plain
CRC回路

CRC結果=0x87が得られる計算式は以下の通りです。
※上記回路の演算方法と下記計算方法は少し違います。
 上記回路では生成多項式の"1"の時だけXORを行い、
 下記計算式は入力データ又はXOR結果の上位ビットが"1"の時だけXORを行います。

f:id:nao-milk:20210502114200p:plain
一般的な計算式

補足)
 回路構成の場合、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パターン

f:id:nao-milk:20210430140052p:plain
検証パターン(設定値と期待値)

検証結果

期待値と一致しました。

ModelSimでシミュレーションを行い、得られたCRC結果と期待値とを比較し、Log表示させたものを以下に示します。(抜粋したもの)
※Log中の「Error」が期待値との不一致数を示します。
 Log中では"0"なので、不一致数は0です。

f:id:nao-milk:20210430140353p:plain
シミュレーション結果(Log表示抜粋)

CRC32の時のシミュレーション波形は以下の通りです。
尚、入力データは連続で入力しています。

f:id:nao-milk:20210430142601p:plain
シミュレーション波形(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 VBACRC期待値作成

今後の用途で利用できるよう、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

確認

上記2つのVBAもネットで見つけたプログラムと一致するか確認しました。
Functionで作成したため、Excelのユーザ定義から関数を読み出し、表示させました。
以下が比較した結果となり、全てネットのものと一致しました。

f:id:nao-milk:20210430145136p:plain
VBAの比較確認

実装

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を追加しました。

f:id:nao-milk:20210430150215p:plain
Platform Designer

Quartusの実行

FPGAに実装し、結果は以下の通りです。
STA結果は、50MHzクロックでMetしました。
尚、前回のFRAMI/Fのみ結果は以下を参照ください。
nao-milk.hatenablog.com

Summary

f:id:nao-milk:20210430150801p:plain
Quartus Summary

Resorce

f:id:nao-milk:20210430151434p:plain
Resource Summary

Timing Report

STA結果を以下より示します。

Clock

50MHzクロック(CPU_CLK)及び200MHzクロック(SYS_CLK)共にMetしています。

f:id:nao-milk:20210430151812p:plain
Timing Report(Clock)
CRC回路内

モジュールNML_CRC_CORE内の入力データ(信号名:in_dat[7:0])を起点に、CRC演算結果確定までの遅延量を計測しました。
以下が結果です。

f:id:nao-milk:20210430152320p:plain
Timing Report(CRC演算内)

入力データ(8bit)が確定し、CRC結果が確定するまでの最大遅延が、「8.321ns」となり、50MHzクロック(20ns)の制約にMetします。

ソフトウェア

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

シミュレーション波形

実行時のシミュレーション波形は以下の通りです。

f:id:nao-milk:20210430162145p:plain
波形(協調シミュレーション)

シミュレーション結果

結果は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の表示内容】

f:id:nao-milk:20210430162718p:plain
ModelSim Log表示内容

最後に

前回の「FRAMにアクセス」で、データのSave/Loadでデータの妥当性を検査するため、CRCを入れるのが定石かと思い、CRCのモジュールを追加しました。
尚、上記説明の回路は、シリアルで処理するCRC回路を並列化しただけとなります。
※生成多項式で"1"とあるbitだけXORする回路です。あとは論理合成任せです。

ソフトウェアでCRC演算すると処理時間やメモリ容量などが気になりますが、Logicにしてしまえば「CRCモジュールに入力データを入れるだけ。。。」となります。

FPGAの便利な所は、ソフトウェア処理とハードウェア処理を自由に作れる所だと思っています。

*1:入力bit幅はparameterで設定

*2:CRCのbit幅はparameterで設定

変数を別メモリに配置

f:id:nao-milk:20210426164612p:plain

特定の変数をプログラムメモリ(CPUが使用するメモリ)とは別のメモリに配置する場合について、説明します。

尚、Intel社製 NiosIIの場合になります。

 

以下の記事では設定手順などの説明がないと思いますので、記載しておきます。

(ネット上にもあまり無いかと思います) 

nao-milk.hatenablog.com

 

 

大まかな設定手順は、

  1. BSP EditorのLinkerでセクションを作る。
  2. 変数とセクションを関連付ける。

です。

 

BSP EditorのLinkerでセクションを作る

1、Project Explorerの????_bspをクリックし、[Nios II] → [BSP Editor]を選択します。

f:id:nao-milk:20210426154100p:plain

BSP Editorの起動

2、Linker Scriptタブを選択します。

  ここでセクションを作成します。

f:id:nao-milk:20210426154438p:plain

Linker Scriptタブ

3、「Linker Memory Regions」の"Add"ボタンをクリックし、メモリとサイズとOffsetアドレスを指定し、"Linker Region Name"を作成します。

  以下のような感じで、配置させたい変数分作ります。

f:id:nao-milk:20210426160419p:plain

f:id:nao-milk:20210426160512p:plain

f:id:nao-milk:20210426160609p:plain

f:id:nao-milk:20210426160703p:plain

f:id:nao-milk:20210426160755p:plain

 

   必要な領域を指定するとPARAMETERが設定されており、アドレスが重複しているために赤文字でメッセージが表示されます。

  "Linker Region Name"の「PARAMETER」選択し、"Remove"ボタンで削除します。

  以下が削除後の表示内容になります。

f:id:nao-milk:20210426161002p:plain

Linker Memory Regions設定

4、「Linker Section Mappings」の"Add"ボタンをクリックし、"Linker Region Name"と"Linker Section Name"を関連付けします。

f:id:nao-milk:20210426161336p:plain

関連付け

  必要な領域分設定します。

f:id:nao-milk:20210426161715p:plain

Linker Section Mappings設定

  "Generate"ボタンをクリックし、終了します。

 

変数とセクションを関連付ける。

 ソースコード内(.cファイル)で「__attribute__」を使って、sectionと変数を関連付けます。

f:id:nao-milk:20210426162423p:plain

ソースコード

 

確認方法

プロジェクトをコンパイルすると「???.map」が出来上がり、変数とセクションが関連付けられたことを確認できます。

f:id:nao-milk:20210426162956p:plain

Map確認

 最後に

上記.mapで配置を確認できますが、Niosを含めたシミュレーションでも確認しています。

以下の記事をご参照ください。

 

以下はNiosソースコードを記載しています。

nao-milk.hatenablog.com

 

以下はシミュレーション波形を載せています。

nao-milk.hatenablog.com

 

FRAMにアクセス (協調シミュレーション編)

f:id:nao-milk:20210426133742p:plain

NIOSを含めたシミュレーションの実行になります。

 

シミュレーション環境の構築方法とNiosのシミュレーション用コードの生成方法、及びシミュレーション波形を添付します。

 

 

NIOSプログラムやテストベンチは、前回の記事をご参照ください。

nao-milk.hatenablog.com

 

シミュレーション環境構築

シミュレーション環境を構築しますが、必要なファイルなどのリストを作成するのが面倒なので、Platform Designer で環境を作成し、生成されたスクリプトファイルを改造して使用します。

 

実行スクリプトファイル生成

Quartusを起動し、「Platform Designer」を立ち上げて作成したQsysファイルを読み込みます。

その後、[Generate]→[Generate Tesetbench System]を選択します。

f:id:nao-milk:20210426100651p:plain

Generation Testbench System

「Generation」Windowが表示され、"Generate"ボタンをクリックします。

以下の選択内容で十分です。(生成されたテストベンチは使用しないので。。。)

f:id:nao-milk:20210426100857p:plain

Generation

"Generate"ボタンをクリックすると、Quartus実行フォルダに以下のフォルダが出来上がります。

フォルダ

 <Platform Designerで作成したモジュール名>/testbench/mentor

 

このフォルダに実行スクリプトファイル(ファイル名:msim_setup.tcl)が出来上がります。

このスクリプトファイルは、「Platform Designerで作成したモジュール」をトップとしたシミュレーション環境になるため、FPGA TOPからシミュレーションする場合は、改造が必要になります。

尚、Platform Designerで作成した必要なIPのリストはあるので、あとは、ユーザの作成したソースを追加するだけとなります。

 

スクリプトファイルの修正

msim_setup.tclの修正内容を説明します。

1、NIOSプログラムファイルのコピー元を変更

  Nios II - Eclipseコンパイルしたデータのコピー元を変更します。

f:id:nao-milk:20210426103124p:plain

コピー元の変更

2、テストベンチのコメントアウト

  Platform Designerが生成したテストベンチをコメントアウトします。

f:id:nao-milk:20210426103602p:plain

テストベンチのコメントアウト

3、ユーザー回路の追加

  ユーザー回路を追加し、テストベンチや必要なファイルを追加します。

 私の場合、alias comとは別に、新しいaliasを作成して追加します。

f:id:nao-milk:20210426104033p:plain

  尚、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」を変更するともっと速くなります。

以下は、私の設定です。

f:id:nao-milk:20210426105634p:plain

f:id:nao-milk:20210426105731p:plain

 

コード生成

上記設定を行った後、再度コンパイルを行います。

その後、[Make Targets]→[Build]を選択し、「mem_init_generate」を選択して、"Build"ボタンをクリックします。

f:id:nao-milk:20210426110124p:plain

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]から実行する。

  

f:id:nao-milk:20210426111619p:plain

Tcl実行

  "Transcript"から実行する。

f:id:nao-milk:20210426111904p:plain

doで実行

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を実行しているためです。)

f:id:nao-milk:20210426114552p:plain

全体波形

マスタモード Load

リセット解除後、マスタモードによるロード動作になります。

入力信号MST_SLVとPRM_???はテストベンチからforceで値を入力し、動作させています。(ブート制御回路を作成するのが面倒だったので、外部から動かしました。)

ここでは、FRAMのDevice ID、Serial No、特殊セクタ、通常メモリをメモリPARAMETERに保存しています。

f:id:nao-milk:20210426115405p:plain

マスタモード Load

ステータスリード

FRAMのステータスレジスタをリードし、メモリPARAMETERの指定した番地へ保存し、リードしていることが確認できます。(変数FramSTは、メモリPARAMETERの0x920番地に関連付け)

尚、データバス幅は32bitとなるため、アドレスは「÷4」した値になります。

f:id:nao-milk:20210426120442p:plain

ステータスリード

Device ID及びSerial No確認

メモリPARAMETERの指定した番地をリードしています。

f:id:nao-milk:20210426120739p:plain

Device ID確認

f:id:nao-milk:20210426120858p:plain

Serial No確認

データ書き換え

メモリPARAMETERの指定した番地をリードし、リードしたデータを加工し書き戻しています。

f:id:nao-milk:20210426121848p:plain

データ書き換え

尚、FramSS,FramDTも書き換えています。

メモリPARAMETERからFRAM I/Fへ転送

メモリPARAMETERの指定した番地をリードし、FRAM I/Fへライトしています。

f:id:nao-milk:20210426123409p:plain

FRAM I/Fへ転送

FRAM I/FからメモリPARAMETERへ転送

FRAM I/Fからデータをリードし、メモリPARAMETERへ保存しています。

メモリPARAMETERの保存は、FramTMとなります。

f:id:nao-milk:20210426123741p:plain

FRAM I/Fから転送

割り込み動作

FRAMへのアクセスが完了し、割り込み(irq[0])が発生。

その後、割り込みクリアして割り込みが解除されます。

f:id:nao-milk:20210426124412p:plain

割り込み動作

比較結果

Niosソース及びテストベンチでの比較結果は、動作が終了するとerror数を表示します。

以下が結果表示内容です。

f:id:nao-milk:20210426124738p:plain

比較結果

比較結果のerror数が0を表示したため、データエラーは無しになります。

 

最後に

実機があれば、SignalTapでFPGAの内部波形を確認してデバッグが可能ですが、上記のように接続関係や基本動作を確認する上では、シミュレーションが速いと思います。

SignalTapでは、保存する信号と保存期間はRAMの空き容量に依存し、見たい所が見れない場合もあります。

また、クロックが何系統もあると、保存クロックに悩んでしまいます。

 

今回、「__attribute__」を使って別メモリに変数を関連付けました。

それが思い通りになっているかも、波形で確認ができます。

 

「FRAMにアクセス」をテーマに、半導体(FPGAですが)を実機で動かす手前までの工程をざっくりですがブログに記載しました。

ここまで2回のシミュレーションを行い、動作確認をしています。

FRAM I/Fブロックのシミュレーションでは、ブロック自体にバグが無いかの確認のため。

FPGA TOPからのシミュレーションでは、各ブロックの接続関係と矛盾点が無いかの確認のため。

このあとは、基板上のデバイス間接続を確認するため、Niosコードをそれ用に変更していきます。

 

もし上記シミュレーション無しで、基板で確認すると、どこが悪いのかの切り分けができず、余計に時間がかかります。

Niosを含めたシミュレーション環境構築は非常に簡単です。

もし実機で動かない場合は、シミュレーションで確認してみるのも良いかもしれません。

FRAMにアクセス (Niosコーディング編)

f:id:nao-milk:20210425180306p:plain

Quartusを実行し、リソースとタイミングもOKだったので、次はNiosのプログラムコーディングとシミュレーションについてになります。
NiosはC言語で記述し、シミュレーションはFPGA トップからの行います。

ここでは、シミュレーションを実行する前のNiosのプログラム作成とテストベンチ記述を記載しています。
トップ構成は、以下をご参照ください。
nao-milk.hatenablog.com

※Quartusを実行した時のリソース使用状況とタイミングに関しては、以下をご参照ください。
nao-milk.hatenablog.com

シミュレーション内容

目的

接続が正しく行われるかを確認するため、Niosを含めたシミュレーションを行います。

経路

接続が正しく行われているか各経路を確認します。
経路は以下の通りになります。

f:id:nao-milk:20210425152624p:plain
データの流れ

①マスタモード時、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の設定内容は、以下通りです。

f:id:nao-milk:20210425170218p:plain
Linker設定
メモリマップ

メモリPARAMETERのアドレスマップは以下の通りです。

f:id:nao-milk:20210425163622p:plain
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実行編)

f:id:nao-milk:20210421105607p:plain

Quartusの論理合成とインプリメントとなります。

ここでは、単に実行するだけになるため、実行結果とSTA結果のみ記載します。

また、FRAM I/FブロックとFRAMデバイス間に遅延を付けて行ったシミュレーション波形も載せています。

 

TOP構成は、以下をご参照ください。

nao-milk.hatenablog.com

 

 

実行結果

f:id:nao-milk:20210425120556p:plain

Summary

リソース

f:id:nao-milk:20210425121005p:plain

Resource Summary

 Input Pin

クロックとリセット以外の入力端子は、「Input Register」を使用していることを確認します。これにより、端子とFF間の配線遅延を小さくすることにより、Setup/Holdをバランスよくします。

f:id:nao-milk:20210425121620p:plain

Input Pin

以下に入力端子FRAM_MISOとFF(in_miso)の接続関係を示します。

f:id:nao-milk:20210425121930p:plain

FRAM_MISO構成

 Output Pin

 出力端子LED_LOCKを除く信号が「Output Register」を使用していることを確認します。

f:id:nao-milk:20210425122620p:plain

Output Pin

 階層構造とメモリ使用状況

f:id:nao-milk:20210425123453p:plain

階層構造とメモリ使用状況

ブロックメモリ(M10K)の使用率は以下の通りです。

f:id:nao-milk:20210425124235p:plain

 

STA結果

クロック制約

f:id:nao-milk:20210425124518p:plain

最高動作周波数

CPU_CLKは50MHz、SYS_CLKは200MHz以上の結果が得られていることが確認できます。

f:id:nao-milk:20210425124615p:plain

 

タイミングレポート

各タイミングレポートを確認します。

 

CPU_CLK(50MHz) 間遅延

FF間が20nsの制約に対し、10ns以上の余裕があることが分かります。

f:id:nao-milk:20210425125058p:plain

SYS_CLK(200MHz)間遅延

FF間が5nsの制約に対し、0.2ns以上の余裕があることが分かります。

f:id:nao-milk:20210425125345p:plain

 

False Path (CPU_CLK → SYS_CLK)

f:id:nao-milk:20210425125715p:plain

False Path (SYS_CLK → CPU_CLK)

f:id:nao-milk:20210425125835p:plain

False Path (CPU_CLK間)

f:id:nao-milk:20210425130133p:plain

False Path (SYS_CLK間)

f:id:nao-milk:20210425130331p:plain

 

AC特性

Report Timingを実行し、FRAM制御信号の遅延時間を測定し、AC特性をまとめました。

f:id:nao-milk:20210425131538p:plain

AC特性

上記AC特性からFRAMデバイスのAC特性に当てはめると以下となり、各Setup/Holdをカバーすることが分かります。

f:id:nao-milk:20210425131836p:plain


f:id:nao-milk:20210421011721p:plain

※tdrMISOの遅延時間 と 基板配線遅延 の 合計がFRAM_SCLKの周期を超えた場合は、基準取り込みタイミングを遅延させる調整が必要になります。(REG_TAK又はPRM_TAK)

 

f:id:nao-milk:20210421011544p:plain

 シミュレーション

FRAM I/FブロックとFRAMデバイス間の信号に遅延を与えてシミュレーションを行いました。

各信号に上表(AC特性)の遅延値(tdo????,tdi????)を与えてRTLシミュレーションを行いました。

以下が波形となります。

※起点となるFRAM_SCLK↓ から in_misoまでの遅延が18.283nsとなります。

※遅延値の与え方は、前回のテストベンチ内のコメント「FRAM モデル間遅延」で調整できるようになっています。

f:id:nao-milk:20210421100620p:plain

 

遅延量18.283に+2nsして20.283nsにした場合の波形を以下に示します。

FRAM_SCLK周期を超えたため、1bitズレたパラレルデータになります。

f:id:nao-milk:20210421101703p:plain

 

20.283nsの遅延で取り込みタイミングをPRM_TAK又はREG_TAKで1クロックずらすことにより、周期越えした場合でも取り込むことができます。以下の波形が1クロックずらした場合の波形になります。

f:id:nao-milk:20210421102132p:plain

尚、同じ遅延量(20.283ns)にして、FRAM_SCLKを50MHzから25MHzにすると、周期越えを起こさないため、取り込みタイミングをずらす必要はなくなります。(以下の波形)

f:id:nao-milk:20210421102738p:plain

 

ちなみに、

FRAM_SCLKの周期を最大値(PRM_DIV=15又はREG_DIV=15)にすると、以下のような波形になります。

f:id:nao-milk:20210421103311p:plain

 

最後に

リソースの使用率及びSTAによるタイミング解析を行う必要があります。

この結果次第で、再設計など発生します。

本格的に検証を始める前に仮合成して確認しておく必要があります。

※シミュレーションした後に、FPGAに入らない(リソースをオーバーする)、タイミングが間に合わないなどがあれば、シミュレーションが無駄になるためです。

 

タイミングレポートのFalse Pathでは、遅延が大き過ぎないかを確認します。

特に、ラッチするタイミングまでに値が確定しているか、レジスタ値では、処理が始まるまでに確定しているかなど、遅延量を確認します。