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,那么根据上面表格的算法模型。需要根据初始值,输入输出值是否需要反转而对代码做些相应的修改。