以太网CRC检验原理及实现
项目简述
在千兆以太网的项目中,有一个非常关键的步骤就是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-8 的工作原理
都说 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 个时钟就可以处
理好全部的内容。 速度提升,也不会给输入系统带来瓶颈。
CRC32 8 位输入校验推算过程
以上 0-31 是 crc 寄存器,以下记做 C0-C31;
化简相同的 D 去掉:
经过上面的推导,一个八位数经过一个时钟周期便可以出来检验码。接下来给出我们的CRC检验部分的代码。
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