分享

CRC(查表)-表的由来

 skyyks 2011-06-25

1)硬件电路组成

    a) x^16 + x^12 + x^5 + 1
     ┌───────────┬─────────────────┬─────────────┐
     ↑  ┌─┬─┬─┬─┐  ↓  ┌─┬─┬─┬─┬─┬─┬─┐  ↓  ┌─┬─┬─┬─┬─┐  │
     
←│15│14│13│12│←←│11│10│09│08│07│06│05│←←│04│03│02│01│00│←┘
     ↑  └─┴─┴─┴─┘      └─┴─┴─┴─┴─┴─┴─┘      └─┴─┴─┴─┴─┘
     in

    b) x^8 + x^2 + x + 1
     ┌───────────────┬─────┬─────┐
     ↑  ┌─┬─┬─┬─┬─┬─┐  ↓  ┌─┐  ↓  ┌─┐  │
     
←│07│06│05│04│03│02│←←│01│←←│00│←┘
     ↑  └─┴─┴─┴─┴─┴─┘      └─┘      └─┘
     in

2) 
简单算法模型(bit计算)
    
照以上的硬件电路来看,其工作原理就是:
    
如果原来CRC的最高位异或输入1的话,那么絇OST并且异或生成多项式(a0x1021,b0x7).
如果原来CRC的最高位异或输入是高位异或输入是0的话,那么结果就是CRC左移一位.

    
那么可以得到以下的程序(以图a)
    U16 crc_cal(bit * in, U32 cnt)
    {
        U16 crc = 0;
        while(cnt--)
        {
            bool tmp = (crc >> 15) ^ *in;
            crc <<= 1;
            if(tmp)
                crc ^= 0x1021;
            in ++;
        }
        return crc;
    }


3) 
查表(按字节计算)
    
很显然,按比特计算的方法,其效率是低下的.
    
下面介绍查表方法(按字节计算).

    
要知道为什么可以用查表的方法,需要一些预备知识.

    
以图b为例,假设当前的CRC值是1011 1001,现在要输入4比特数据1101,其生成多项式是:0000 0111

    CRC = 1011 1001, in=1101 , G(X)= 0000 0111


                        <
计算方法1>                              <计算方法2>
            step:   1011 1001                                   0110 1001

            1:       011 1001 0                                  110 1001 0

            2:        11 1001 00                                  10 1001 00
                    ^ 00 0001 11                                 ^00 0001 11     <1>
                   --------------                               --------------
                      11 1000 11                                  10 1000 11

            3:         1 1000 110                                  0 1000 110
                    ^  0 0000 111                                 ^0 0000 111    <2>
                   ----------------                              -------------
                       1 1000 001                                  0 1000 001

            4:           1000 0010                                   1000 0010


    <
计算方法1>是硬件电路的完全模拟算法
    step 1: 
crc左移一位,因为crc的最高位是1,输入也是1,所以不做处理.
    step 2: 
crc左移一位,因为crc的最高位是0,输入是1,所以还需要异或G(X).
    ....
    step 4: 
crc左移一位,因为crc的最高位是0,输入也是0,所以不做处理.
    
得到最终的结果 crc = 1000 0010

    
实际上,crc左移以后,是否还要异或生成多项式的条件是: crc的最高位和输入位异或后的值.
    
那么是否可以预先将CRC(h)的值与要输入的4比特数据异或,作为是否判断条件呢.

    
答案是肯定的. CRC = 1011 1001, in=1101, CRC(h)^in = 0110
    
其计算过程见<计算方法2>
    step 1: 
CRC左移一位,因为CRC的最高位是0,所以不做处理.
    step 2: 
CRC左移一位,因为CRC的最高位是1,所以还需要异或G(X).
    ....
    step 4: 
CRC左移一位,因为CRC的最高位是0,所以不做处理.
    
得到最终的结果 CRC = 1000 0010


    
虽然,上面的结果是一样的,可有证据证明无论什么情况下,结果都是对的?
    
静下来想想,你就是知道这2个方法确实能得出相同的结果.
    
    
4比特的输入完成之后,整个CRC值左移了4,原来的CRC(h)只是作为判断异或生成多项式的条件存在过.
    
