CRC校验原理

原文:https://blog.csdn.net/qq_26652069/article/details/100578942

线性分组码中有一种重要的码称为循环码(Cyclic code),这种码编码和解码都不太复杂,而且检(纠)错能力较强。循环码除了具有线性分组码的一般性质外,还具有循环性。循环性是指任一码组循环一位以后(即将最右端的一个码元移至左端,或反之)以后,仍然为该码中的一个码组

什么是CRC校验?

CRC即循环冗余校验码:

  • 是数据通信领域中最常用的一种查错校验码;
  • 其特征是信息字段校验字段的长度可以任意选定。

循环冗余检查(CRC, Cyclic Redundancy Check)是一种数据传输检错功能,对数据进行多项式计算(利用多项式,对数据进行模2除法),并将得到的结果附在帧的后面传输,接收设备接收后也执行类似的算法(依旧利用同一个多项式,对接收的数据进行模2除法,但是这里要求没有余数才算是正确传输),以保证数据传输的正确性和完整性。CRC利用除法及余数的原理,实现错误侦测的功能,具有原理清晰、实现简单等优点。

相比于奇偶校验(PCC)只能校验一位错误,循环冗余校验码的检错能力要更强,能够检出多位错误。

CRC校验原理

      其根本思想就是先在要发送的帧后面附加一个数(即用来校验的校验码,但要注意,这里的数也是二进制序列的,下同),生成一个新帧发送给接收端。当然,这个附加的数不是随意的,它要使所生成的新帧能与发送端和接收端共同选定的某个特定数整除(注意,这里不是直接采用二进制除法,而是采用一种称之为“模2除法”)。到达接收端后,再把接收到的新帧除以(同样采用“模2除法”)这个选定的除数。因为在发送端发送数据帧之前就已通过附加一个数,做了“去余”处理(也就已经能整除了),所以结果应该是没有余数。如果有余数,则表明该帧在传输过程中出现了差错

【百科】

循环冗余校验算法将是否除尽,作为数据信息的校验规则。对于能被多项式除尽的数据而言,其数据代码的误码率就较低;对于那些不能被多项式除尽的数据,需要对数据出错的地方进行分析,并将产生的余数进行减去。,如果让被校验数据减去余数,势必能为生成多项式所除尽,但网络通信中减法操作存在复杂的数学计算,无法使用拼装的方式进行数据编码。

基于此种情况,网络通信中引入模2运算的计算方式,模2运算包括:模2加、模2减、模2乘、模2除等四种二进制运算。模2运算不考虑进位和借位,即模2加法是不带进位的二进制加法运算,模2减法是不带借位的二进制减法运算。两个序列模二相加,即两个序列中对应位,相加不进位,相同为0不同为1。在两个二进制位相运算时,这两个位的值能确定最终的运算结果,而不受前一次运算的影响,因此模2运算的加减乘除属于异或运算。

 

模2除法:模2除法与算术除法类似,但每一位除的结果不影响其它位,即不向上一位借位,所以实际上就是异或。在循环冗余校验码(CRC)的计算中有应用到模2除法。模2加法运算为:1+1=0,0+1=1,0+0=0,无进位,也无借位;模2减法运算为:1-1=0,0-1=1,1-0=1,0-0=0,也无进位,无借位。

模2除法/乘法:

CRC校验原理_第1张图片

 

CRC校验原理_第2张图片

 

CRC校验步骤

CRC校验中有两个关键点:

(1)预先确定一个发送送端和接收端都用来作为除数的二进制比特串(或多项式G(x)),可以随机选择,也可以使用国际标准,但是最高位和最低位必须为1即生成多项式G(x)要求次数大于0,并且要求0次幂的系数为1。;

       G(x)为发送端和接收端预先约好的生成多项式。G(x)的选取对校验效果起着关键的作用,使用较多的生成多项式G(x)有CRC-16、CRC-CCITT、CRC-32等;

CRC16的生成多项式为:G(x)= X16+X10+X2+1

CRC32的生成多项式为:G(x)= X32+X26+X23+X22+X16+X12+X11+X10+X8+X7+X5+X4+X2+X1+1

 

(2)把原始帧(假设为k位, 对应信息多项式)与上面计算出的除数(二进制比特串或生成多项式 G(x),假设为m位)进行模2除法运算,余数作为CRC校验码。

        CRC校验码计算示例:现假设选择的CRC生成多项式为G(X) = X4 + X3 + 1,要求出二进制序列10110011的CRC校验码。

设要发送的数据码有k位,则该数据码对应的多项式F(x)有k项,k的每一位取值只能为0或1,即F(x)中每项X^n的系数只能是0或1。

