CRC循环冗余校验(比较全面从校验原理到FPGA程序设计)

一.前言

今年6月份刚换了家新公司,和之前做的项目完全不搭噶,之前做的仪器仪表,现在的公司是专业做音频相关,主要用的是数字网络音频技术,然而一窍不通的我又需要恶补许多知识,今天在看光纤 GTX应用相关的知识时,偶然又看到当初研究的CRC校验,FPGA上我们主要是在以太网发送报文和接收报文时需要对报文进行CRC校验,以确保我们接收的数据是正确的,关于以太网发送接收的知识这里不多做介绍,后面有时间再记录以太网发送接收相关的知识;今天顺便把当初研究CRC校验的过程写下来,刚开始也是从CRC计算器着手了解相关的规则,然后再着手FPGA程序。这里也多谢广大博主,也看了许多博主的文章,其中还是有个疑惑点,大家可以帮忙想想,在下文会详细提到,当然这个问题并补影响我们做FPGA设计,好了进入正题。

二.CRC计算器

1.在线计算器可以点击此处:CRC(循环冗余校验)在线计算器
2.CRC Calculator 应用程序是这样的,第一次记录博客也不知道咋放应用程序,想要的可以私信我。
在这里插入图片描述

三.CRC常用的参考模型

在这里插入图片描述
CRC在线计算器里面也有详细介绍,点击此处:CRC(循环冗余校验)在线计算器,里面也有详细的介绍。

四.参考模型中多项式值的由来

多项式的值是将多项式的系数提取出来,组成一串二进制的数。

例如crc-4:

名称 生成多项式 数值
CRC-4 x4+x+1 10011

多项式x4+x+1里面有很多项的 系数是0,写全就是

1* x4 +0* x3+0* x2+1* x1+1* x0

五.模二除法

模二除法本质上就是异或的运算,对应位置相同为0,不同为1,这是整个过程的运算规则。下图是一个简单的例程,除数11001是根据多项式x4+x3+1(此多项式参考模型没有)得到的,被除数是10110011,注意到到被除数后面加的4个0没,我们多项式最高次幂是多少,这里就加多少个0,说白了就是我们用CRC-(多少)就需要在待校验的值后面加多少个0,然后整个运算过程就用异或运算,最终得到的余数就是CRC校验最终的值FCS,这里还要注意余数是二进制100,因为是crc-4,余数不足4位,所以要在最高位补零。(图是广大博主的文章上复制的,还请谅)

注意注意注意,这里强调3遍哈,这里的例程计算仅是展示模二除法的运算过程的,我们并没有对被除数和余数再做处理,在CRC常用的参考模型中还有 初始值、结果异或值、输入反转、输出反转规则。
在这里插入图片描述

六.CRC算法模型中重要参数

结合crc参考模型,我们在这里重点讲几个重要的参数,也是和我们设计FPGA程序有关的

初始值INT : 这是算法开始时寄存器(crc)的初始化预置值,它的值为全0或者全F,当全为0时, 在算法开始前将需要校验的数据后面补上CRC位数个0后就可以进行后续计算了。当全为1时, 表示在算法开始前对数据的前CRC位数(高位)先和对应位数个1进行异或(即:前CRC位数的值按位取反),再在后面补上CRC位数个0,才进行后续计算。
输入反转REFIN: 当REFIN为true时,待校验的数据需要 以 每个字节按位反转的规则进行处理,当REFIN为False时,则不需要处理。(图是广大博主的文章上复制的,还请谅)
在这里插入图片描述
输出反转REFOUT(计算器): 在计算后之后,异或输出之前,整个数据是否按位反转。(True为需要,False为不需要)(这里输出反转我加了 (计算器) 是因为FPGA程序设计不是按这个规则来的,而是按照输入反转的规则来的,这也是我不明白的地方,看了黑金和正点原子例程都是这么做的)(图是广大博主的文章上复制的,还请谅)
在这里插入图片描述
结果异或值XOROUT: 计算结果与 全0或者全1 异或后得到最终的CRC值。 (在写FPGA程序时,结果异或值为全1时,就是将计算结果按位取反处理,结果异或值为全0时,就是计算结果本身)

七.CRC-32校验(FPGA运算程序自己项目中的)

上面我们对CRC校验规则做了讲解,理解了规则后在理解FPGA程序就很容易了,我们在做FPGA时基本都是用的CRC-32。CRC32 的原理与公式推导较复杂, 在此可不比深究, CRC 校验的源代码可
直接通过网页生成工具直接下载,网址: http://www.easics.com/webtools/crctool,目前网址已经失效,easics是国外的公司,可能由于美国对中国的制裁,国内登录不上去了,自己猜的,嘿嘿。但是影响不大,这些计算程序都是现成的,可以直接套用,下面上两种CRC计算代码。CRC-32参考上面的模型,初始值为32'hFFFFFFFF,结果异或值为32'hFFFFFFFF,输入反转为true,输出反转为true

1.函数类型CRC计算代码

