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で設定