CRC校验码以及Verilog代码实现

  CRC全称循环冗余检验(Cyclic Redundancy Check, CRC)在数据传输的领域应用广泛,是一种比较常用的检错方法,它是利用除法及余数的原理来作错误侦测的。

  貌似大学的课本《通信原理》讲过CRC的原理不过基本是以二进制的多项式形式来说明,对于毕业多年的社畜来说难以理解,下面就以最简单的方式讲讲CRC的工作原理。首先是CRC校验码产生的方式:模2除法,顾名思义就是2取模。假设需要求CRC的数据为Data,而这里选择的多项式为G(x) = x4 + x3 + 1;将该多项式转成对应的二进制码值为G(M) = 11001。CRC= Data mod G(M),该公式为取模值,余数肯定要比除数小,而且规定CRC位数始终比多项式的二进制表示少1位,所以这里产生的CRC应该是4位。

  模2除法的规则:既不向上位借位,也不比较除数和被除数的相同位数值的大小,只要以相同位数进行相除即可。模2加法运算为:1+1=0,0+1=1,0+0=0,无进位,也无借位;模2减法运算为:1-1=0,0-1=1,1-0=1,0-0=0,也无进位,无借位。等价于二进制中的按位异或运算。

  根据上述方式对数据1011001进行CRC编码,由于G(M)是11001,CRC为4位余数。则需要在被除数即1011001后面补上4个0,为101_1001_0000。根据模2计算,下图为计算过程,得到计算的余数即CRC = 1010。

  按位异或与模2计算的方式等价,下面为按位异或的结果,发现CRC也是1010。

  为进一步证明计算结果,可以通过相关工具验证计算结果。笔者通过在线工具进行验证,结果与我们计算结果一致。链接为http://www.ip33.com/crc.html

  当然也可以进行逆运算进行验证,把CRC码补到原数据后面即101_1001_1010,对多项式11001进行mod2,计算的结果余数为0。说明CRC结果正确。

 

 

 

  知道原理就进行编码,根据按位异或的操作编写Verilog代码其实已经很简单,笔者这里就不细做讲解了。就说说用工具自动生成Verilog的方式,毕竟根据不一样的位宽,多项式可以对同一个数据算出不同的CRC。用工具生成的方式既通用,又简单。

  通信领域有一些常用的多项式,如下表示所示:

   以太网的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

可以在百度云盘下载工具,链接:https://pan.baidu.com/s/1Aektliy33AGuabRbGec-1w  提取码:fwtw

也可以由网上CRC生成工具,跟上面工具生成的完全一致。相关链接CRC Generator

 1 //-----------------------------------------------------------------------------
 2 // Copyright (C) 2009 OutputLogic.com
 3 // This source file may be used and distributed without restriction
 4 // provided that this copyright statement is not removed from the file
 5 // and that any derivative work contains the original copyright notice
 6 // and the associated disclaimer.
 7 // THIS SOURCE FILE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS
 8 // OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
 9 // WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