因为是公司的项目程序只贴了比较相关的部分:状态机发送UDP的header、UDP数据部分、发送CRC校验值、 函数类型的CRC计算代码。(CRC校验是从以太网帧的帧头开始到数据最后结束,不包括前导码、帧起始界定符、fcs)
寄存器fcs在初始化时我们已经赋值为 fcs<=32'hffffffff;也就是我们所说的规则初始为全1的情况;在主程序中我们没有对输入数据进行反转,那是因为我们的函数类型的CRC计算代码中已经做了处理,待会儿你们可以和黑金的程序作对比
输出结果时,我们的程序写法是eth_tx_d <= ~{fcs[24],fcs[25],fcs[26],fcs[27],fcs[28],fcs[29],fcs[30],fcs[31]};从高位开始以字节为单位反转,再取反(结果异或处理)。(这里反转就是用的计算器输入反转的规则)

// An highlighted block
  SEND_UDP_HEADER: begin
                eth_tx_d <= `SLICE(udp_header, i);
					 fcs      <= compute_crc(fcs, `SLICE(udp_header, i));
           //     crc_data <= `SLICE(udp_header, i);
                if (i > 0) begin
                    i <= i - 1;
                end else begin
                    if (payload_nibbles != 0) begin
                        fifo_rd_en <= 1;
                        i          <= payload_nibbles - 1;
                        state      <= SEND_PAYLOAD;
                    end else begin
                        i     <= padding_nibbles - 1;
                        state <= SEND_PADDING;
                    end
                end
            end
   SEND_PAYLOAD: begin
                eth_tx_d <= fifo_dout;
                if (i > 0) begin
               //     crc_data <=fifo_dout;
						   fcs <= compute_crc(fcs, fifo_dout);
                    i   <= i - 1;
                end else begin
                    if (padding_nibbles != 0) begin
						           fcs <= compute_crc(fcs, fifo_dout);
                    //    crc_data   <= fifo_dout;
                        fifo_rd_en <= 0;
                        i          <= padding_nibbles - 1;
                        state      <= SEND_PADDING;
                    end else begin
                    //    crc_data   <= fifo_dout;
								 fcs <= compute_crc(fcs, fifo_dout);
                        fifo_rd_en <= 0;
                        i          <= FCS_NIBBLES - 1;
                        state      <= SEND_FCS;
                    end
                end
            end
    SEND_FCS: begin
				    crc_en   <=0;
            //    eth_tx_d <= `SLICE(fcs, i);
                eth_tx_d <= ~{
    
    fcs[24],fcs[25],fcs[26],fcs[27],fcs[28],fcs[29],fcs[30],fcs[31]};
                fcs <= {
    
    fcs[23:0], 8'hff};    
                if (i > 0) begin
                    i <= i - 1;
                end else begin
                    i         <= 32'd0;
                    state     <= WAIT;
                end
            end


 function  [31:0] compute_crc(
        input  [31:0] crc,
        input  [7:0] data);
        begin
        compute_crc[0]=crc[24]^crc[30]^data[1]^data[7];
        compute_crc[1]=crc[25]^crc[31]^data[0]^data[6]^crc[24]^crc[30]^data[1]^data[7];
        compute_crc[2]=crc[26]^data[5]^crc[25]^crc[31]^data[0]^data[6]^crc[24]^crc[30]^data[1]^data[7];
        compute_crc[3]=crc[27]^data[4]^crc[26]^data[5]^crc[25]^crc[31]^data[0]^data[6];
        compute_crc[4]=crc[28]^data[3]^crc[27]^data[4]^crc[26]^data[5]^crc[24]^crc[30]^data[1]^data[7];
        compute_crc[5]=crc[29]^data[2]^crc[28]^data[3]^crc[27]^data[4]^crc[25]^crc[31]^data[0]^data[6]^crc[24]^crc[30]^data[1]^data[7];
        compute_crc[6]=crc[30]^data[1]^crc[29]^data[2]^crc[28]^data[3]^crc[26]^data[5]^crc[25]^crc[31]^data[0]^data[6];
        compute_crc[7]=crc[31]^data[0]^crc[29]^data[2]^crc[27]^data[4]^crc[26]^data[5]^crc[24]^data[7];
        compute_crc[8]=crc[0]^crc[28]^data[3]^crc[27]^data[4]^crc[25]^data[6]^crc[24]^data[7];
        compute_crc[9]=crc[1]^crc[29]^data[2]^crc[28]^data[3]^crc[26]^data[5]^crc[25]^data[6];
        compute_crc[10]=crc[2]^crc[29]^data[2]^crc[27]^data[4]^crc[26]^data[5]^crc[24]^data[7];
        compute_crc[11]=crc[3]^crc[28]^data[3]^crc[27]^data[4]^crc[25]^data[6]^crc[24]^data[7];
        compute_crc[12]=crc[4]^crc[29]^data[2]^crc[28]^data[3]^crc[26]^data[5]^crc[25]^data[6]^crc[24]^crc[30]^data[1]^data[7];
        compute_crc[13]=crc[5]^crc[30]^data[1]^crc[29]^data[2]^crc[27]^data[4]^crc[26]^data[5]^crc[25]^crc[31]^data[0]^data[6];
        compute_crc[14]=crc[6]^crc[31]^data[0]^crc[30]^data[1]^crc[28]^data[3]^crc[27]^data[4]^crc[26]^data[5];
        compute_crc[15]=crc[7]^crc[31]^data[0]^crc[29]^data[2]^crc[28]^data[3]^crc[27]^data[4];
        compute_crc[16]=crc[8]^crc[29]^data[2]^crc[28]^data[3]^crc[24]^data[7];
        compute_crc[17]=crc[9]^crc[30]^data[1]^crc[29]^data[2]^crc[25]^data[6];
        compute_crc[18]=crc[10]^crc[31]^data[0]^crc[30]^data[1]^crc[26]^data[5];
        compute_crc[19]=crc[11]^crc[31]^data[0]^crc[27]^data[4];
        compute_crc[20]=crc[12]^crc[28]^data[3];
        compute_crc[21]=crc[13]^crc[29]^data[2];
        compute_crc[22]=crc[14]^crc[24]^data[7];
        compute_crc[23]=crc[15]^crc[25]^data[6]^crc[24]^crc[30]^data[1]^data[7];
        compute_crc[24]=crc[16]^crc[26]^data[5]^crc[25]^crc[31]^data[0]^data[6];
        compute_crc[25]=crc[17]^crc[27]^data[4]^crc[26]^data[5];
        compute_crc[26]=crc[18]^crc[28]^data[3]^crc[27]^data[4]^crc[24]^crc[30]^data[1]^data[7];
        compute_crc[27]=crc[19]^crc[29]^data[2]^crc[28]^data[3]^crc[25]^crc[31]^data[0]^data[6];
        compute_crc[28]=crc[20]^crc[30]^data[1]^crc[29]^data[2]^crc[26]^data[5];
        compute_crc[29]=crc[21]^crc[31]^data[0]^crc[30]^data[1]^crc[27]^data[4];
        compute_crc[30]=crc[22]^crc[31]^data[0]^crc[28]^data[3];
        compute_crc[31]=crc[23]^crc[29]^data[2];
		  		 end

2.时序类型CRC计算代码

此处是CRC-32的时序类型计算代码部分,其中有CRC-32校验的使能,此种写法时序比较紧凑,适合在RMII中应用;函数类型在RGMII中应用时序就不会紧凑。

// An highlighted block

wire [7:0] crc_data_out_d1 = ~{
    
    crc_data_out[24],crc_data_out[25],crc_data_out[26],crc_data_out[27],crc_data_out[28],crc_data_out[29],crc_data_out[30],crc_data_out[31]};
wire [7:0] crc_data_out_d2 = ~{
    
    crc_data_out[16],crc_data_out[17],crc_data_out[18],crc_data_out[19],crc_data_out[20],crc_data_out[21],crc_data_out[22],crc_data_out[23]};
wire [7:0] crc_data_out_d3 = ~{
    
    crc_data_out[8],crc_data_out[9],crc_data_out[10],crc_data_out[11],crc_data_out[12],crc_data_out[13],crc_data_out[14],crc_data_out[15]};
wire [7:0] crc_data_out_d4 = ~{
    
    crc_data_out[0],crc_data_out[1],crc_data_out[2],crc_data_out[3],crc_data_out[4],crc_data_out[5],crc_data_out[6],crc_data_out[7]};
wire [31:0] fcs={
    
    crc_data_out_d1,crc_data_out_d2,crc_data_out_d3,crc_data_out_d4};

   wire [31:0] crc_data_out;
  //
   crc_calc crc_calc_inst
(
    .reset(crc_rst),
	.clk(~clk125),
	.CRC32_en(crc_en),         //CRC校验使能信号
	.CRC32_init(0),       //CRC校验值初始化信号
	 //input wire  CRC_result_read,    //读CRC校验判断结果
	 //input   CRC32_valid,      //CRC校验值维持有效
    .data(crc_data),	 
	.CRC_data(crc_data_out)  

);
module crc_calc(
    input wire  reset,
	 input wire  clk,
	 input wire  CRC32_en,         //CRC校验使能信号
	 input wire  CRC32_init,       //CRC校验值初始化信号
	 //input wire  CRC_result_read,    //读CRC校验判断结果
	 //input   CRC32_valid,      //CRC校验值维持有效
    input wire [7:0]   data,
	 
	 output wire [31:0]   CRC_data    	 
    );

reg [31:0]   CRC_temp;

assign CRC_data = CRC_temp;
//assign CRC_bad = (CRC_result_read & (CRC_temp == 32'hc704dd7b)) ? 1'b1 : 1'b0;			  
    

always@(posedge clk or posedge reset)         
   if(reset)
	    CRC_temp <= 32'hffffffff;		  
   else if(CRC32_init)
	    CRC_temp <= 32'hffffffff;
   else if(CRC32_en)
	  begin
		 CRC_temp[0]<=CRC_temp[24]^CRC_temp[30]^data[1]^data[7];
		 CRC_temp[1]<=CRC_temp[25]^CRC_temp[31]^data[0]^data[6]^CRC_temp[24]^CRC_temp[30]^data[1]^data[7];
		 CRC_temp[2]<=CRC_temp[26]^data[5]^CRC_temp[25]^CRC_temp[31]^data[0]^data[6]^CRC_temp[24]^CRC_temp[30]^data[1]^data[7];
		 CRC_temp[3]<=CRC_temp[27]^data[4]^CRC_temp[26]^data[5]^CRC_temp[25]^CRC_temp[31]^data[0]^data[6];
		 CRC_temp[4]<=CRC_temp[28]^data[3]^CRC_temp[27]^data[4]^CRC_temp[26]^data[5]^CRC_temp[24]^CRC_temp[30]^data[1]^data[7];
		 CRC_temp[5]<=CRC_temp[29]^data[2]^CRC_temp[28]^data[3]^CRC_temp[27]^data[4]^CRC_temp[25]^CRC_temp[31]^data[0]^data[6]^CRC_temp[24]^CRC_temp[30]^data[1]^data[7];
		 CRC_temp[6]<=CRC_temp[30]^data[1]^CRC_temp[29]^data[2]^CRC_temp[28]^data[3]^CRC_temp[26]^data[5]^CRC_temp[25]^CRC_temp[31]^data[0]^data[6];
		 CRC_temp[7]<=CRC_temp[31]^data[0]^CRC_temp[29]^data[2]^CRC_temp[27]^data[4]^CRC_temp[26]^data[5]^CRC_temp[24]^data[7];
		 CRC_temp[8]<=CRC_temp[0]^CRC_temp[28]^data[3]^CRC_temp[27]^data[4]^CRC_temp[25]^data[6]^CRC_temp[24]^data[7];
		 CRC_temp[9]<=CRC_temp[1]^CRC_temp[29]^data[2]^CRC_temp[28]^data[3]^CRC_temp[26]^data[5]^CRC_temp[25]^data[6];
		 CRC_temp[10]<=CRC_temp[2]^CRC_temp[29]^data[2]^CRC_temp[27]^data[4]^CRC_temp[26]^data[5]^CRC_temp[24]^data[7];
		 CRC_temp[11]<=CRC_temp[3]^CRC_temp[28]^data[3]^CRC_temp[27]^data[4]^CRC_temp[25]^data[6]^CRC_temp[24]^data[7];
		 CRC_temp[12]<=CRC_temp[4]^CRC_temp[29]^data[2]^CRC_temp[28]^data[3]^CRC_temp[26]^data[5]^CRC_temp[25]^data[6]^CRC_temp[24]^CRC_temp[30]^data[1]^data[7];
		 CRC_temp[13]<=CRC_temp[5]^CRC_temp[30]^data[1]^CRC_temp[29]^data[2]^CRC_temp[27]^data[4]^CRC_temp[26]^data[5]^CRC_temp[25]^CRC_temp[31]^data[0]^data[6];
		 CRC_temp[14]<=CRC_temp[6]^CRC_temp[31]^data[0]^CRC_temp[30]^data[1]^CRC_temp[28]^data[3]^CRC_temp[27]^data[4]^CRC_temp[26]^data[5];
		 CRC_temp[15]<=CRC_temp[7]^CRC_temp[31]^data[0]^CRC_temp[29]^data[2]^CRC_temp[28]^data[3]^CRC_temp[27]^data[4];
		 CRC_temp[16]<=CRC_temp[8]^CRC_temp[29]^data[2]^CRC_temp[28]^data[3]^CRC_temp[24]^data[7];
		 CRC_temp[17]<=CRC_temp[9]^CRC_temp[30]^data[1]^CRC_temp[29]^data[2]^CRC_temp[25]^data[6];
		 CRC_temp[18]<=CRC_temp[10]^CRC_temp[31]^data[0]^CRC_temp[30]^data[1]^CRC_temp[26]^data[5];
		 CRC_temp[19]<=CRC_temp[11]^CRC_temp[31]^data[0]^CRC_temp[27]^data[4];
		 CRC_temp[20]<=CRC_temp[12]^CRC_temp[28]^data[3];
		 CRC_temp[21]<=CRC_temp[13]^CRC_temp[29]^data[2];
		 CRC_temp[22]<=CRC_temp[14]^CRC_temp[24]^data[7];
		 CRC_temp[23]<=CRC_temp[15]^CRC_temp[25]^data[6]^CRC_temp[24]^CRC_temp[30]^data[1]^data[7];
		 CRC_temp[24]<=CRC_temp[16]^CRC_temp[26]^data[5]^CRC_temp[25]^CRC_temp[31]^data[0]^data[6];
		 CRC_temp[25]<=CRC_temp[17]^CRC_temp[27]^data[4]^CRC_temp[26]^data[5];
		 CRC_temp[26]<=CRC_temp[18]^CRC_temp[28]^data[3]^CRC_temp[27]^data[4]^CRC_temp[24]^CRC_temp[30]^data[1]^data[7];
		 CRC_temp[27]<=CRC_temp[19]^CRC_temp[29]^data[2]^CRC_temp[28]^data[3]^CRC_temp[25]^CRC_temp[31]^data[0]^data[6];
		 CRC_temp[28]<=CRC_temp[20]^CRC_temp[30]^data[1]^CRC_temp[29]^data[2]^CRC_temp[26]^data[5];
		 CRC_temp[29]<=CRC_temp[21]^CRC_temp[31]^data[0]^CRC_temp[30]^data[1]^CRC_temp[27]^data[4];
		 CRC_temp[30]<=CRC_temp[22]^CRC_temp[31]^data[0]^CRC_temp[28]^data[3];
		 CRC_temp[31]<=CRC_temp[23]^CRC_temp[29]^data[2];
	  end
endmodule

八.黑金的CRC-32计算代码

此处只贴了两部分代码,一部分是状态机发送CRC-32校验结果的部分,另一部分是CRC-32计算部分。
看程序输出结果的反转是按照从FCS高位开始以字节为单位反转,再取反(结果异或处理)。(这里反转也是用的计算器输入反转的规则)
注意啦,主程序是下降沿判断,CRC计算模块是上升沿判断这里有半个时钟周期差。所以时序也紧凑。

// An highlighted block
always@(negedge clk) ///下降沿判断
............///省略代码
	sendcrc: begin                              //发送32位的crc校验
				crcen<=1'b0;
				if(i==0)	begin
					  dataout[7:0] <= {
    
    ~crc[24], ~crc[25], ~crc[26], ~crc[27], ~crc[28], ~crc[29], ~crc[30], ~crc[31]};
					  i<=i+1'b1;
					end
				else begin
				  if(i==1) begin
					   dataout[7:0]<={
    
    ~crc[16], ~crc[17], ~crc[18], ~crc[19], ~crc[20], ~crc[21], ~crc[22], ~crc[23]};
						i<=i+1'b1;
				  end
				  else if(i==2) begin
					   dataout[7:0]<={
    
    ~crc[8], ~crc[9], ~crc[10], ~crc[11], ~crc[12], ~crc[13], ~crc[14], ~crc[15]};
						i<=i+1'b1;
				  end
				  else if(i==3) begin
					   dataout[7:0]<={
    
    ~crc[0], ~crc[1], ~crc[2], ~crc[3], ~crc[4], ~crc[5], ~crc[6], ~crc[7]};
						i<=0;
						tx_state<=idle;
				  end
				  else begin
                  txer<=1'b1;
				  end
				end
			end					
..........//省略代码
end

module crc (Clk, Reset, Data_in, Enable, Crc,CrcNext);


parameter Tp = 1;

input Clk;
input Reset;
input [7:0] Data_in;
input Enable;

output [31:0] Crc;
reg  [31:0] Crc;

output [31:0] CrcNext;

wire [7:0] Data;

assign Data={
    
    Data_in[0],Data_in[1],Data_in[2],Data_in[3],Data_in[4],Data_in[5],Data_in[6],Data_in[7]};


assign CrcNext[0] = Crc[24] ^ Crc[30] ^ Data[0] ^ Data[6];
assign CrcNext[1] = Crc[24] ^ Crc[25] ^ Crc[30] ^ Crc[31] ^ Data[0] ^ Data[1] ^ Data[6] ^ Data[7];
assign CrcNext[2] = Crc[24] ^ Crc[25] ^ Crc[26] ^ Crc[30] ^ Crc[31] ^ Data[0] ^ Data[1] ^ Data[2] ^ Data[6] ^ Data[7];
assign CrcNext[3] = Crc[25] ^ Crc[26] ^ Crc[27] ^ Crc[31] ^ Data[1] ^ Data[2] ^ Data[3] ^ Data[7];
assign CrcNext[4] = Crc[24] ^ Crc[26] ^ Crc[27] ^ Crc[28] ^ Crc[30] ^ Data[0] ^ Data[2] ^ Data[3] ^ Data[4] ^ Data[6];
assign CrcNext[5] = Crc[24] ^ Crc[25] ^ Crc[27] ^ Crc[28] ^ Crc[29] ^ Crc[30] ^ Crc[31] ^ Data[0] ^ Data[1] ^ Data[3] ^ Data[4] ^ Data[5] ^ Data[6] ^ Data[7];
assign CrcNext[6] = Crc[25] ^ Crc[26] ^ Crc[28] ^ Crc[29] ^ Crc[30] ^ Crc[31] ^ Data[1] ^ Data[2] ^ Data[4] ^ Data[5] ^ Data[6] ^ Data[7];
assign CrcNext[7] = Crc[24] ^ Crc[26] ^ Crc[27] ^ Crc[29] ^ Crc[31] ^ Data[0] ^ Data[2] ^ Data[3] ^ Data[5] ^ Data[7];
assign CrcNext[8] = Crc[0] ^ Crc[24] ^ Crc[25] ^ Crc[27] ^ Crc[28] ^ Data[0] ^ Data[1] ^ Data[3] ^ Data[4];
assign CrcNext[9] = Crc[1] ^ Crc[25] ^ Crc[26] ^ Crc[28] ^ Crc[29] ^ Data[1] ^ Data[2] ^ Data[4] ^ Data[5];
assign CrcNext[10] = Crc[2] ^ Crc[24] ^ Crc[26] ^ Crc[27] ^ Crc[29] ^ Data[0] ^ Data[2] ^ Data[3] ^ Data[5];
assign CrcNext[11] = Crc[3] ^ Crc[24] ^ Crc[25] ^ Crc[27] ^ Crc[28] ^ Data[0] ^ Data[1] ^ Data[3] ^ Data[4];
assign CrcNext[12] = Crc[4] ^ Crc[24] ^ Crc[25] ^ Crc[26] ^ Crc[28] ^ Crc[29] ^ Crc[30] ^ Data[0] ^ Data[1] ^ Data[2] ^ Data[4] ^ Data[5] ^ Data[6];
assign CrcNext[13] = Crc[5] ^ Crc[25] ^ Crc[26] ^ Crc[27] ^ Crc[29] ^ Crc[30] ^ Crc[31] ^ Data[1] ^ Data[2] ^ Data[3] ^ Data[5] ^ Data[6] ^ Data[7];
assign CrcNext[14] = Crc[6] ^ Crc[26] ^ Crc[27] ^ Crc[28] ^ Crc[30] ^ Crc[31] ^ Data[2] ^ Data[3] ^ Data[4] ^ Data[6] ^ Data[7];
assign CrcNext[15] =  Crc[7] ^ Crc[27] ^ Crc[28] ^ Crc[29] ^ Crc[31] ^ Data[3] ^ Data[4] ^ Data[5] ^ Data[7];
assign CrcNext[16] = Crc[8] ^ Crc[24] ^ Crc[28] ^ Crc[29] ^ Data[0] ^ Data[4] ^ Data[5];
assign CrcNext[17] = Crc[9] ^ Crc[25] ^ Crc[29] ^ Crc[30] ^ Data[1] ^ Data[5] ^ Data[6];
assign CrcNext[18] = Crc[10] ^ Crc[26] ^ Crc[30] ^ Crc[31] ^ Data[2] ^ Data[6] ^ Data[7];
assign CrcNext[19] = Crc[11] ^ Crc[27] ^ Crc[31] ^ Data[3] ^ Data[7];
assign CrcNext[20] = Crc[12] ^ Crc[28] ^ Data[4];
assign CrcNext[21] = Crc[13] ^ Crc[29] ^ Data[5];
assign CrcNext[22] = Crc[14] ^ Crc[24] ^ Data[0];
assign CrcNext[23] = Crc[15] ^ Crc[24] ^ Crc[25] ^ Crc[30] ^ Data[0] ^ Data[1] ^ Data[6];
assign CrcNext[24] = Crc[16] ^ Crc[25] ^ Crc[26] ^ Crc[31] ^ Data[1] ^ Data[2] ^ Data[7];
assign CrcNext[25] = Crc[17] ^ Crc[26] ^ Crc[27] ^ Data[2] ^ Data[3];
assign CrcNext[26] = Crc[18] ^ Crc[24] ^ Crc[27] ^ Crc[28] ^ Crc[30] ^ Data[0] ^ Data[3] ^ Data[4] ^ Data[6];
assign CrcNext[27] = Crc[19] ^ Crc[25] ^ Crc[28] ^ Crc[29] ^ Crc[31] ^ Data[1] ^ Data[4] ^ Data[5] ^ Data[7];
assign CrcNext[28] = Crc[20] ^ Crc[26] ^ Crc[29] ^ Crc[30] ^ Data[2] ^ Data[5] ^ Data[6];
assign CrcNext[29] = Crc[21] ^ Crc[27] ^ Crc[30] ^ Crc[31] ^ Data[3] ^ Data[6] ^ Data[7];
assign CrcNext[30] = Crc[22] ^ Crc[28] ^ Crc[31] ^ Data[4] ^ Data[7];
assign CrcNext[31] = Crc[23] ^ Crc[29] ^ Data[5];

always @ (posedge Clk, posedge Reset)
 begin
  if (Reset) begin
    Crc <={
    
    32{
    
    1'b1}};
  end
   else if (Enable)
    Crc <=CrcNext;
 end
endmodule

九.正点原子的CRC-32 计算代码

此处也只贴了两部分代码,一部分是状态机发送CRC-32校验结果的部分,另一部分是CRC-32计算部分。

看程序输出结果的反转是按照从FCS高位开始以字节为单位反转,再取反(结果异或处理)。(这里反转也是用的计算器输入反转的规则)

注意啦,这里和黑金程序有差异,因为主程序和CRC-32计算程序都是上升沿判断,所以crc_32计算程序是慢一个时钟节拍的,所以第一个clk gmii_tx发送的crc_next的值,因为crc_next是wire类型,所以没有时钟差,至于取crc_next[7:0],你们可以仿真就知道了。

// An highlighted block

always @(posedge clk or negedge rst_n) begin
.....................//代码省略
          st_crc      : begin                          //发送CRC校验值
                gmii_tx_en <= 1'b1;
                cnt <= cnt + 1'b1;
                if(cnt == 6'd0)
                    gmii_txd <= {
    
    ~crc_next[0], ~crc_next[1], ~crc_next[2],~crc_next[3],
                                 ~crc_next[4], ~crc_next[5], ~crc_next[6],~crc_next[7]};
                else if(cnt == 6'd1)
                    gmii_txd <= {
    
    ~crc_data[16], ~crc_data[17], ~crc_data[18],
                                 ~crc_data[19], ~crc_data[20], ~crc_data[21], 
                                 ~crc_data[22],~crc_data[23]};
                else if(cnt == 6'd2) begin
                    gmii_txd <= {
    
    ~crc_data[8], ~crc_data[9], ~crc_data[10],
                                 ~crc_data[11],~crc_data[12], ~crc_data[13], 
                                 ~crc_data[14],~crc_data[15]};                              
                end
                else if(cnt == 6'd3) begin
                    gmii_txd <= {
    
    ~crc_data[0], ~crc_data[1], ~crc_data[2],~crc_data[3],
                                 ~crc_data[4], ~crc_data[5], ~crc_data[6],~crc_data[7]};  
                    tx_done_t <= 1'b1;
                    skip_en <= 1'b1;
                    cnt <= 1'b0;
                end                                                                                                                                            
            end      
.....................//代码省略
end

module crc32_d8(
    input                 clk     ,  //时钟信号
    input                 rst_n   ,  //复位信号,低电平有效
    input         [7:0]   data    ,  //输入待校验8位数据
    input                 crc_en  ,  //crc使能,开始校验标志
    input                 crc_clr ,  //crc数据复位信号            
    output   reg  [31:0]  crc_data,  //CRC校验数据
    output        [31:0]  crc_next   //CRC下次校验完成数据
    );

//*****************************************************
//**                    main code
//*****************************************************

//输入待校验8位数据,需要先将高低位互换
wire    [7:0]  data_t;

assign data_t = {
    
    data[0],data[1],data[2],data[3],data[4],data[5],data[6],data[7]};

//CRC32的生成多项式为:G(x)= x^32 + x^26 + x^23 + x^22 + x^16 + x^12 + x^11 
//+ x^10 + x^8 + x^7 + x^5 + x^4 + x^2 + x^1 + 1

assign crc_next[0] = crc_data[24] ^ crc_data[30] ^ data_t[0] ^ data_t[6];
assign crc_next[1] = crc_data[24] ^ crc_data[25] ^ crc_data[30] ^ crc_data[31] 
                     ^ data_t[0] ^ data_t[1] ^ data_t[6] ^ data_t[7];
assign crc_next[2] = crc_data[24] ^ crc_data[25] ^ crc_data[26] ^ crc_data[30] 
                     ^ crc_data[31] ^ data_t[0] ^ data_t[1] ^ data_t[2] ^ data_t[6] 
                     ^ data_t[7];
assign crc_next[3] = crc_data[25] ^ crc_data[26] ^ crc_data[27] ^ crc_data[31] 
                     ^ data_t[1] ^ data_t[2] ^ data_t[3] ^ data_t[7];
assign crc_next[4] = crc_data[24] ^ crc_data[26] ^ crc_data[27] ^ crc_data[28] 
                     ^ crc_data[30] ^ data_t[0] ^ data_t[2] ^ data_t[3] ^ data_t[4] 
                     ^ data_t[6];
assign crc_next[5] = crc_data[24] ^ crc_data[25] ^ crc_data[27] ^ crc_data[28] 
                     ^ crc_data[29] ^ crc_data[30] ^ crc_data[31] ^ data_t[0] 
                     ^ data_t[1] ^ data_t[3] ^ data_t[4] ^ data_t[5] ^ data_t[6] 
                     ^ data_t[7];
assign crc_next[6] = crc_data[25] ^ crc_data[26] ^ crc_data[28] ^ crc_data[29] 
                     ^ crc_data[30] ^ crc_data[31] ^ data_t[1] ^ data_t[2] ^ data_t[4] 
                     ^ data_t[5] ^ data_t[6] ^ data_t[7];
assign crc_next[7] = crc_data[24] ^ crc_data[26] ^ crc_data[27] ^ crc_data[29] 
                     ^ crc_data[31] ^ data_t[0] ^ data_t[2] ^ data_t[3] ^ data_t[5] 
                     ^ data_t[7];
assign crc_next[8] = crc_data[0] ^ crc_data[24] ^ crc_data[25] ^ crc_data[27] 
                     ^ crc_data[28] ^ data_t[0] ^ data_t[1] ^ data_t[3] ^ data_t[4];
assign crc_next[9] = crc_data[1] ^ crc_data[25] ^ crc_data[26] ^ crc_data[28] 
                     ^ crc_data[29] ^ data_t[1] ^ data_t[2] ^ data_t[4] ^ data_t[5];
assign crc_next[10] = crc_data[2] ^ crc_data[24] ^ crc_data[26] ^ crc_data[27] 
                     ^ crc_data[29] ^ data_t[0] ^ data_t[2] ^ data_t[3] ^ data_t[5];
assign crc_next[11] = crc_data[3] ^ crc_data[24] ^ crc_data[25] ^ crc_data[27] 
                     ^ crc_data[28] ^ data_t[0] ^ data_t[1] ^ data_t[3] ^ data_t[4];
assign crc_next[12] = crc_data[4] ^ crc_data[24] ^ crc_data[25] ^ crc_data[26] 
                     ^ crc_data[28] ^ crc_data[29] ^ crc_data[30] ^ data_t[0] 
                     ^ data_t[1] ^ data_t[2] ^ data_t[4] ^ data_t[5] ^ data_t[6];
assign crc_next[13] = crc_data[5] ^ crc_data[25] ^ crc_data[26] ^ crc_data[27] 
                     ^ crc_data[29] ^ crc_data[30] ^ crc_data[31] ^ data_t[1] 
                     ^ data_t[2] ^ data_t[3] ^ data_t[5] ^ data_t[6] ^ data_t[7];
assign crc_next[14] = crc_data[6] ^ crc_data[26] ^ crc_data[27] ^ crc_data[28] 
                     ^ crc_data[30] ^ crc_data[31] ^ data_t[2] ^ data_t[3] ^ data_t[4]
                     ^ data_t[6] ^ data_t[7];
assign crc_next[15] =  crc_data[7] ^ crc_data[27] ^ crc_data[28] ^ crc_data[29]
                     ^ crc_data[31] ^ data_t[3] ^ data_t[4] ^ data_t[5] ^ data_t[7];
assign crc_next[16] = crc_data[8] ^ crc_data[24] ^ crc_data[28] ^ crc_data[29] 
                     ^ data_t[0] ^ data_t[4] ^ data_t[5];
assign crc_next[17] = crc_data[9] ^ crc_data[25] ^ crc_data[29] ^ crc_data[30] 
                     ^ data_t[1] ^ data_t[5] ^ data_t[6];
assign crc_next[18] = crc_data[10] ^ crc_data[26] ^ crc_data[30] ^ crc_data[31] 
                     ^ data_t[2] ^ data_t[6] ^ data_t[7];
assign crc_next[19] = crc_data[11] ^ crc_data[27] ^ crc_data[31] ^ data_t[3] ^ data_t[7];
assign crc_next[20] = crc_data[12] ^ crc_data[28] ^ data_t[4];
assign crc_next[21] = crc_data[13] ^ crc_data[29] ^ data_t[5];
assign crc_next[22] = crc_data[14] ^ crc_data[24] ^ data_t[0];
assign crc_next[23] = crc_data[15] ^ crc_data[24] ^ crc_data[25] ^ crc_data[30] 
                      ^ data_t[0] ^ data_t[1] ^ data_t[6];
assign crc_next[24] = crc_data[16] ^ crc_data[25] ^ crc_data[26] ^ crc_data[31] 
                      ^ data_t[1] ^ data_t[2] ^ data_t[7];
assign crc_next[25] = crc_data[17] ^ crc_data[26] ^ crc_data[27] ^ data_t[2] ^ data_t[3];
assign crc_next[26] = crc_data[18] ^ crc_data[24] ^ crc_data[27] ^ crc_data[28] 
                      ^ crc_data[30] ^ data_t[0] ^ data_t[3] ^ data_t[4] ^ data_t[6];
assign crc_next[27] = crc_data[19] ^ crc_data[25] ^ crc_data[28] ^ crc_data[29] 
                      ^ crc_data[31] ^ data_t[1] ^ data_t[4] ^ data_t[5] ^ data_t[7];
assign crc_next[28] = crc_data[20] ^ crc_data[26] ^ crc_data[29] ^ crc_data[30] 
                      ^ data_t[2] ^ data_t[5] ^ data_t[6];
assign crc_next[29] = crc_data[21] ^ crc_data[27] ^ crc_data[30] ^ crc_data[31] 
                      ^ data_t[3] ^ data_t[6] ^ data_t[7];
assign crc_next[30] = crc_data[22] ^ crc_data[28] ^ crc_data[31] ^ data_t[4] ^ data_t[7];
assign crc_next[31] = crc_data[23] ^ crc_data[29] ^ data_t[5];

always @(posedge clk or negedge rst_n) begin
    if(!rst_n)
        crc_data <= 32'hff_ff_ff_ff;
    else if(crc_clr)                             //CRC校验值复位
        crc_data <= 32'hff_ff_ff_ff;
    else if(crc_en)
        crc_data <= crc_next;
end

endmodule

十.CRC-32计算器与FPGA程序计算结果的对比

例如:我们需要检验的值为8'hAB

1.计算器计算的结果

在这里插入图片描述

2.FPGA计算的结果

看仿真图,以太网发送的端口是eth_tx_d[7:0],8’hAB经过程序输出的结果为32’hed 950693。(反转是按照从FCS高位开始以字节为单位反转,再取反)
端口crc32[31:0]的值是32’h93095ed。(反转是按照32位整体按位反转,再取反)
在这里插入图片描述

关于CRC校验的原理以及FPGA程序大概就这样,关于crc-32输出结果是整体32位反转还是从高位以字节为单位按位反转其实对设计的没影响,只要以太网接收方和发送方用的同样的规则就没问题,但是还是想知道下原因。

猜你喜欢

转载自blog.csdn.net/qq_43618170/article/details/127240087