一、概念
CRC校验,中文翻译过来是:循环冗余校验,英文全称是:Cyclic Redundancy Check。是一种通过对数据产生固定位数的校验码,以检验数据是否存在错误的技术。
其主要特点是检错能力强、开销小,易于电路实现。像网络通信上,就使用了CRC32进行数据校验。
1.1 CRC的数学基础
其数学基础是,使用除法求余数。
1、将K位的信息码写成如下多项式形式:
2、将信息码左移R位,变成如下多项式形式:
3、将移位后的信息码,除以指定的生成多项式,最后得到的余数即为CRC校验值。
转换成二进制信息表述如下:
1、K位的信息码,右移R位,得到新的K+R位的信息码,
2、将新的K+R位的信息码,除以指定的二进制数,得到的余数即为CRC校验值。
当然,此处采用的是模2运算,即没有借位。实质上在运行加减法的时候,采用的是异或运算。
1.2 其他重要概念
CRC 校验的核心是模2除法运算,但是还存在一些其他的规则,描述如下:
初始值:给CRC一个计算初始值,可以是0,也可以为其他值,会将待计算的信息码的值与初始值进行异或。(网上大部分关于CRC的校验计算,初始值都是默认取0,但是实际应用中,比如CRC32,其初始值是0xFFFFFFFF)
结果异或值:将计算结果与结果异或值进行异或运算后输出,目的是防止全0数据的CRC一直为0,
数据反转:CRC中数据 反转,指的是一个字节的数据中,高bit变低bit,低bit变高bit。 比如0x55,经过数据反转后,变为0xAA。
生成多项式:模2除法中的除数,根据多项式可以生成二进制除数,不同的CRC校验有不同的多项式。
1.3 CRC校验的标准流程
1、初始值赋值给crc_reg;
2、判断信息码是否需要反转,若需要则进行数据反转,不需要则保持不变,结果赋值给crc_reg;
3、信息码(或者反转后的信息码)左移R位,即信息码后面补上R个二进制的0;(R为校验码的位宽,同时也是生成多项式的最高次幂)
4、crc_reg与补0后的信息码(高位)进行异或运算,并赋值给crc_reg;
5、crc_reg与信息码进行模2除法运算,运算的余数结果赋值给crc_reg;
6、判断输出结果是否需要反转,若需要则进行数据反转,不需要则保持不变,结果赋值给crc_reg;
7、crc_reg与结果异或值进行异或运算,得到最终的校验值。
二、CRC32
2.1 CRC32相关信息
最近在考虑使用FPGA实现UDP协议,就研究到了CRC32校验,像赛灵思提供的MAC核内部就实现了CRC32校验方式。于是我就抱着学习的态度,研究了一下CRC32。关于CRC8、CRC16等等其余的CRC校验方式,此处就不赘述了。
CRC32校验里面提到了几个概念:
1、生成多项式(generator polynomial)
CRC32=X32+X26+X23+X22+X16+X12+X11+X10+X8+X7+X5+X4+X2+X1+1。
二进制可以表示为33'b1_0000_0100_1100_0001_0001_1101_1011_0111。
十六进制表示为32‘h104C11DB7。
生成多项式,即为除数。
2、待校验的数据
待校验的数据即为被除数,即上面描述的信息码。
最终获得的结果,即为CRC校验值。
2.2 CRC32校验流程
具体的操作流程:
1、初始值赋值给crc_reg;
2、判断信息码是否需要反转,若需要则进行数据反转,不需要则保持不变,结果赋值给crc_reg;
3、信息码(或者反转后的信息码)左移32位,即信息码后面补上32个二进制的0;
4、crc_reg与补0后的信息码(高32位)进行异或运算,并赋值给crc_reg;
5、crc_reg与信息码进行模2除法运算,运算的余数结果赋值给crc_reg;
6、判断输出结果是否需要反转,若需要则进行数据反转,不需要则保持不变,结果赋值给crc_reg;
7、crc_reg与结果异或值进行异或运算,得到最终的校验值。
按照C语言编写了此CRC32校验流程,在VScode中进行测试。设置不同的反转信息、初始值、结果异或值,输出结果与CRC计算工具相一致。
CRC计算器工具:
CRC(循环冗余校验)在线计算_ip33.comhttp://www.ip33.com/crc.html
//8位数据反转 uint8_t invertuint8(uint8_t data) { uint8_t tmp; tmp = 0; for(int i = 0; i<8;i++){ if(data & (1<<i) ){ tmp |= 1<< (7-i); } } return tmp; } //32位数据反转 uint32_t invertuint32(uint32_t data) { uint32_t tmp; tmp = 0; for(int i = 0; i<32;i++){ if(data &(1<<i) ){ tmp |= 1<< (31-i); } } return tmp; } uint32_t CRC32(uint8_t& data) { uint8_t in_reverse_en = 1; //输入数据是否反转 uint8_t out_reverse_en = 1;//输出数据是否反转 uint32_t poly = 0x04C11DB7; //生成多项式0x1_04C11DB7 ,仅取低32位,最高为1通过左移直接处理 uint32_t init_value = 0xFFFFFFFF;//初始值 uint32_t out_xor_value = 0xFFFFFFFF;//结果异或值 uint32_t crc_reg; uint32_t data_reg; uint32_t data_shift; //第一步,赋初值 crc_reg = init_value; //第二步,输入是否反转 if(in_reverse_en == 1) data_reg = invertuint8(data); else data_reg = data; printf("data:%x\n",data); printf("data_reg:%x\n",data_reg); //第三步,信息码左移32位,赋值。由于字宽限制,仅左移24位,实际上8次移位异或运算后,其余数与原有运算相一致。 data_shift = (data_reg <<24); //第四步,初始值与数据异或操作 crc_reg = crc_reg ^ data_shift; //第五步,模2除法 for(int i=0;i<8;i++){ if(crc_reg & 0x80000000)//最高位是1时 crc_reg = (crc_reg << 1) ^ poly; //左移将生成多项式的第32bits处理掉,相当于异或 else crc_reg = crc_reg << 1; } // //第六步,输出是否反转 if(out_reverse_en == 1) crc_reg = invertuint32(crc_reg); //第七步,与结果异或值进行异或运算 crc_reg = crc_reg^out_xor_value; return crc_reg; } int main(int argc, char *argv[]) { uint8_t data = 0x55; uint32_t CRC_result; CRC_result = CRC32(data); printf("CRC_result:%x\n",CRC_result); getchar(); return 0; }
三、CRC32的FPGA实现
原理已经清楚了,按照上述流程就可以实现CRC32。但是FPGA有更简易的实现形式。就属于找规律的范畴了。对于CRC32,上一个校验值(或者初始值)进行CRC校验的时候,CRC校验的单个bits的校验结果固定与上一个校验值的某几个bits有关。
所以可以直接采用bit运算的方式输出CRC校验结果。具体找规律这里不再分析,直接上示例代码(正点原子的代码)。也有现成的CRC FPGA代码生成工具,可以直接调用。
CRC代码生成工具一:Easics CRC Toolhttp://crctool.easics.be/
CRC代码生成工具二:OutputLogic.com » CRC Generatorhttp://outputlogic.com/?page_id=321
module crc32_d8( input clk , //时钟信号 input rst_n , //复位信号,低电平有效 input [7:0] data , //输入待校验8位数据 input crc_en , //crc使能,开始校验标志 input crc_clr , //crc数据复位信号 output reg [31:0] crc_data, //CRC校验数据 output [31:0] crc_next //CRC下次校验完成数据 ); //***************************************************** //** main code //***************************************************** //输入待校验8位数据,需要先将高低位互换 wire [7:0] data_t; assign data_t = {data[0],data[1],data[2],data[3],data[4],data[5],data[6],data[7]}; //CRC32的生成多项式为:G(x)= x^32 + x^26 + x^23 + x^22 + x^16 + x^12 + x^11 //+ x^10 + x^8 + x^7 + x^5 + x^4 + x^2 + x^1 + 1 assign crc_next[0] = crc_data[24] ^ crc_data[30] ^ data_t[0] ^ data_t[6]; assign crc_next[1] = crc_data[24] ^ crc_data[25] ^ crc_data[30] ^ crc_data[31] ^ data_t[0] ^ data_t[1] ^ data_t[6] ^ data_t[7]; assign crc_next[2] = crc_data[24] ^ crc_data[25] ^ crc_data[26] ^ crc_data[30] ^ crc_data[31] ^ data_t[0] ^ data_t[1] ^ data_t[2] ^ data_t[6] ^ data_t[7]; assign crc_next[3] = crc_data[25] ^ crc_data[26] ^ crc_data[27] ^ crc_data[31] ^ data_t[1] ^ data_t[2] ^ data_t[3] ^ data_t[7]; assign crc_next[4] = crc_data[24] ^ crc_data[26] ^ crc_data[27] ^ crc_data[28] ^ crc_data[30] ^ data_t[0] ^ data_t[2] ^ data_t[3] ^ data_t[4] ^ data_t[6]; assign crc_next[5] = crc_data[24] ^ crc_data[25] ^ crc_data[27] ^ crc_data[28] ^ crc_data[29] ^ crc_data[30] ^ crc_data[31] ^ data_t[0] ^ data_t[1] ^ data_t[3] ^ data_t[4] ^ data_t[5] ^ data_t[6] ^ data_t[7]; assign crc_next[6] = crc_data[25] ^ crc_data[26] ^ crc_data[28] ^ crc_data[29] ^ crc_data[30] ^ crc_data[31] ^ data_t[1] ^ data_t[2] ^ data_t[4] ^ data_t[5] ^ data_t[6] ^ data_t[7]; assign crc_next[7] = crc_data[24] ^ crc_data[26] ^ crc_data[27] ^ crc_data[29] ^ crc_data[31] ^ data_t[0] ^ data_t[2] ^ data_t[3] ^ data_t[5] ^ data_t[7]; assign crc_next[8] = crc_data[0] ^ crc_data[24] ^ crc_data[25] ^ crc_data[27] ^ crc_data[28] ^ data_t[0] ^ data_t[1] ^ data_t[3] ^ data_t[4]; assign crc_next[9] = crc_data[1] ^ crc_data[25] ^ crc_data[26] ^ crc_data[28] ^ crc_data[29] ^ data_t[1] ^ data_t[2] ^ data_t[4] ^ data_t[5]; assign crc_next[10] = crc_data[2] ^ crc_data[24] ^ crc_data[26] ^ crc_data[27] ^ crc_data[29] ^ data_t[0] ^ data_t[2] ^ data_t[3] ^ data_t[5]; assign crc_next[11] = crc_data[3] ^ crc_data[24] ^ crc_data[25] ^ crc_data[27] ^ crc_data[28] ^ data_t[0] ^ data_t[1] ^ data_t[3] ^ data_t[4]; assign crc_next[12] = crc_data[4] ^ crc_data[24] ^ crc_data[25] ^ crc_data[26] ^ crc_data[28] ^ crc_data[29] ^ crc_data[30] ^ data_t[0] ^ data_t[1] ^ data_t[2] ^ data_t[4] ^ data_t[5] ^ data_t[6]; assign crc_next[13] = crc_data[5] ^ crc_data[25] ^ crc_data[26] ^ crc_data[27] ^ crc_data[29] ^ crc_data[30] ^ crc_data[31] ^ data_t[1] ^ data_t[2] ^ data_t[3] ^ data_t[5] ^ data_t[6] ^ data_t[7]; assign crc_next[14] = crc_data[6] ^ crc_data[26] ^ crc_data[27] ^ crc_data[28] ^ crc_data[30] ^ crc_data[31] ^ data_t[2] ^ data_t[3] ^ data_t[4] ^ data_t[6] ^ data_t[7]; assign crc_next[15] = crc_data[7] ^ crc_data[27] ^ crc_data[28] ^ crc_data[29] ^ crc_data[31] ^ data_t[3] ^ data_t[4] ^ data_t[5] ^ data_t[7]; assign crc_next[16] = crc_data[8] ^ crc_data[24] ^ crc_data[28] ^ crc_data[29] ^ data_t[0] ^ data_t[4] ^ data_t[5]; assign crc_next[17] = crc_data[9] ^ crc_data[25] ^ crc_data[29] ^ crc_data[30] ^ data_t[1] ^ data_t[5] ^ data_t[6]; assign crc_next[18] = crc_data[10] ^ crc_data[26] ^ crc_data[30] ^ crc_data[31] ^ data_t[2] ^ data_t[6] ^ data_t[7]; assign crc_next[19] = crc_data[11] ^ crc_data[27] ^ crc_data[31] ^ data_t[3] ^ data_t[7]; assign crc_next[20] = crc_data[12] ^ crc_data[28] ^ data_t[4]; assign crc_next[21] = crc_data[13] ^ crc_data[29] ^ data_t[5]; assign crc_next[22] = crc_data[14] ^ crc_data[24] ^ data_t[0]; assign crc_next[23] = crc_data[15] ^ crc_data[24] ^ crc_data[25] ^ crc_data[30] ^ data_t[0] ^ data_t[1] ^ data_t[6]; assign crc_next[24] = crc_data[16] ^ crc_data[25] ^ crc_data[26] ^ crc_data[31] ^ data_t[1] ^ data_t[2] ^ data_t[7]; assign crc_next[25] = crc_data[17] ^ crc_data[26] ^ crc_data[27] ^ data_t[2] ^ data_t[3]; assign crc_next[26] = crc_data[18] ^ crc_data[24] ^ crc_data[27] ^ crc_data[28] ^ crc_data[30] ^ data_t[0] ^ data_t[3] ^ data_t[4] ^ data_t[6]; assign crc_next[27] = crc_data[19] ^ crc_data[25] ^ crc_data[28] ^ crc_data[29] ^ crc_data[31] ^ data_t[1] ^ data_t[4] ^ data_t[5] ^ data_t[7]; assign crc_next[28] = crc_data[20] ^ crc_data[26] ^ crc_data[29] ^ crc_data[30] ^ data_t[2] ^ data_t[5] ^ data_t[6]; assign crc_next[29] = crc_data[21] ^ crc_data[27] ^ crc_data[30] ^ crc_data[31] ^ data_t[3] ^ data_t[6] ^ data_t[7]; assign crc_next[30] = crc_data[22] ^ crc_data[28] ^ crc_data[31] ^ data_t[4] ^ data_t[7]; assign crc_next[31] = crc_data[23] ^ crc_data[29] ^ data_t[5]; always @(posedge clk or negedge rst_n) begin if(!rst_n) crc_data <= 32'hff_ff_ff_ff; else if(crc_clr) //CRC校验值复位 crc_data <= 32'hff_ff_ff_ff; else if(crc_en) crc_data <= crc_next; else; end endmodule