R(x)为生成的 r 阶冗余码多项式,R(x)的项数比G(x)要少一项。R(x)的计算方法为:以F(x)作为被除数,先将F(x)乘x′即左移r位,再以G(x)作为除数作模2运算。

EX:选定CRC校验的生成多项式G(x)=x^4+x^3+1,待传输二进制序列1011 0011,求出其发送端的CRC校验码以及接收端的校验过程。

答:下面是具体的计算过程:

发送端:

① 将选定的多项式转化为二进制序列:由G(X) = X4 + X3 + 1可知二进制一种有五位,第4位、第三位和第零位分别为1,则序列为11001

② 多项式的位数位5,则在数据帧的后面加上5-1位0,数据帧变为101100110000,然后使用模2除法除以除数11001,得到余数0100即为CRC校验码。

③ 将计算出来的CRC校验码0100添加在原始帧的后面,真正的数据帧为10110011 0100,再把这个数据帧发送到接收端。

④ 接收端收到数据帧10110011 0100后,依3旧 用上预先选定的除数11001,用模2除法除去,验证余数是否为0,如果为0,则说明数据帧没有出错。


发送端:

CRC校验原理_第3张图片

接收端:

CRC校验原理_第4张图片


Verilog设计

分析:CRC码由发送端计算,放置于发送信息报文的尾部。接收信息的设备再重新计算接收到信息报文的CRC,比较计算得到的CRC是否与接收到的相符,如果两者不相符,则表明出错。
校验码的计算多项式为(X16 + X15 + X2 + 1,1 1000 0000 0000 0101)。具体CRC16码的计算方法是:
        1.预置1个16位的寄存器为十六进制FFFF(即全为1);称此寄存器为CRC寄存器;
        2.把第一个8位二进制数据 (既通讯信息帧的第一个字节)与16位的CRC寄存器的低8位相异或,把结果放于CRC寄存器;
        3.把CRC寄存器的内容右移一 位(朝低位)用0填补最高位,并检查右移后的移出位;
        4.如果移出位为0:重复第3步(再次右移一位);
             如果移出位为1:CRC寄存器与多项式A001(1010 0000 0000 0001)进行异或;(Modbus)
        5.重复步骤3和4,直到右移8次,这样整个8位数据全部进行了处理;
        6.重复步骤2到步骤5,进行通讯信息帧下一个字节的处理;
        7.将该通讯信息帧所有字节按上述步骤计算完成后,得到的16位CRC寄存器的高、低字节进行交换;
        8.最后得到的CRC寄存器内容即为:CRC码。

 

下面举例说明CRC校验码的求法:(此例子摘自百度百科:CRC校验码)


信息字段代码为: 1011001;对应m(x)=x6+x4+x3+1
假设生成多项式为:g(x)=x4+x3+1;则对应g(x)的代码为: 11001
m(x)=x10+x8+x7+x4 对应的代码记为:10110010000;
采用多项式除法: 得余数为: 1010 (即CRC校验字段为:1010)
发送方:发出的传输字段为: 1 0 1 1 0 0 1 1010
给出余数(1010)的计算步骤:
除法没有数学上的含义,而是采用计算机的模二除法,即,除数和被除数做异或运算。进行异或运算时除数和被除数最高位对齐,按位异或。
10110010000
^

11001
--------------------------
01111010000
1111010000
^11001
-------------------------
0011110000
11110000
^11001
--------------------------
00111000
111000
^11001
-------------------
001010
则四位CRC校验码就为:1010。
 

 

补充:

CRC16常见的标准有以下几种,被用在各个规范中,其算法原理基本一致,就是在数据的输入和输出有所差异,下边把这些标准的差异列出,并给出C语言的算法实现。

CRC16_CCITT:多项式x16+x12+x5+1(0x1021),初始值0x0000,低位在前,高位在后,结果与0x0000异或

CRC16_CCITT_FALSE:多项式x16+x12+x5+1(0x1021),初始值0xFFFF,低位在后,高位在前,结果与0x0000异或

CRC16_XMODEM:多项式x16+x12+x5+1(0x1021),初始值0x0000,低位在后,高位在前,结果与0x0000异或

CRC16_X25:多项式x16+x12+x5+1(0x1021),初始值0x0000,低位在前,高位在后,结果与0xFFFF异或

CRC16_MODBUS:多项式x16+x15+x2+1(0x8005),初始值0xFFFF,低位在前,高位在后,结果与0x0000异或

CRC16_IBM:多项式x16+x15+x2+1(0x8005),初始值0x0000,低位在前,高位在后,结果与0x0000异或

