CRCの生成回路

[] [] []


CRCの生成回路を素直に考えると、メッセージをシフトインしていってMSB=1のときg[n:0]を引くという除算回路になる。

 

しかし本に書いてあるCRCの生成回路はこれとは少し異なっている。なぜこの回路でCRCが計算できるのだろうか

 

例で確かめてみると、この回路でも途中は異なるものの最終結果はあっているし、素朴な除算回路では最初のビットがd[n-1]に到達するまでnクロック余計にかかったのに対して、この回路では余計なクロックが必要ない。

) g[3:0]=1101, msg[4:0]=10011

素朴な除算回路

本に書いてある回路

d[2:0]←{msg[4:0],000}

_-----____------------------

_000____10011000

_001____0011000

_010____011000

_100____11000

_100____1000

_100____000

_101____00

_111____0

_011

d[2:0]←msg[4:0]

_-----____-------------

_000____10011

_101____0011

_111____011

_011____11

_011____1

_011

◇◇

一般の本では除算回路にxnの乗算を組み合わせた回路と説明しているが、次のように「あるメッセージのCRCが既に計算されていて、このメッセージに1ビットを付け加えたときのCRCを計算する」と考えた方がわかりやすい。

今、あるメッセージmsg[ ]CRCcrc[n-1:0]とおくと

  crc[n-1:0] = {msg[ ],0…0} mod g[n:0]  (0…0nビットのゼロ)

このメッセージに1ビットdinを付け加えた{msg[ ],din}CRC

  {msg[ ],din,0…0} mod g[n:0]

  = {msg[ ],0,0…0} mod g[n:0] + {din,0…0} mod g[n:0]

  = {crc[n-1:0],0} mod g[n:0] + {din,0…0} mod g[n:0]

  = {crc[n-2:0],0} + (crc[n-1])? g[n-1:0]:0 + (din)? g[n-1:0]:0  (最上位ビットg[n]は計算不要)

  = {crc[n-2:0],0} + (crc[n-1]+din)? g[n-1:0]:0

と上記の回路になる。ここで + GF(2n)の加算(XOR)である。

初期値は、オールゼロのmsg[ ] に対応するCRC=0…0から始めればよい。

◇◇◇

実際に通信規格で使われているCRCは定義どおりではなく次のように加工されているようである。

項目

定義どおり

加工あり

レジスタ初期値

0・・・0

1・・・1

シフト入力順

バイト毎にMSBから

バイト毎にLSBから

結果の左右ビット反転

そのままcrc[n-1:0]

左右ビット反転crc[0:n-1]

結果の01反転

そのままcrc

01反転 ~crc

このような加工をして誤り検出に影響しないのか気になるが、ビット順の並べ替えや01反転は明らかに影響なさそうであり、

レジスタ初期値はその時点でのCRC値であるから、そのCRC値に対応するメッセージが本来のメッセージの前に付け加わったのと等価であり、この付加メッセージを除いて考えればやはり影響ない。

なお、初期値をゼロ以外にする理由は、オールゼロのメッセージのCRCはオールゼロなので、シリアル通信で0固定の故障が検出されないのを防ぐためと思われる。オール1のメッセージのCRCはオール1ではないので1固定の故障は検出される。

 

(1) HDLC

・生成多項式 CRC-16-CCITT g(x) = x16+x12+x5+1 g[16:0]=11021

・初期値 crc[15:0]=ffff

・シフト入力順はバイト毎にLSBから

・結果を左右ビット反転する

・結果を01反転する

例で計算してみると、メッセージにCRCを付加したコードのCRC~crc[0:15]=0f47になる。

メッセージ

CRC

msg[7:0] ="A"=41

~crc[0:15]=a3f5

msg[23:0]={"A",f5a3}=41f5a3

~crc[0:15]=0f47

msg[71:0]="123456789"

      =313233343536373839

~crc[0:15]=906e

msg[87:0]={"123456789",6e90}

      =3132333435363738396e90

~crc[0:15]=0f47

ちなみに定義どおりだと、メッセージにCRCを付加したコードのCRCはゼロになる。

メッセージ

CRC

msg[7:0] ="A"=41

crc[15:0]=58e5

msg[23:0]={"A",58e5}=4158e5

crc[15:0]=0000

msg[71:0]="123456789"

      =313233343536373839

