CRC校验
CRC校验是怎么回事?
CRC校验又叫做循环冗余校验码,利用二进制除法和求余的方式进行数据查错校验,以保证传输过程中数据的正确完整性。其中多项式为二进制除数,进行校验的数据为被除数,余数则为CRC校验码。
具体地,如发送操作时,对发送数据进行CRC多项式计算,得到固定位数的CRC校验值,将该值附在发送数据的后面一起发送。
接收时首先将接收到的数据进行数据和校验码的分离,然后对数据采用同样的多项式进行计算,若计算结果和接收到的校验值一致,说明数据校验正确,该数据保留,否则说明传输过程中出错,该次传输数据丢弃重发。
CRC多项式计算时采用线性反馈移位寄存器(lfsr)的方式,各级寄存器储存着上一次CRC运算的结果,经整个lfsr后,得到最终的CRC校验结果。
另外不同的多项式表示不同的CRC校验,其对应的lsfr模型不同,检测效果以及CRC校验结果也就不同。
比如说16多项式= X16+X12+X^5+1,即CRC16,其CRC码有16bit。
有了多项式之后如何计算?
1、 首先确定多项式,根据多项式得到二进制数,多项式最高次数为多少,则在待校验数据后加多少bit的0。
若多项式 = X^4 + x^3 + 1,则对应二进制数11001,最高次=4,因此在被除数后加4bit0。
2、 然后开始模2运算(异或运算),即求余数。
3、 算出来的余数即为CRC校验码,注意余数位数必须比除数少1位,如果不够前面加0补齐
4、 将该校验码加在原数据帧的后面,构成新的发送数据
5、 接收端再次对接收到的数据进行模2运算,比较余数,若相同,说明传输正确。
以上是CRC校验码计算的过程,但实际应用中存在不同的CRC参数模型,从而导致相同的多项式也可能计算出不同的CRC校验码。
因此计算前首先要确定所采用的CRC参数模型。
一个完成的CRC参数模型 = CRC数据位宽(width)、十六进制多项式(POLY)、多项式输入初值(INIT)、输入数据反转(REFIN)、输出数据反转(REFOUT)、输出结果异或(XOROUT).
具体如下:
Width:表示CRC校验位宽;多使用CRC16,CRC校验位宽为16bit
POLY:十六进制多项式。如 x8 + x2 + x + 1,二进制为1 0000 0111,省略最高位1,转换为十六进制为0x07 ( 之所以把最高位去掉,是因为任意多项式生成的二进制数的最高位肯定为1,所以为了便于16进制表示,统一把最高位去掉 )
INIT:和Width位宽一致,如全1或全0
REFIN:决定输入是否进行字节反转,false不反转;true反转
REFOUT:决定输出是否bit反转, false不反转;true反转
XOROUT:输出结果异或,若异或值全0,那么结果与全0异或运算,运算后得到最终的CRC校验结果。
通常如果只给了一个多项式,那么默认:INIT=0x00,REFIN=false,REFOUT=false,XOROUT=0x00
Verilog如何实现?
当已知CRC多项式时,可以首先在某网址下生成对应的verilog代码。
然后根据CRC参数模型在生成的代码基础上进行修改即可。
给出具体的例子:
比如采用CRC16 = X16 + X12 +X5 + 1 ;根据多项式以及width=16生成对应的verilog代码后,若:INIT = 16’hFFFF; XOROUT = 16’h0000; REFIN = false; REFOUT = false;
那么代码如下:
//INIT = 16'hFFFF; XOROUT = 16'h0000; REFIN = false; REFOUT = false;
//crc[15:0]=1+x^5+x^12+x^16;
module crc_16(
input USER_CLK ,
input SYS_RST ,
input CRC_EN ,
input [15:0] DATA_IN ,
output [15:0] CRC_OUT
);
wire [15:0] c ;
reg [15:0] newcrc ;
reg [15:0] d ;
//输入不反转
assign c = DATA_IN;
always @ (posedge USER_CLK)begin
if(SYS_RST)
d <= 16'hFFFF;//初值
else begin
if(CRC_EN)
d <= newcrc;
else
d <= 16'hFFFF;
end
end
//输出不反转,且进行异或运算
assign CRC_OUT = d ^ 16'h0000;
always @ (*) begin
newcrc[0] = d[12] ^ d[11] ^ d[8] ^ d[4] ^ d[0] ^ c[0] ^ c[4] ^ c[8] ^ c[11] ^ c[12];
newcrc[1] = d[13] ^ d[12] ^ d[9] ^ d[5] ^ d[1] ^ c[1] ^ c[5] ^ c[9] ^ c[12] ^ c[13];
newcrc[2] = d[14] ^ d[13] ^ d[10] ^ d[6] ^ d[2] ^ c[2] ^ c[6] ^ c[10] ^ c[13] ^ c[14];
newcrc[3] = d[15] ^ d[14] ^ d[11] ^ d[7] ^ d[3] ^ c[3] ^ c[7] ^ c[11] ^ c[14] ^ c[15];
newcrc[4] = d[15] ^ d[12] ^ d[8] ^ d[4] ^ c[4] ^ c[8] ^ c[12] ^ c[15];
newcrc[5] = d[13] ^ d[12] ^ d[11] ^ d[9] ^ d[8] ^ d[5] ^ d[4] ^ d[0] ^ c[0] ^ c[4] ^ c[5] ^ c[8] ^ c[9] ^ c[11] ^ c[12] ^ c[13];
newcrc[6] = d[14] ^ d[13] ^ d[12] ^ d[10] ^ d[9] ^ d[6] ^ d[5] ^ d[1] ^ c[1] ^ c[5] ^ c[6] ^ c[9] ^ c[10] ^ c[12] ^ c[13] ^ c[14];
newcrc[7] = d[15] ^ d[14] ^ d[13] ^ d[11] ^ d[10] ^ d[7] ^ d[6] ^ d[2] ^ c[2] ^ c[6] ^ c[7] ^ c[10] ^ c[11] ^ c[13] ^ c[14] ^ c[15];
newcrc[8] = d[15] ^ d[14] ^ d[12] ^ d[11] ^ d[8] ^ d[7] ^ d[3] ^ c[3] ^ c[7] ^ c[8] ^ c[11] ^ c[12] ^ c[14] ^ c[15];
newcrc[9] = d[15] ^ d[13] ^ d[12] ^ d[9] ^ d[8] ^ d[4] ^ c[4] ^ c[8] ^ c[9] ^ c[12] ^ c[13] ^ c[15];
newcrc[10] = d[14] ^ d[13] ^ d[10] ^ d[9] ^ d[5] ^ c[5] ^ c[9] ^ c[10] ^ c[13] ^ c[14];
newcrc[11] = d[15] ^ d[14] ^ d[11] ^ d[10] ^ d[6] ^ c[6] ^ c[10] ^ c[11] ^ c[14] ^ c[15];
newcrc[12] = d[15] ^ d[8] ^ d[7] ^ d[4] ^ d[0] ^ c[0] ^ c[4] ^ c[7] ^ c[8] ^ c[15];
newcrc[13] = d[9] ^ d[8] ^ d[5] ^ d[1] ^ c[1] ^ c[5] ^ c[8] ^ c[9];
newcrc[14] = d[10] ^ d[9] ^ d[6] ^ d[2] ^ c[2] ^ c[6] ^ c[9] ^ c[10];
newcrc[15] = d[11] ^ d[10] ^ d[7] ^ d[3] ^ c[3] ^ c[7] ^ c[10] ^ c[11];
end
endmodule