CRC16_MAXIM:多项式x16+x15+x2+1(0x8005),初始值0x0000,低位在前,高位在后,结果与0xFFFF异或

CRC16_USB:多项式x16+x15+x2+1(0x8005),初始值0xFFFF,低位在前,高位在后,结果与0xFFFF异或

 

————————————————
原文链接:https://blog.csdn.net/li200503028/article/details/26591243

下面给出CRC校验码的verilog实现方式:
本例中实现了求得8bit信息序列的CRC校验码,生成多项式取g(x)=X^16+X^12+X^5+1,对应的生成序列为1_0001_0000_0010_0001,输入的8bit序列data左移16位后得到stemp={data,16‘b0000_0000_0000_0000} ,每次异步复位信号rst_n复位时将crc的值清零并把线型变量stemp的值打入寄存器temp中,然后通过时序电路将temp与生成多项式对应的序列进行多次按位异或,最终得到一个小于生成序列的temp后,temp[15:0]的值即为CRC校验序列,并把它赋给输出crc。
 下面是code:

 
  1.  
  2. //信息码 : 8bit

  3. //生成多项式 :1_0001_0000_0010_0001

  4. //附加位 :0000 0000 0000 0000

  5. //传输的码 : data+0000 0000 0000 0000 =24位

  6. module crc(

  7. clk ,

  8. data ,

  9. rst_n ,

  10. crc  

  11. );

  12. input      [7:0] data ;

  13. input             clk ;

  14. input             rst_n ;

  15. output reg [15:0] crc=0 ;

  16. wire [23:0] stemp ; 

  17. reg [23:0] temp=0 ;

  18. parameter polynomial=17'b1_0001_0000_0010_0001;

  19. assign stemp={data,16'b0000000000000000};

  20.  
  21. always @ (posedge clk or negedge rst_n)

  22.  begin 

  23.     if(!rst_n)

  24.           begin

  25.               crc <= 0 ;

  26.               temp <= stemp ; 

  27.               end

  28.     else 

  29.         begin

  30.           if(temp[23]) temp[23:7]<=temp[23:7]^polynomial;

  31.           else if(temp[22]) temp[22:6]<=temp[22:6]^polynomial;

  32.           else if(temp[21]) temp[21:5]<=temp[21:5]^polynomial;

  33.          else if(temp[20]) temp[20:4]<=temp[20:4]^polynomial;

  34.           else if(temp[19]) temp[19:3]<=temp[19:3]^polynomial;

  35.           else if(temp[18]) temp[18:2]<=temp[18:2]^polynomial;

  36.           else if(temp[17]) temp[17:1]<=temp[17:1]^polynomial;

  37.           else if(temp[16]) temp[16:0]<=temp[16:0]^polynomial;

  38.           else   crc<=temp[15:0];    

  39.           end

  40.  end  

  41. endmodule


testbench:

 
  1. module testbench;

  2. // Inputs

  3. reg clk;

  4. reg [7:0] data;

  5. reg rst_n;

  6.  
  7. // Outputs

  8. wire [15:0] crc;

  9.  
  10. // Instantiate the Unit Under Test (UUT)

  11. crc uut (

  12. .clk(clk), 

  13. .data(data), 

  14. .rst_n(rst_n), 

  15. .crc(crc)

  16. );

  17. initial begin

  18. // Initialize Inputs

  19.  
  20. clk=0;

  21. data=0;

  22. rst_n=0;

  23. #100

  24.  
  25. data =8'b10110110;

  26. rst_n = 1;

  27.  
  28. // Wait 100 ns for global reset to finish

  29. #50;

  30. rst_n =0;

  31. #50

  32. rst_n =1;

  33.                 #1000

  34.  
  35. data =8'b01001100;

  36. rst_n = 1;

  37. #50;

  38. rst_n =0;

  39. #50

  40. rst_n =1;

  41. #1000

  42.  
  43. data =8'b10110011;

  44. rst_n = 1;

  45. #50;

  46. rst_n =0;

  47. #50

  48. rst_n =1;

  49. #1000

  50.  
  51. data =8'b10010110;

  52. rst_n = 1;

  53. #50;

  54. rst_n =0;

  55. #50

  56. rst_n =1;

  57. #1000

  58.  
  59. data =8'b10100101;

  60. rst_n = 1;

  61. #50;

  62. rst_n =0;

  63. #50

  64. rst_n =1;

  65. // Add stimulus here

  66. end

  67.       always #10 clk=~clk;

  68. endmodule


下面是仿真结果:
testbench中加了5组输入的data值,仿真结果已通过软件《CRC计算器》验证,全部正确。

 

你可能感兴趣的:(CRC校验原理)