crc[15:0]=31c3

msg[87:0]={"123456789",31c3}

      =31323334353637383931c3

crc[15:0]=0000

 

(2) IEEE802.3 Ethernet

・生成多項式 CRC-32 g(x) = x32+x26+x23+x22+x16+x12+x11+x10+x8+x7+x5+x4+x2+x+1  g[32:0]=104c11db7

・初期値 crc[31:0]=ffffffff

・シフト入力順はバイト毎にLSBから

・結果を左右ビット反転する

・結果を01反転する

例で計算してみると、メッセージにCRCを付加したコードのCRC~crc[0:31]=2144df1cになる。

メッセージ

CRC

msg[7:0] ="A"=41

~crc[0:31]=d3d99e8b

msg[39:0]={"A",8b9e}=418b9ed9d3

~crc[0:31]=2144df1c

msg[71:0]="123456789"

      =313233343536373839

~crc[0:31]=cbf43926

msg[103:0]={"123456789",2639f4cb}

      =3132333435363738392639f4cb

~crc[0:31]=2144df1c

 

計算に使ったCコードは次のとおりで、VivadoHLSの固定小数点テンプレートap_int.hを使っている。

/* crc.cpp */

#include <ap_int.h>

using namespace std;

const int N = 16;           // CRCビット幅

typedef ap_uint<N> crc_t;   // CRCデータ型

const crc_t INIT = 0xffff;  // CRC初期値

const crc_t POLY = 0x1021;  // x16+x12+x5+x0 (X16は省略)

const bool LSB_FIRST = true;       // バイト毎にLSB firstで入力

typedef ap_uint<8> dat_t;   // バイト型

const int L = 20;           // 入力する最大バイト数

/**********************************************************

 * サブルーチン - バイト毎に入力してCRC計算する

 *********************************************************/

int calc_crc

    (crc_t *crc       // (o) CRC

    ,dat_t dat[L]     // (i) 入力バイト

    ,int len          // (i) 入力バイト数

    ) {

    crc_t crc_tmp = INIT;

    bool din;

    for(int k=0; k<len; k++) {

        for(int j=0; j<=7; j++) {

            din = (LSB_FIRST)? dat[k][j] : dat[k][7-j];

            if(crc_tmp[N-1] ^ din)

                crc_tmp = (crc_tmp << 1) ^ POLY;

            else

                crc_tmp = (crc_tmp << 1);

        }

        *crc = crc_tmp;

    }

}

/**********************************************************

 * サブルーチン - CRC計算結果を表示する

 *********************************************************/

int disp_crc

    (crc_t crc

    ,dat_t dat[L]

    ,int len

    ) {

    crc_t crc_rev;

    crc_t crc_inv;

    crc_t crc_rev_inv;

    for(int k=0; k<len; k++)

            printf("-- dat[%0d] = %02x\n",k,(int)dat[k]);

    printf("--  crc = %x\n",(int) crc);

    crc_inv = ~crc;

    printf("-- ~crc = %x\n",(int) crc_inv);

    for(int j=0; j<=N-1; j++) crc_rev[j] = crc[N-1-j];

    printf("--  crc = %x bit reversed\n",(int) crc_rev);

    crc_rev_inv = ~crc_rev;

    printf("-- ~crc = %x bit reversed\n",(int) crc_rev_inv);

    printf("\n");

}

/**********************************************************

 * メインルーチン - バイト列dat[len]についてCRCを求める

 **********************************************************/

int main(int argc, char *argv[]) {

    dat_t dat[L];

    crc_t crc;

    int   len;

   

    len = 1;

    dat[0] ='A';

    calc_crc(&crc,dat,len);

    disp_crc( crc,dat,len);

   

    len = 3;

    dat[0] ='A';

    dat[1] =0xf5;

    dat[2] =0xa3;

    calc_crc(&crc,dat,len);

    disp_crc( crc,dat,len);

   

    len = 9;

    sscanf("123456789","%s",dat);

    calc_crc(&crc,dat,len);

    disp_crc( crc,dat,len);

   

    len = 11;

    sscanf("123456789","%s",dat);

    dat[9] =0x6e;

    dat[10]=0x90;

    calc_crc(&crc,dat,len);

    disp_crc( crc,dat,len);

   

    return 0;

}


[] [] []