基于FPGA的千兆以太网的实现(3)

以太网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
然后做异或除法运算:
基于FPGA的千兆以太网的实现(3)_第1张图片
计算结果,取余数 (01100101)2 则总共需要发送的序列:
0110001001001100_00111100
|<----------信息位---------->| |<-校验位->|

b. 如果接收端接收到该序列,除去生成多项式 G(X),则结果应该为 0。
计算如下:
基于FPGA的千兆以太网的实现(3)_第2张图片
基于FPGA的千兆以太网的实现(3)_第3张图片
运算结果为 0 则说明以上校验位为正确的。

2. 移位寄存器
G(X) = X8+X2+X1+X0, 合计为(1_0000_0111)2
移位寄存器接近于硬件设计。在输入为单比特时很有效。
按照生成多项式,可以画出如图 3.1 所示的硬件结构图:
基于FPGA的千兆以太网的实现(3)_第4张图片
图中包含 8 个寄存器,初始值均为 1。
数据由左端输入口输入。分为两种情况:生成 CRC-8 校验码和解码。
其中生成 CRC-8 校验码,如上例中信息位为(0110001001001100)2,则将
(0110001001001100_00000000)2 输入该移位寄存器中,最后读取 8 个 寄存器的值
即为生成的校验码。
其中对于寄存器的每一个时钟上升沿,值为:
基于FPGA的千兆以太网的实现(3)_第5张图片
其中 n 为当前输入的比特数。
当输入一个 8 比特数据后,输出寄存器应该为:
基于FPGA的千兆以太网的实现(3)_第6张图片
其中 X0-X7 为输入数据, I1-I8 为寄存器初始值。
从以上两种方式生成和校验中都可以完成 CRC 生成和解码,但就 FPGA 设计而言,实际
手算没能帮上什么忙,移位寄存器是一个很好的选择。但对于输入数据的大小和速率,限制
了该方式,为什么呢?当输入数据为 8 比特,则我们就得增加 8 倍时钟的速率来处理。这大
大降低了工作效率。而最后的并行加速处理后。每输入 8 比特数据,只要 1 个时钟就可以处
理好全部的内容。 速度提升,也不会给输入系统带来瓶颈。

CRC32 8 位输入校验推算过程

基于FPGA的千兆以太网的实现(3)_第7张图片
以上 0-31 是 crc 寄存器,以下记做 C0-C31;
基于FPGA的千兆以太网的实现(3)_第8张图片
基于FPGA的千兆以太网的实现(3)_第9张图片
基于FPGA的千兆以太网的实现(3)_第10张图片
基于FPGA的千兆以太网的实现(3)_第11张图片
基于FPGA的千兆以太网的实现(3)_第12张图片
化简相同的 D 去掉:
基于FPGA的千兆以太网的实现(3)_第13张图片
经过上面的推导,一个八位数经过一个时钟周期便可以出来检验码。接下来给出我们的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 					<=			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)