以太网CRC检验原理及实现
- 项目简述
- 基本概念
- CRC-8 的工作原理
- CRC32 8 位输入校验推算过程
- 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检验部分的代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150module 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文件、图片文件在群中)对文章有什么看法或者需要更近一步交流的同学,可以加入下面的群:
最后
以上就是飞快石头最近收集整理的关于基于FPGA的千兆以太网的实现(3)项目简述基本概念CRC-8 的工作原理CRC32 8 位输入校验推算过程CRC检验代码总结的全部内容,更多相关基于FPGA的千兆以太网的实现(3)项目简述基本概念CRC-8内容请搜索靠谱客的其他文章。
发表评论 取消回复