最终的CRC值完全是G(X)CRC(l)不停地(异或/移位)的结果.
    
虽然,CRC计算的过程中,CRC不停的在变化着,:
       1. 
如果在<方法1>,由于CRC的最高位和输入异或后的结果等于0,那么CRC只是左移一位.
          
显然2个方法是一样的.
       2. 
如果在<方法1>,由于CRC的最高位和输入异或后的结果等于1,那么CRC左移一位后,还需要异或G(X).
          
异或G(X)的过程中,可能使CRC的后续某位产生变化(也可能不变化,视生成多项式的值而定).
           a.
如果没发生变化,那当这位最后移到最高位,作为判断条件的时候,仍然是以前的这个值和输入位的异或.
             
显然2个计算方法是一样的.
           b.
如果变化过那当这位最后移到最高位,作为判断条件的时候,是变化过后的值和输入位的异或
             
但如果<方法1>能引起后续某位的变化,<方法2>同样也会引起同一位的变化.
             
这样当这位最后移到最高位,作为判断条件的时候,2种方法的判断条件仍然是一致的.

    * 
关于这部分,我描述得不怎么清楚,那是因为我小时候地语文基础没打好,:).
      
如果你有更好地描述,请告诉我.
      

    
好了,预备知识完毕,现在开始探讨那个查找表是怎么来的.
    
请看<方法2>,最终的CRC的结果是: (CRC(l) << 4) ^ <1> ^ <2>
    
          
               <
计算方法2> 
               
            CRC = 1011 1001, in=1101 , G(X)= 0000 0111
               |-----------------------> CRC(h)^in =  1011 ^ 1101 = 0110 
               |
               |    |------------------> CRC(l)
              ---- ----                    
              0110 1001     
                        
               110 1001 0           
                                    
                10 1001 00          
               ^00 0001 11     <1>  
              --------------        
                10 1000 11          
                                    
                 0 1000 110         
                ^0 0000 111    <2>  
               -------------        
                 0 1000 001         
                                    
                   1000 0010        
    
由于异或的可结合律,其结果等同于: (CRC(l) << 4) ^ ( <1> ^ <2> )  
    
这说明, ( <1) ^ <2> )可以预先制作成表格,采用查表的方法计算CRC, 表的索引是 CRC(h) ^ in .
    
其结果是: ( CRC(l) << 4) ^ table[ CRC(h) ^ in ].
    
因为是4比特,表的大小是16.
    
表的内容可以根据G(X),预先计算.
    
    
这里举例用的4比特,基于字节的方法可以用同样的方法.
    
    
那么现在开始编程了.
    
    U16 crc_tab[256]= {...};
    U16 crc_cal(U8 * ptr, U32 cnt)
    {
        U16 crc = 0;
        U8  da;
        while (cnt--)
        {
            da = crc >> 8;  // CRC(h)
            crc <<= 8;
            crc ^= crc_tab[da ^ *ptr++];
        }
        
        return crc;
    }
    
    
既然你已经知道了查表的原理,那么编一个计算表值的程序不成问题了.
    
    #define GX 0x1021
    void CCrcDlg::OnButton1() 
    {
        WORD table[256];
    
        for(int i =0; i<256; i++)
        {
            WORD crc = i << 8;
            for(int n=0; n<8; n++)
            {
                bool tmp = crc & (1<<15) ? true : false;
                crc <<= 1;
                if(tmp)
                    crc ^= GX;
            }
            table  = crc;
        }
    }
    
    