10 //-----------------------------------------------------------------------------
11 // CRC module for
12 //       data[7:0]
13 //       crc[31:0]=1+x^1+x^2+x^4+x^5+x^7+x^8+x^10+x^11+x^12+x^16+x^22+x^23+x^26+x^32;
14 //
15 module crc(
16         input [7:0] data_in,
17         input        crc_en,
18         output [31:0] crc_out,
19         input        rst,
20         input        clk);
21 
22         reg [31:0] lfsr_q,
23                    lfsr_c;
24         assign crc_out = lfsr_q;
25         always @(*) begin
26                 lfsr_c[0] = lfsr_q[24] ^ lfsr_q[30] ^ data_in[0] ^ data_in[6];
27                 lfsr_c[1] = lfsr_q[24] ^ lfsr_q[25] ^ lfsr_q[30] ^ lfsr_q[31] ^ data_in[0] ^ data_in[1] ^ data_in[6] ^ data_in[7];
28                 lfsr_c[2] = lfsr_q[24] ^ lfsr_q[25] ^ lfsr_q[26] ^ lfsr_q[30] ^ lfsr_q[31] ^ data_in[0] ^ data_in[1] ^ data_in[2] ^ data_in[6] ^ data_in[7];
29                 lfsr_c[3] = lfsr_q[25] ^ lfsr_q[26] ^ lfsr_q[27] ^ lfsr_q[31] ^ data_in[1] ^ data_in[2] ^ data_in[3] ^ data_in[7];
30                 lfsr_c[4] = lfsr_q[24] ^ lfsr_q[26] ^ lfsr_q[27] ^ lfsr_q[28] ^ lfsr_q[30] ^ data_in[0] ^ data_in[2] ^ data_in[3] ^ data_in[4] ^ data_in[6];
31                 lfsr_c[5] = lfsr_q[24] ^ lfsr_q[25] ^ lfsr_q[27] ^ lfsr_q[28] ^ lfsr_q[29] ^ lfsr_q[30] ^ lfsr_q[31] ^ data_in[0] ^ data_in[1] ^ data_in[3] ^ data_in[4] ^ data_in[5] ^ data_in[6] ^ data_in[7];
32                 lfsr_c[6] = lfsr_q[25] ^ lfsr_q[26] ^ lfsr_q[28] ^ lfsr_q[29] ^ lfsr_q[30] ^ lfsr_q[31] ^ data_in[1] ^ data_in[2] ^ data_in[4] ^ data_in[5] ^ data_in[6] ^ data_in[7];
33                 lfsr_c[7] = lfsr_q[24] ^ lfsr_q[26] ^ lfsr_q[27] ^ lfsr_q[29] ^ lfsr_q[31] ^ data_in[0] ^ data_in[2] ^ data_in[3] ^ data_in[5] ^ data_in[7];
34                 lfsr_c[8] = lfsr_q[0] ^ lfsr_q[24] ^ lfsr_q[25] ^ lfsr_q[27] ^ lfsr_q[28] ^ data_in[0] ^ data_in[1] ^ data_in[3] ^ data_in[4];
35                 lfsr_c[9] = lfsr_q[1] ^ lfsr_q[25] ^ lfsr_q[26] ^ lfsr_q[28] ^ lfsr_q[29] ^ data_in[1] ^ data_in[2] ^ data_in[4] ^ data_in[5];
36                 lfsr_c[10] = lfsr_q[2] ^ lfsr_q[24] ^ lfsr_q[26] ^ lfsr_q[27] ^ lfsr_q[29] ^ data_in[0] ^ data_in[2] ^ data_in[3] ^ data_in[5];
37                 lfsr_c[11] = lfsr_q[3] ^ lfsr_q[24] ^ lfsr_q[25] ^ lfsr_q[27] ^ lfsr_q[28] ^ data_in[0] ^ data_in[1] ^ data_in[3] ^ data_in[4];
38                 lfsr_c[12] = lfsr_q[4] ^ lfsr_q[24] ^ lfsr_q[25] ^ lfsr_q[26] ^ lfsr_q[28] ^ lfsr_q[29] ^ lfsr_q[30] ^ data_in[0] ^ data_in[1] ^ data_in[2] ^ data_in[4] ^ data_in[5] ^ data_in[6];
39                 lfsr_c[13] = lfsr_q[5] ^ lfsr_q[25] ^ lfsr_q[26] ^ lfsr_q[27] ^ lfsr_q[29] ^ lfsr_q[30] ^ lfsr_q[31] ^ data_in[1] ^ data_in[2] ^ data_in[3] ^ data_in[5] ^ data_in[6] ^ data_in[7];
40                 lfsr_c[14] = lfsr_q[6] ^ lfsr_q[26] ^ lfsr_q[27] ^ lfsr_q[28] ^ lfsr_q[30] ^ lfsr_q[31] ^ data_in[2] ^ data_in[3] ^ data_in[4] ^ data_in[6] ^ data_in[7];
41                 lfsr_c[15] = lfsr_q[7] ^ lfsr_q[27] ^ lfsr_q[28] ^ lfsr_q[29] ^ lfsr_q[31] ^ data_in[3] ^ data_in[4] ^ data_in[5] ^ data_in[7];
42                 lfsr_c[16] = lfsr_q[8] ^ lfsr_q[24] ^ lfsr_q[28] ^ lfsr_q[29] ^ data_in[0] ^ data_in[4] ^ data_in[5];
43                 lfsr_c[17] = lfsr_q[9] ^ lfsr_q[25] ^ lfsr_q[29] ^ lfsr_q[30] ^ data_in[1] ^ data_in[5] ^ data_in[6];
44                 lfsr_c[18] = lfsr_q[10] ^ lfsr_q[26] ^ lfsr_q[30] ^ lfsr_q[31] ^ data_in[2] ^ data_in[6] ^ data_in[7];
45                 lfsr_c[19] = lfsr_q[11] ^ lfsr_q[27] ^ lfsr_q[31] ^ data_in[3] ^ data_in[7];
46                 lfsr_c[20] = lfsr_q[12] ^ lfsr_q[28] ^ data_in[4];
47                 lfsr_c[21] = lfsr_q[13] ^ lfsr_q[29] ^ data_in[5];
48                 lfsr_c[22] = lfsr_q[14] ^ lfsr_q[24] ^ data_in[0];
49                 lfsr_c[23] = lfsr_q[15] ^ lfsr_q[24] ^ lfsr_q[25] ^ lfsr_q[30] ^ data_in[0] ^ data_in[1] ^ data_in[6];
50                 lfsr_c[24] = lfsr_q[16] ^ lfsr_q[25] ^ lfsr_q[26] ^ lfsr_q[31] ^ data_in[1] ^ data_in[2] ^ data_in[7];
51                 lfsr_c[25] = lfsr_q[17] ^ lfsr_q[26] ^ lfsr_q[27] ^ data_in[2] ^ data_in[3];
52                 lfsr_c[26] = lfsr_q[18] ^ lfsr_q[24] ^ lfsr_q[27] ^ lfsr_q[28] ^ lfsr_q[30] ^ data_in[0] ^ data_in[3] ^ data_in[4] ^ data_in[6];
53                 lfsr_c[27] = lfsr_q[19] ^ lfsr_q[25] ^ lfsr_q[28] ^ lfsr_q[29] ^ lfsr_q[31] ^ data_in[1] ^ data_in[4] ^ data_in[5] ^ data_in[7];
54                 lfsr_c[28] = lfsr_q[20] ^ lfsr_q[26] ^ lfsr_q[29] ^ lfsr_q[30] ^ data_in[2] ^ data_in[5] ^ data_in[6];
55                 lfsr_c[29] = lfsr_q[21] ^ lfsr_q[27] ^ lfsr_q[30] ^ lfsr_q[31] ^ data_in[3] ^ data_in[6] ^ data_in[7];
56                 lfsr_c[30] = lfsr_q[22] ^ lfsr_q[28] ^ lfsr_q[31] ^ data_in[4] ^ data_in[7];
57                 lfsr_c[31] = lfsr_q[23] ^ lfsr_q[29] ^ data_in[5];
58 
59 
60         end // always
61 
62         always @(posedge clk, posedge rst) begin
63                 if(rst) begin
64                         lfsr_q  <= {32{1'b1}};
65                 end
66                 else begin
67                         lfsr_q  <= crc_en ? lfsr_c : lfsr_q;
68                 end
69         end // always
70 endmodule // crc

  正点原子的开源例程也有相关代码,DEMO文档提到的链接貌似已经没法打开。网址:http://www.easics.com/webtools/crctool

  1 //****************************************Copyright (c)***********************************//
  2 //技术支持:www.openedv.com
  3 //淘宝店铺:http://openedv.taobao.com 
  4 //关注微信公众平台微信号:"正点原子",免费获取FPGA & STM32资料。
  5 //版权所有,盗版必究。
  6 //Copyright(C) 正点原子 2018-2028
  7 //All rights reserved                                  
  8 //----------------------------------------------------------------------------------------
  9 // File name:           crc32_d8
 10 // Last modified Date:  2019/8/21 8:37:49
 11 // Last Version:        V1.0
 12 // Descriptions:        CRC32校验模块
 13 //----------------------------------------------------------------------------------------
 14 // Created by:          正点原子
 15 // Created date:        2018/3/12 8:37:49
 16 // Version:             V1.0
 17 // Descriptions:        The original version
 18 //
 19 //----------------------------------------------------------------------------------------
 20 //****************************************************************************************//
 21 
 22 module crc32_d8(
 23     input                 clk     ,  //时钟信号
 24     input                 rst_n   ,  //复位信号,低电平有效
 25     input         [7:0]   data    ,  //输入待校验8位数据
 26     input                 crc_en  ,  //crc使能,开始校验标志
 27     input                 crc_clr ,  //crc数据复位信号            
 28     output   reg  [31:0]  crc_data,  //CRC校验数据
 29     output        [31:0]  crc_next   //CRC下次校验完成数据
 30     );
 31 
 32 //*****************************************************
 33 //**                    main code
 34 //*****************************************************
 35 
 36 //输入待校验8位数据,需要先将高低位互换
 37 wire    [7:0]  data_t;
 38 
 39 assign data_t = {data[0],data[1],data[2],data[3],data[4],data[5],data[6],data[7]};
 40 
 41 //CRC32的生成多项式为:G(x)= x^32 + x^26 + x^23 + x^22 + x^16 + x^12 + x^11 
 42 //+ x^10 + x^8 + x^7 + x^5 + x^4 + x^2 + x^1 + 1
 43 
 44 assign crc_next[0] = crc_data[24] ^ crc_data[30] ^ data_t[0] ^ data_t[6];
 45 assign crc_next[1] = crc_data[24] ^ crc_data[25] ^ crc_data[30] ^ crc_data[31] 
 46                      ^ data_t[0] ^ data_t[1] ^ data_t[6] ^ data_t[7];
 47 assign crc_next[2] = crc_data[24] ^ crc_data[25] ^ crc_data[26] ^ crc_data[30] 
 48                      ^ crc_data[31] ^ data_t[0] ^ data_t[1] ^ data_t[2] ^ data_t[6] 
 49                      ^ data_t[7];
 50 assign crc_next[3] = crc_data[25] ^ crc_data[26] ^ crc_data[27] ^ crc_data[31] 
 51                      ^ data_t[1] ^ data_t[2] ^ data_t[3] ^ data_t[7];
 52 assign crc_next[4] = crc_data[24] ^ crc_data[26] ^ crc_data[27] ^ crc_data[28] 
 53                      ^ crc_data[30] ^ data_t[0] ^ data_t[2] ^ data_t[3] ^ data_t[4] 
 54                      ^ data_t[6];
 55 assign crc_next[5] = crc_data[24] ^ crc_data[25] ^ crc_data[27] ^ crc_data[28] 
 56                      ^ crc_data[29] ^ crc_data[30] ^ crc_data[31] ^ data_t[0] 
 57                      ^ data_t[1] ^ data_t[3] ^ data_t[4] ^ data_t[5] ^ data_t[6] 
 58                      ^ data_t[7];
 59 assign crc_next[6] = crc_data[25] ^ crc_data[26] ^ crc_data[28] ^ crc_data[29] 
 60                      ^ crc_data[30] ^ crc_data[31] ^ data_t[1] ^ data_t[2] ^ data_t[4] 
 61                      ^ data_t[5] ^ data_t[6] ^ data_t[7];
 62 assign crc_next[7] = crc_data[24] ^ crc_data[26] ^ crc_data[27] ^ crc_data[29] 
 63                      ^ crc_data[31] ^ data_t[0] ^ data_t[2] ^ data_t[3] ^ data_t[5] 
 64                      ^ data_t[7];
 65 assign crc_next[8] = crc_data[0] ^ crc_data[24] ^ crc_data[25] ^ crc_data[27] 
 66                      ^ crc_data[28] ^ data_t[0] ^ data_t[1] ^ data_t[3] ^ data_t[4];
 67 assign crc_next[9] = crc_data[1] ^ crc_data[25] ^ crc_data[26] ^ crc_data[28] 
 68                      ^ crc_data[29] ^ data_t[1] ^ data_t[2] ^ data_t[4] ^ data_t[5];
 69 assign crc_next[10] = crc_data[2] ^ crc_data[24] ^ crc_data[26] ^ crc_data[27] 
 70                      ^ crc_data[29] ^ data_t[0] ^ data_t[2] ^ data_t[3] ^ data_t[5];
 71 assign crc_next[11] = crc_data[3] ^ crc_data[24] ^ crc_data[25] ^ crc_data[27] 
 72                      ^ crc_data[28] ^ data_t[0] ^ data_t[1] ^ data_t[3] ^ data_t[4];
 73 assign crc_next[12] = crc_data[4] ^ crc_data[24] ^ crc_data[25] ^ crc_data[26] 
 74                      ^ crc_data[28] ^ crc_data[29] ^ crc_data[30] ^ data_t[0] 
 75                      ^ data_t[1] ^ data_t[2] ^ data_t[4] ^ data_t[5] ^ data_t[6];
 76 assign crc_next[13] = crc_data[5] ^ crc_data[25] ^ crc_data[26] ^ crc_data[27] 
 77                      ^ crc_data[29] ^ crc_data[30] ^ crc_data[31] ^ data_t[1] 
 78                      ^ data_t[2] ^ data_t[3] ^ data_t[5] ^ data_t[6] ^ data_t[7];
 79 assign crc_next[14] = crc_data[6] ^ crc_data[26] ^ crc_data[27] ^ crc_data[28] 
 80                      ^ crc_data[30] ^ crc_data[31] ^ data_t[2] ^ data_t[3] ^ data_t[4]
 81                      ^ data_t[6] ^ data_t[7];
 82 assign crc_next[15] =  crc_data[7] ^ crc_data[27] ^ crc_data[28] ^ crc_data[29]
 83                      ^ crc_data[31] ^ data_t[3] ^ data_t[4] ^ data_t[5] ^ data_t[7];
 84 assign crc_next[16] = crc_data[8] ^ crc_data[24] ^ crc_data[28] ^ crc_data[29] 
 85                      ^ data_t[0] ^ data_t[4] ^ data_t[5];
 86 assign crc_next[17] = crc_data[9] ^ crc_data[25] ^ crc_data[29] ^ crc_data[30] 
 87                      ^ data_t[1] ^ data_t[5] ^ data_t[6];
 88 assign crc_next[18] = crc_data[10] ^ crc_data[26] ^ crc_data[30] ^ crc_data[31] 
 89                      ^ data_t[2] ^ data_t[6] ^ data_t[7];
 90 assign crc_next[19] = crc_data[11] ^ crc_data[27] ^ crc_data[31] ^ data_t[3] ^ data_t[7];
 91 assign crc_next[20] = crc_data[12] ^ crc_data[28] ^ data_t[4];
 92 assign crc_next[21] = crc_data[13] ^ crc_data[29] ^ data_t[5];
 93 assign crc_next[22] = crc_data[14] ^ crc_data[24] ^ data_t[0];
 94 assign crc_next[23] = crc_data[15] ^ crc_data[24] ^ crc_data[25] ^ crc_data[30] 
 95                       ^ data_t[0] ^ data_t[1] ^ data_t[6];
 96 assign crc_next[24] = crc_data[16] ^ crc_data[25] ^ crc_data[26] ^ crc_data[31] 
 97                       ^ data_t[1] ^ data_t[2] ^ data_t[7];
 98 assign crc_next[25] = crc_data[17] ^ crc_data[26] ^ crc_data[27] ^ data_t[2] ^ data_t[3];
 99 assign crc_next[26] = crc_data[18] ^ crc_data[24] ^ crc_data[27] ^ crc_data[28] 
100                       ^ crc_data[30] ^ data_t[0] ^ data_t[3] ^ data_t[4] ^ data_t[6];
101 assign crc_next[27] = crc_data[19] ^ crc_data[25] ^ crc_data[28] ^ crc_data[29] 
102                       ^ crc_data[31] ^ data_t[1] ^ data_t[4] ^ data_t[5] ^ data_t[7];
103 assign crc_next[28] = crc_data[20] ^ crc_data[26] ^ crc_data[29] ^ crc_data[30] 
104                       ^ data_t[2] ^ data_t[5] ^ data_t[6];
105 assign crc_next[29] = crc_data[21] ^ crc_data[27] ^ crc_data[30] ^ crc_data[31] 
106                       ^ data_t[3] ^ data_t[6] ^ data_t[7];
107 assign crc_next[30] = crc_data[22] ^ crc_data[28] ^ crc_data[31] ^ data_t[4] ^ data_t[7];
108 assign crc_next[31] = crc_data[23] ^ crc_data[29] ^ data_t[5];
109 
110 always @(posedge clk or negedge rst_n) begin
111     if(!rst_n)
112         crc_data <= 32'hff_ff_ff_ff;
113     else if(crc_clr)                             //CRC校验值复位
114         crc_data <= 32'hff_ff_ff_ff;
115     else if(crc_en)
116         crc_data <= crc_next;
117 end
118 
119 endmodule

  笔者对比了二者发现代码差不多,只是正点原子的多了一些小的操作,比如对进来的数据先高低位转换。我们其他的流程基本是一致的,这些代码应该问题不大,这里不做相关的仿真验证

  实际上使用怎样的CRC格式,最好参考相关文档,比如以太网的格式是CRC-32,那么根据上面表格的算法模型。需要根据初始值,输入输出值是否需要反转而对代码做些相应的修改。

 

posted on 2022-05-17 10:59  Galois_V  阅读(2665)  评论(0编辑  收藏  举报