在千兆以太网的项目中,有一个非常关键的步骤就是CRC检验。CRC检验是一种循环码,属于线性分组码的一种,具体的概念参考通信原理与信息论,上面关于这些的概念非常详细。
冗余校验
循环冗余码校验(Cyclical Redundancy Check),简称 CRC。 是数据通信领域中最常用的一
种差错校验码,其特征是信息字段和校验字段的长度可以任意选定。
生成多项式
关于生成多项式,就算是 CRC-8 都有好几种,但总有这么一个结果,最低位和最高位
均为 1,所以可以表达为:
G(X) = X8+H7X7+H6X6+H5X5+H4X4+H3X3+H2X2+H1*X1+X0
其中 Hn 为相关性,只能是”0”或者”1”,
关于 G(X),这里不会更详细的说明为什么,这些问题交给数学家们,而作为设计者,只
需要满足需求的情况下即可。
都说 CRC 是除法运算,但跟数值运算除法有很大的差异。这或许是为了方便硬件资源而
设计的。
首先介绍 CRC-8, CRC-8 的余数是一个 8 比特数据,意味着发送设备除了发送 k 位信息
码外,还需要 8 比特的校验位。假设信息码为(0110_0010_0100_1100)2,这是一个 16 比特的
信息位,也是设备需要发送的数据。但加上 CRC-8 校验位,则必须发送 24(16+8)比特的数据。
假设我们的 CRC-8 生成多项式的方程为 G(X) = X8+X2+X1+X0, 合计为(1_0000_0111)2
有几种方法可以计算,有实际手算、移位寄存器和并行处理
1. 实际手算
a. 首先,把信息位左移 8 比特。即结果为: 0110001001001100_0000_0000
然后做异或除法运算:
计算结果,取余数 (01100101)2 则总共需要发送的序列:
0110001001001100_00111100
|<----------信息位---------->| |<-校验位->|
b. 如果接收端接收到该序列,除去生成多项式 G(X),则结果应该为 0。
计算如下:
运算结果为 0 则说明以上校验位为正确的。
2. 移位寄存器
G(X) = X8+X2+X1+X0, 合计为(1_0000_0111)2
移位寄存器接近于硬件设计。在输入为单比特时很有效。
按照生成多项式,可以画出如图 3.1 所示的硬件结构图:
图中包含 8 个寄存器,初始值均为 1。
数据由左端输入口输入。分为两种情况:生成 CRC-8 校验码和解码。
其中生成 CRC-8 校验码,如上例中信息位为(0110001001001100)2,则将
(0110001001001100_00000000)2 输入该移位寄存器中,最后读取 8 个 寄存器的值
即为生成的校验码。
其中对于寄存器的每一个时钟上升沿,值为:
其中 n 为当前输入的比特数。
当输入一个 8 比特数据后,输出寄存器应该为:
其中 X0-X7 为输入数据, I1-I8 为寄存器初始值。
从以上两种方式生成和校验中都可以完成 CRC 生成和解码,但就 FPGA 设计而言,实际
手算没能帮上什么忙,移位寄存器是一个很好的选择。但对于输入数据的大小和速率,限制
了该方式,为什么呢?当输入数据为 8 比特,则我们就得增加 8 倍时钟的速率来处理。这大
大降低了工作效率。而最后的并行加速处理后。每输入 8 比特数据,只要 1 个时钟就可以处
理好全部的内容。 速度提升,也不会给输入系统带来瓶颈。
以上 0-31 是 crc 寄存器,以下记做 C0-C31;
化简相同的 D 去掉:
经过上面的推导,一个八位数经过一个时钟周期便可以出来检验码。接下来给出我们的CRC检验部分的代码。
CRC检验部分的代码如下:
module crc32_d8_send_02(
input resetb ,
input sclk ,
input dsin ,
input [ 7:0] din ,
input pre_flag ,
input crc_err_en ,
output reg dsout ,
output reg [ 7:0] dout
);
//========================================================================================\
//**************Define Parameter and Internal Signals**********************************
//========================================================================================/
reg crc_ds,crc_en ;
reg [ 4:0] crc_ds_t ;
reg dsin_t ;
reg [ 7:0] din_t ;
reg [ 2:0] d_count ;
reg [ 7:0] d ;
wire [31:0] c ;
reg [31:0] crc32_value ;
reg crc_en_t ;
reg [ 2:0] d_cnt ;
//========================================================================================\
//************** Main Code **********************************
//========================================================================================/
assign c = crc32_value;
always @(dsin or pre_flag)
if (dsin == 1 && pre_flag == 0)
crc_ds <= 1;
else
crc_ds <= 0;
always @(posedge sclk or negedge resetb)
if (resetb == 0)
crc_ds_t <= 0;
else
crc_ds_t <= {crc_ds_t[3:0],crc_ds};
always@(posedge sclk)
if (crc_ds == 1)//crc_ds_t[3]==1)
crc_en <= 1;
else
crc_en <= 0;
always@(posedge sclk)
if (crc_ds == 0)
d_count <= 0;
else if (d_count[2] == 0)
d_count <= d_count + 1;
always@(posedge sclk)
if (crc_ds == 'd0)
d <= 'd0;
else if(d_count[2] == 0)
d <= din;
else
d <= din;
always @(posedge sclk or negedge resetb)
if (resetb==0)
crc32_value <= 32'hFFFFFFFF;
else if(crc_en == 1)
begin
crc32_value[0] <= c[24]^c[30]^d[1]^d[7];
crc32_value[1] <= c[25]^c[31]^d[0]^d[6]^c[24]^c[30]^d[1]^d[7];
crc32_value[2] <= c[26]^d[5]^c[25]^c[31]^d[0]^d[6]^c[24]^c[30]^d[1]^d[7];
crc32_value[3] <= c[27]^d[4]^c[26]^d[5]^c[25]^c[31]^d[0]^d[6];
crc32_value[4] <= c[28]^d[3]^c[27]^d[4]^c[26]^d[5]^c[24]^c[30]^d[1]^d[7];
crc32_value[5] <= c[29]^d[2]^c[28]^d[3]^c[27]^d[4]^c[25]^c[31]^d[0]^d[6]^c[24]^c[30]^d[1]^d[7];
crc32_value[6] <= c[30]^d[1]^c[29]^d[2]^c[28]^d[3]^c[26]^d[5]^c[25]^c[31]^d[0]^d[6];
crc32_value[7] <= c[31]^d[0]^c[29]^d[2]^c[27]^d[4]^c[26]^d[5]^c[24]^d[7];
crc32_value[8] <= c[0]^c[28]^d[3]^c[27]^d[4]^c[25]^d[6]^c[24]^d[7];
crc32_value[9] <= c[1]^c[29]^d[2]^c[28]^d[3]^c[26]^d[5]^c[25]^d[6];
crc32_value[10] <= c[2]^c[29]^d[2]^c[27]^d[4]^c[26]^d[5]^c[24]^d[7];
crc32_value[11] <= c[3]^c[28]^d[3]^c[27]^d[4]^c[25]^d[6]^c[24]^d[7];
crc32_value[12] <= c[4]^c[29]^d[2]^c[28]^d[3]^c[26]^d[5]^c[25]^d[6]^c[24]^c[30]^d[1]^d[7];
crc32_value[13] <= c[5]^c[30]^d[1]^c[29]^d[2]^c[27]^d[4]^c[26]^d[5]^c[25]^c[31]^d[0]^d[6];
crc32_value[14] <= c[6]^c[31]^d[0]^c[30]^d[1]^c[28]^d[3]^c[27]^d[4]^c[26]^d[5];
crc32_value[15] <= c[7]^c[31]^d[0]^c[29]^d[2]^c[28]^d[3]^c[27]^d[4];
crc32_value[16] <= c[8]^c[29]^d[2]^c[28]^d[3]^c[24]^d[7];
crc32_value[17] <= c[9]^c[30]^d[1]^c[29]^d[2]^c[25]^d[6];
crc32_value[18] <= c[10]^c[31]^d[0]^c[30]^d[1]^c[26]^d[5];
crc32_value[19] <= c[11]^c[31]^d[0]^c[27]^d[4];
crc32_value[20] <= c[12]^c[28]^d[3];
crc32_value[21] <= c[13]^c[29]^d[2];
crc32_value[22] <= c[14]^c[24]^d[7];
crc32_value[23] <= c[15]^c[25]^d[6]^c[24]^c[30]^d[1]^d[7];
crc32_value[24] <= c[16]^c[26]^d[5]^c[25]^c[31]^d[0]^d[6];
crc32_value[25] <= c[17]^c[27]^d[4]^c[26]^d[5];
crc32_value[26] <= c[18]^c[28]^d[3]^c[27]^d[4]^c[24]^c[30]^d[1]^d[7];
crc32_value[27] <= c[19]^c[29]^d[2]^c[28]^d[3]^c[25]^c[31]^d[0]^d[6];
crc32_value[28] <= c[20]^c[30]^d[1]^c[29]^d[2]^c[26]^d[5];
crc32_value[29] <= c[21]^c[31]^d[0]^c[30]^d[1]^c[27]^d[4];
crc32_value[30] <= c[22]^c[31]^d[0]^c[28]^d[3];
crc32_value[31] <= c[23]^c[29]^d[2];
end
else
crc32_value <= {crc32_value[23:0],8'hff};
always @(posedge sclk)
crc_en_t <= crc_en;
always @ (posedge sclk)
if(!resetb)
d_cnt <= 0;
else if(!crc_en && crc_en_t)
d_cnt <= d_cnt + 1'd1;
else if(d_cnt>3'd0 && d_cnt<3'd4)
d_cnt <= d_cnt+1'd1;
else
d_cnt <= 3'd0;
//dsin_t
always @(posedge sclk)
dsin_t <= dsin;
always @ (posedge sclk)
din_t <= din;
always@(posedge sclk)
if (dsin_t == 'd0 && d_cnt == 'd0 && crc_en_t == 'd0)
dout <= 'd0;
else if (dsin_t == 1'b1)
dout <= din_t;
else if (d_cnt == 'd4)
dout <= 'd0;
else if (crc_err_en == 0)
dout <= ~{crc32_value[24], crc32_value[25], crc32_value[26], crc32_value[27], crc32_value[28], crc32_value[29], crc32_value[30], crc32_value[31]};
else
dout <= {crc32_value[24], crc32_value[25], crc32_value[26], crc32_value[27], crc32_value[28], crc32_value[29], crc32_value[30], crc32_value[31]};
//dsout
always @(posedge sclk)
if(!resetb)
dsout <= 'd0;
else if(dsin_t)
dsout <= 'd1;
else if(d_cnt == 'd4)
dsout <= 'd0;
endmodule
上面的代码是我们根据我们前面的推导写出来的CRC代码。
创作不易,认为文章有帮助的同学们可以关注、点赞、转发支持。(txt文件、图片文件在群中)对文章有什么看法或者需要更近一步交流的同学,可以加入下面的群: