循环冗余检验(Cyclic Redundancy Check,CRC)模块,是数据通信中一种常用的,纠错能力很强的数据检错模块,,该检验编码常用于串行通信。CRC校验码的基本思想是利用线性编码的原理,在发送端根据要传送的k位二进制码序列,以一定规则产生一个校验用的监督码(CRC码)r位,并附在信息后面组成一个k+r位的发送码。在接收端,根据信息码和CRC码之间所遵循的规则进行检验,以确定传送过程是否出错。接收端处理方法通常有两种:
模2运算规则:
- 模2加法:0+0=0 0+1=1 1+0=1 1+1=0
- 模2减法:0-0=0 0-1=1 1-0=1 1-1=0
- 模2乘法:0x0=0 0x1=0 1x0=0 1x1=1
- 模2除法:0/1=0 1/1=1
信息代码多项式: 在纠错编码代数中,把以二进制数字表示的一个数据系列堪称一个多项式。例如,二进制序列10011,用多项式可以表示成:
M(x)称为信息代码多项式。
生成多项式: 当进行CRC检验时,发送方和接收方需事先约定一个除数,即生成多项式G(x),生成多项式的最高位和最低位必须是1,下面给出几种标准的CRC码生成多项式:
CRC码 | 生成多项式G(x) |
---|---|
CRC-4 | |
CRC-5 | |
CRC-8 | |
CRC-9 | |
CRC-12 | |
CRC-16 | |
CRC-CCITT | |
CRC-32 |
国际通行标准查看
在数据存储和数据通讯领域,CRC检验无处不在:著名的通讯协议X.25的FCS(帧检错序列)采用CRC算法检错,以及CCITT,ARJ,LHA等压缩工具软件采用CRC32,磁盘驱动器的读写采用CRC16,通用的图像存储格式GIF,TIFF等也都采用CRC 作为检测手段。
CRC校验方法:通过生成多项式,借助多项式除法,所得余数为校验字段,则发出数据由信息字段+校验字段构成;接收方采用相同的生成多项式对发出数据进行检验,利用多项式除法,若能够除尽则传输正确。
例:
选取生成多项式:CRC-4, 11001;
采用多项式除法:10110010000 / 11001得余数为1010,即1010为检验字段,因此发出得传输字段为:1011001|1010 (信息字段|检验字段)
接收方采用传输字段除以相同的生成码CRC-4: 11001,即10110011010 / 11001 ,所得结果余数为0,证明传输正确。
利用CRC原理,基于FPGA设计一个8位CRC,要求输入din为64 bit 数据,生成多项式为CRC-8 : 100000111,输出crc_o 为8 bit 校验字段:
通过上述crc算法求解校验字段代码和模2除法规律可知,模2除法在可除的情况下,等效于利用生成多项式与相应的位数进行异或运算,因此考虑可以将64 bit 输入数据依次进行移位,并逐次与规定的生成多项式CRC-8进行异或运算,这个是很好实现的;
为了使求得余数比生成多项式少一位,即输出crc_o为8位,应在对输入数据进行运算前在其后补充8个“0”;
若用生成多项式与数据位数进行逐位异或将显得十分繁琐(如下):
always @ (posedge clk or negedge rst_n)
begin
if(!rst_n)
begin
crc_o<=0;
temp<=stemp;
end
else
begin
if(temp[71]) temp[71:63]<=temp[71:63]^polynomial;
else if(temp[70]) temp[70:62]<=temp[70:62]^polynomial;
else if(temp[69]) temp[69:61]<=temp[69:61]^polynomial;
else if(temp[68]) temp[68:60]<=temp[68:60]^polynomial;
else if(temp[67]) temp[67:59]<=temp[67:59]^polynomial;
else if(temp[66]) temp[66:58]<=temp[66:58]^polynomial;
else if(temp[65]) temp[65:57]<=temp[65:57]^polynomial;
else if(temp[64]) temp[64:56]<=temp[64:56]^polynomial;
else if(temp[63]) temp[63:55]<=temp[63:55]^polynomial;
else if(temp[62]) temp[62:54]<=temp[62:54]^polynomial;
else if(temp[61]) temp[61:53]<=temp[61:53]^polynomial;
else if(temp[60]) temp[60:52]<=temp[60:52]^polynomial;
else if(temp[59]) temp[59:51]<=temp[59:51]^polynomial;
else if(temp[58]) temp[58:50]<=temp[58:50]^polynomial;
else if(temp[57]) temp[57:49]<=temp[57:49]^polynomial;
else if(temp[56]) temp[56:48]<=temp[56:48]^polynomial;
else if(temp[55]) temp[55:47]<=temp[55:47]^polynomial;
else if(temp[54]) temp[54:46]<=temp[54:46]^polynomial;
else if(temp[53]) temp[53:45]<=temp[53:45]^polynomial;
else if(temp[52]) temp[52:44]<=temp[52:44]^polynomial;
else if(temp[51]) temp[51:43]<=temp[51:43]^polynomial;
else if(temp[50]) temp[50:42]<=temp[50:42]^polynomial;
else if(temp[49]) temp[49:41]<=temp[49:41]^polynomial;
else if(temp[48]) temp[48:40]<=temp[48:40]^polynomial;
else if(temp[47]) temp[47:39]<=temp[47:39]^polynomial;
else if(temp[46]) temp[46:38]<=temp[46:38]^polynomial;
else if(temp[45]) temp[45:37]<=temp[45:37]^polynomial;
else if(temp[44]) temp[44:36]<=temp[44:36]^polynomial;
else if(temp[43]) temp[43:35]<=temp[44:35]^polynomial;
else if(temp[42]) temp[42:34]<=temp[44:34]^polynomial;
else if(temp[41]) temp[41:33]<=temp[44:33]^polynomial;
else if(temp[40]) temp[40:32]<=temp[40:32]^polynomial;
else if(temp[39]) temp[39:31]<=temp[39:31]^polynomial;
else if(temp[38]) temp[38:30]<=temp[38:30]^polynomial;
else if(temp[37]) temp[37:29]<=temp[37:29]^polynomial;
else if(temp[36]) temp[36:28]<=temp[36:28]^polynomial;
else if(temp[35]) temp[35:27]<=temp[35:27]^polynomial;
else if(temp[34]) temp[34:26]<=temp[34:26]^polynomial;
else if(temp[33]) temp[33:25]<=temp[33:25]^polynomial;
else if(temp[32]) temp[32:24]<=temp[32:24]^polynomial;
else if(temp[31]) temp[31:23]<=temp[31:23]^polynomial;
else if(temp[30]) temp[30:22]<=temp[30:22]^polynomial;
else if(temp[29]) temp[29:21]<=temp[29:21]^polynomial;
else if(temp[28]) temp[28:20]<=temp[28:20]^polynomial;
else if(temp[27]) temp[27:19]<=temp[27:19]^polynomial;
else if(temp[26]) temp[26:18]<=temp[26:18]^polynomial;
else if(temp[25]) temp[25:17]<=temp[25:17]^polynomial;
else if(temp[24]) temp[24:16]<=temp[24:16]^polynomial;
else if(temp[23]) temp[23:15]<=temp[23:15]^polynomial;
else if(temp[22]) temp[22:14]<=temp[22:14]^polynomial;
else if(temp[21]) temp[21:13]<=temp[21:13]^polynomial;
else if(temp[20]) temp[20:12]<=temp[20:12]^polynomial;
else if(temp[19]) temp[19:11]<=temp[19:11]^polynomial;
else if(temp[18]) temp[18:10]<=temp[18:10]^polynomial;
else if(temp[17]) temp[17:9]<=temp[17:9]^polynomial;
else if(temp[16]) temp[16:8]<=temp[16:8]^polynomial;
else if(temp[15]) temp[15:7]<=temp[15:7]^polynomial;
else if(temp[14]) temp[14:6]<=temp[14:6]^polynomial;
else if(temp[13]) temp[13:5]<=temp[13:5]^polynomial;
else if(temp[12]) temp[12:4]<=temp[12:4]^polynomial;
else if(temp[11]) temp[11:3]<=temp[11:3]^polynomial;
else if(temp[10]) temp[10:2]<=temp[10:2]^polynomial;
else if(temp[9]) temp[9:1]<=temp[9:1]^polynomial;
else if(temp[8]) temp[8:0]<=temp[8:0]^polynomial;
else crc_o<=temp[7:0];
end
end
因此为了简便代码,这里将72 bit 写进数组后,对数组内的数依次向左侧进行移位,而保证始终使前9位与poly生成多项式进行异或,这样省去了繁琐的代码,仅用两行代码便可实现(见FPGA源码部分);
FPGA实现代码如下:
module crc_8 ( clk,
rst_n,
din,
crc_start,
crc_vld,
crc_o
);
input clk;
input rst_n;
input [63:0] din;
input crc_start;
output crc_vld;
output [7:0] crc_o;
parameter poly = 8'h07;
reg crc_vld;
reg [71:0] din_tmp;
reg [5:0] cnt;
reg pro_flag;
wire [7:0] crc_o;
//pro_flag
always @ (posedge clk or negedge rst_n)
if (~rst_n)
pro_flag <= 1'b0;
else if (crc_start)
pro_flag <= 1'b1;
else if (cnt == 6'd63)
pro_flag <= 1'b0;
//cnt
always @ (posedge clk or negedge rst_n)
if (~rst_n)
cnt <= 6'b0;
else if (pro_flag)
cnt <= cnt + 1'b1;
else
cnt <= 6'b0;
//din_tmp
always @ (posedge clk or negedge rst_n)
if (~rst_n)
din_tmp <= 72'b0;
else if (crc_start)
din_tmp <= {din, 8'b0};
else if (pro_flag)
begin
din_tmp[71:64] <= din_tmp[71] ? din_tmp[70:63] ^ poly : din_tmp[70:63];
din_tmp[63:0] <= {din_tmp[62:0],1'b0};
end
//crc_vld
always @ (posedge clk or negedge rst_n)
if (~rst_n)
crc_vld <= 1'b0;
else if (cnt == 6'd63)
crc_vld <= 1'b1;
else
crc_vld <= 1'b0;
assign crc_o = crc_vld ? din_tmp[71:64] : 'b0;
endmodule
testbench:
`timescale 1ns/1ns
module tb_crc();
reg clk;
reg rst_n;
reg [63:0] din;
reg crc_start;
wire crc_vld;
wire [7:0] crc_o;
crc i_crc ( .clk (clk ),
.rst_n (rst_n ),
.din (din ),
.crc_start (crc_start),
.crc_vld (crc_vld ),
.crc_o (crc_o )
);
always #5 clk = ~clk;
initial begin
clk = 1'b0;
rst_n = 1'b0;
din = 'b0;
crc_start = 1'b0;
#100;
rst_n = 1'b1;
@(posedge clk) ;
din = 64'h12345678;
crc_start = 1'b1;
@(posedge clk) ;
crc_start = 1'b0;
@(posedge crc_vld);
#2;
if (crc_o == 8'h1c)
$display("Correct !");
else
$display("Incorrect !");
#50;
$stop;
end
endmodule