那么你得到了这么个表:
    U16 table[256]=
    {
        0X0000, 0X1021, 0X2042, 0X3063, 0X4084, 0X50A5, 0X60C6, 0X70E7, 
        0X8108, 0X9129, 0XA14A, 0XB16B, 0XC18C, 0XD1AD, 0XE1CE, 0XF1EF, 
        0X1231, 0X0210, 0X3273, 0X2252, 0X52B5, 0X4294, 0X72F7, 0X62D6, 
        0X9339, 0X8318, 0XB37B, 0XA35A, 0XD3BD, 0XC39C, 0XF3FF, 0XE3DE, 
        0X2462, 0X3443, 0X0420, 0X1401, 0X64E6, 0X74C7, 0X44A4, 0X5485, 
        0XA56A, 0XB54B, 0X8528, 0X9509, 0XE5EE, 0XF5CF, 0XC5AC, 0XD58D, 
        0X3653, 0X2672, 0X1611, 0X0630, 0X76D7, 0X66F6, 0X5695, 0X46B4, 
        0XB75B, 0XA77A, 0X9719, 0X8738, 0XF7DF, 0XE7FE, 0XD79D, 0XC7BC, 
        0X48C4, 0X58E5, 0X6886, 0X78A7, 0X0840, 0X1861, 0X2802, 0X3823, 
        0XC9CC, 0XD9ED, 0XE98E, 0XF9AF, 0X8948, 0X9969, 0XA90A, 0XB92B, 
        0X5AF5, 0X4AD4, 0X7AB7, 0X6A96, 0X1A71, 0X0A50, 0X3A33, 0X2A12, 
        0XDBFD, 0XCBDC, 0XFBBF, 0XEB9E, 0X9B79, 0X8B58, 0XBB3B, 0XAB1A, 
        0X6CA6, 0X7C87, 0X4CE4, 0X5CC5, 0X2C22, 0X3C03, 0X0C60, 0X1C41, 
        0XEDAE, 0XFD8F, 0XCDEC, 0XDDCD, 0XAD2A, 0XBD0B, 0X8D68, 0X9D49, 
        0X7E97, 0X6EB6, 0X5ED5, 0X4EF4, 0X3E13, 0X2E32, 0X1E51, 0X0E70, 
        0XFF9F, 0XEFBE, 0XDFDD, 0XCFFC, 0XBF1B, 0XAF3A, 0X9F59, 0X8F78, 
        0X9188, 0X81A9, 0XB1CA, 0XA1EB, 0XD10C, 0XC12D, 0XF14E, 0XE16F, 
        0X1080, 0X00A1, 0X30C2, 0X20E3, 0X5004, 0X4025, 0X7046, 0X6067, 
        0X83B9, 0X9398, 0XA3FB, 0XB3DA, 0XC33D, 0XD31C, 0XE37F, 0XF35E, 
        0X02B1, 0X1290, 0X22F3, 0X32D2, 0X4235, 0X5214, 0X6277, 0X7256, 
        0XB5EA, 0XA5CB, 0X95A8, 0X8589, 0XF56E, 0XE54F, 0XD52C, 0XC50D, 
        0X34E2, 0X24C3, 0X14A0, 0X0481, 0X7466, 0X6447, 0X5424, 0X4405, 
        0XA7DB, 0XB7FA, 0X8799, 0X97B8, 0XE75F, 0XF77E, 0XC71D, 0XD73C, 
        0X26D3, 0X36F2, 0X0691, 0X16B0, 0X6657, 0X7676, 0X4615, 0X5634, 
        0XD94C, 0XC96D, 0XF90E, 0XE92F, 0X99C8, 0X89E9, 0XB98A, 0XA9AB, 
        0X5844, 0X4865, 0X7806, 0X6827, 0X18C0, 0X08E1, 0X3882, 0X28A3, 
        0XCB7D, 0XDB5C, 0XEB3F, 0XFB1E, 0X8BF9, 0X9BD8, 0XABBB, 0XBB9A, 
        0X4A75, 0X5A54, 0X6A37, 0X7A16, 0X0AF1, 0X1AD0, 0X2AB3, 0X3A92, 
        0XFD2E, 0XED0F, 0XDD6C, 0XCD4D, 0XBDAA, 0XAD8B, 0X9DE8, 0X8DC9, 
        0X7C26, 0X6C07, 0X5C64, 0X4C45, 0X3CA2, 0X2C83, 0X1CE0, 0X0CC1, 
        0XEF1F, 0XFF3E, 0XCF5D, 0XDF7C, 0XAF9B, 0XBFBA, 0X8FD9, 0X9FF8, 
        0X6E17, 0X7E36, 0X4E55, 0X5E74, 0X2E93, 0X3EB2, 0X0ED1, 0X1EF0 
    }; 
        
---------------------------------- END ------------------------------------

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多