CRC校验
1.简介
在数字通信的传输过程中, 由于信道存在的噪声、线路间的串扰等各种因素的影响, 造成所传输的信号失真。
为了提高通信的可靠性和减少误码率, 通常采用信道编码技术来进行差错控制。循环冗余校验(Cyclic Redundancy Code, CRC) 由于其误码检测能力强, 抗干扰性能优异, 在众多的信道编码方法中得到广泛的应用。
CRC是最常用的一种查错校验码,其特征是信息字段和校验字段的长度可以任意选定。
2.原理
CRC码是由两部分组成的,前部分是信息码,就是需要校验的信息,后部分是校验码,如果CRC码长共n bit,信息码长k bit,就称为(n,k)码,剩余的r bit即为校验位。如:(7,3)码:110 1001,前三位110为信息码,1001为校验码。
这个校验码不是随意的,是需要经过计算得出,这里需要引进一个生成多项式的概念。
2.1 生成多项式g(x)
在产生CRC校验码时,要用到除法运算,一般来说,这是比较麻烦的,因此,把二进制信息预先转换成一定的格式,这就是CRC的多项式表示。二进制数表示为生成多项式的系数,如下:
所有二进制数均被表示为一个多项式,x仅是码元位置的标记,因此我们并不关心x的取值,称之为码多项式。(我没研究过CRC代数推理过程,没体会到用多项式计算的方便之处,这里要学会的就是给出生成多项式g(x),能写出对应的二进制即可)
常见的生成多项式如下:
2.2校验码计算
假定生成多项式的位数为n,则在原信息码的后面加上(n-1)为0,并将生成的新的信息码作为被除数,与生成多项式的二进制进行模二除法,得到的余数就是校验码。
例子:
3.CRC校验分类
CRC校验有两种方式,一种是串行计算CRC,一种是并行。
- 串行CRC
如果用时序电路串行实现,则8 bit数据要移位8次,就需要8个clk,效率低下,为了能在一个时钟周期输出结果,必须采用组合电路,当然,这是以空间换时间的方法,由于使用了for循环8次,直观的讲电路规模将扩大8倍。 - 并行CRC
以8bit数据为例,并行计算一次性输入8bit数据,相当于一次并行运算就得到了串行运算需要8位移位所得的结果。
4.串行CRC校验码计算
通常CRC 的串行实现是通过线性反馈移位寄存器 (L FSR s) 来实现的。移位寄存器L FSR s 由m (设生成多项
式的最高阶是m ) 个D 触发器, 加上一些异或门和一条反馈回路组成。线性反馈移位寄存器(L FSR s) 有2 种可能
结构, 分别如图2、图3 所示。本文中把图2 所示的结构称为L FSR, 而把图3 的结构称为L FSR2。
串行编码实现如图2 所示, 开始时, 寄存器初始化为全0 (或全1) , 数据流码字按高位到低位(或反之) 的顺
序, 依次通过L FSR s。当数据的一位码字从右边移出寄存器时, 就通过反馈回路并根据生成多项式相应位的值(1
表示通路, 0 表示断路) , 和后续输入数据进行异或运算, 从左边反馈进入移位寄存器。当所有位数据移入寄存
器后, 移位寄存器中所形成的结果即为CRC 校验码。需要指出的是, 利用L FSR 进行CRC 编码时, 应在信息码后面
加上m 位的0 序列, 而利用L FSR2 进行编码则不需要。
串行译码使用的电路与编码电路一样, 通常利用L FSR 作为译码电路。与编码不同的是, 利用L FSR 进行译码时, 不需要在信息码后面添加零序列。
4.1 L FSR的伪随机数
通过一定的算法对事先选定的随机种子(seed)做一定的运算可以得到一组人工生成的周期序列,在这组序列中以相同的概率选取其中一个数字,该数字称作伪随机数,由于所选数字并不具有完全的随机性,但是从实用的角度而言,其随机程度已足够了。这里的“伪”的含义是,由于该随机数是按照一定算法模拟产生的,其结果是确定的,是可见的,因此并不是真正的随机数。伪随机数的选择是从随机种子开始的,所以为了保证每次得到的伪随机数都足够地“随机”,随机种子的选择就显得非常重要,如果随机种子一样,那么同一个随机数发生器产生的随机数也会一样。
产生伪随机数的方法最常见的是利用一种线性反馈移位寄存器(LFSR),它是由n个D触发器和若干个异或门组成的,如下图:
其中,gn为反馈系数,取值只能为0或1,取为0时表明不存在该反馈之路,取为1时表明存在该反馈之路;n个D触发器最多可以提供2^n-1个状态(不包括全0的状态),为了保证这些状态没有重复,gn的选择必须满足一定的条件。下面以n=3,g0=1,g1=1,g2=0,g3=1为例,说明LFSR的特性,具有该参数的LFSR结构如下图:
从图可以看出,正好有2^3-1=7个状态,不包括全0;
如果您理解了上图,至少可以得到三条结论:
1)初始状态是由SEED提供的;
2)当反馈系数不同时,得到的状态转移图也不同;必须保证gn===1,否则哪来的反馈?
3)D触发器的个数越多,产生的状态就越多,也就越“随机”;
通过CRC的生成原理知道CRC的检验码生成是通过除法得到,由此联想到可以通过LFSR来产生校验码。
假设原信息码子多项式为
生成多项式为
那么CRC的码字为 ,使用LFSR电路来进行实现,将M(x)向左移r位在电路中的意义即为输入完信息码后再输入r个0,所以在电路上的表现就如图5所示。
将这个时刻产生的寄存器输入添加到原信息码的后边就进行完了CRC编码,同样接收端可以使用LFSR来进行CRC检验。
4.2 串行计算CRC verilog实现
module CRC_GEN(
input rst, /*async reset,active low*/
input clk, /*clock input*/
input [7:0] data_in, /*parallel data input pins */
input d_valid, /* data valid,start to generate CRC, active high*/
output reg[15:0] crc
);
integer i;
reg feedback;
reg [15:0] crc_tmp;
/*
* sequential process
*/
always @(posedge clk or negedge rst)
begin
if(!rst)
crc <= 16'b0; /*触发器中的初始值十分重要 */
else if(d_valid==1'b0)
crc <= 16'b0;
else
crc <= crc_tmp;
end
/*
* combination process
*/
always@( data_in or crc)
begin
crc_tmp = crc;
for(i=7; i>=0; i=i-1)
begin
feedback = crc_tmp[15] ^ data_in[i];
crc_tmp[15] = crc_tmp[14];
crc_tmp[14] = crc_tmp[13];
crc_tmp[13] = crc_tmp[12];
crc_tmp[12] = crc_tmp[11] ^ feedback;
crc_tmp[11] = crc_tmp[10] ;
crc_tmp[10] = crc_tmp[9];
crc_tmp[9] = crc_tmp[8];
crc_tmp[8] = crc_tmp[7];
crc_tmp[7] = crc_tmp[6];
crc_tmp[6] = crc_tmp[5];
crc_tmp[5] = crc_tmp[4] ^ feedback;
crc_tmp[4] = crc_tmp[3];
crc_tmp[3] = crc_tmp[2];
crc_tmp[2] = crc_tmp[1];
crc_tmp[1] = crc_tmp[0];
crc_tmp[0] = feedback;
end
end
endmodule
5.并行CRC校验码计算
并行计算CRC校验码推导公式有点难懂,放上网址,以后在学
https://dspace.xmu.edu.cn/bitstream/handle/2288/157622/%e4%b8%80%e7%a7%8d%e5%b9%b6%e8%a1%8cCRC%e7%ae%97%e6%b3%95%e7%9a%84%e5%ae%9e%e7%8e%b0%e6%96%b9%e6%b3%95.pdf?sequence=1&isAllowed=y
5.1 并行verilog实现
https://www.cnblogs.com/kingstacker/p/9848191.html
1)并行计算crc用verilog语言描述是复杂繁琐的,所以可以使用在线工具生成verilog或者VHDL模板:http://www.easics.com/webtools/crctool,模板生成的代码稍加修改即可使用。
(2)本次校验模型为G(x) = X16+X12+X5+1。在在线工具中操作相关选项生成模板。
这是生成的代码
// Copyright (C) 1999-2008 Easics NV.
// This source file may be used and distributed without restriction
// provided that this copyright statement is not removed from the file
// and that any derivative work contains the original copyright notice
// and the associated disclaimer.
//
// THIS SOURCE FILE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS
// OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
// WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
//
// Purpose : synthesizable CRC function
// * polynomial: x^16 + x^12 + x^5 + 1
// * data width: 16
//
// Info : tools@easics.be
// http://www.easics.com
module CRC16_D16;
// polynomial: x^16 + x^12 + x^5 + 1
// data width: 16
// convention: the first serial bit is D[15]
function [15:0] nextCRC16_D16;
input [15:0] Data;
input [15:0] crc;
reg [15:0] d;
reg [15:0] c;
reg [15:0] newcrc;
begin
d = Data;
c = crc;
newcrc[0] = d[12] ^ d[11] ^ d[8] ^ d[4] ^ d[0] ^ c[0] ^ c[4] ^ c[8] ^ c[11] ^ c[12];
newcrc[1] = d[13] ^ d[12] ^ d[9] ^ d[5] ^ d[1] ^ c[1] ^ c[5] ^ c[9] ^ c[12] ^ c[13];
newcrc[2] = d[14] ^ d[13] ^ d[10] ^ d[6] ^ d[2] ^ c[2] ^ c[6] ^ c[10] ^ c[13] ^ c[14];
newcrc[3] = d[15] ^ d[14] ^ d[11] ^ d[7] ^ d[3] ^ c[3] ^ c[7] ^ c[11] ^ c[14] ^ c[15];
newcrc[4] = d[15] ^ d[12] ^ d[8] ^ d[4] ^ c[4] ^ c[8] ^ c[12] ^ c[15];
newcrc[5] = d[13] ^ d[12] ^ d[11] ^ d[9] ^ d[8] ^ d[5] ^ d[4] ^ d[0] ^ c[0] ^ c[4] ^ c[5] ^ c[8] ^ c[9] ^ c[11] ^ c[12] ^ c[13];
newcrc[6] = d[14] ^ d[13] ^ d[12] ^ d[10] ^ d[9] ^ d[6] ^ d[5] ^ d[1] ^ c[1] ^ c[5] ^ c[6] ^ c[9] ^ c[10] ^ c[12] ^ c[13] ^ c[14];
newcrc[7] = d[15] ^ d[14] ^ d[13] ^ d[11] ^ d[10] ^ d[7] ^ d[6] ^ d[2] ^ c[2] ^ c[6] ^ c[7] ^ c[10] ^ c[11] ^ c[13] ^ c[14] ^ c[15];
newcrc[8] = d[15] ^ d[14] ^ d[12] ^ d[11] ^ d[8] ^ d[7] ^ d[3] ^ c[3] ^ c[7] ^ c[8] ^ c[11] ^ c[12] ^ c[14] ^ c[15];
newcrc[9] = d[15] ^ d[13] ^ d[12] ^ d[9] ^ d[8] ^ d[4] ^ c[4] ^ c[8] ^ c[9] ^ c[12] ^ c[13] ^ c[15];
newcrc[10] = d[14] ^ d[13] ^ d[10] ^ d[9] ^ d[5] ^ c[5] ^ c[9] ^ c[10] ^ c[13] ^ c[14];
newcrc[11] = d[15] ^ d[14] ^ d[11] ^ d[10] ^ d[6] ^ c[6] ^ c[10] ^ c[11] ^ c[14] ^ c[15];
newcrc[12] = d[15] ^ d[8] ^ d[7] ^ d[4] ^ d[0] ^ c[0] ^ c[4] ^ c[7] ^ c[8] ^ c[15];
newcrc[13] = d[9] ^ d[8] ^ d[5] ^ d[1] ^ c[1] ^ c[5] ^ c[8] ^ c[9];
newcrc[14] = d[10] ^ d[9] ^ d[6] ^ d[2] ^ c[2] ^ c[6] ^ c[9] ^ c[10];
newcrc[15] = d[11] ^ d[10] ^ d[7] ^ d[3] ^ c[3] ^ c[7] ^ c[10] ^ c[11];
nextCRC16_D16 = newcrc;
end
endfunction
endmodule
这是经过修改的
`timescale 1ns/1ps
module crc16_test (
input wire i_clk , //时钟;
input wire i_rst_n , //同步复位;
input wire i_din_valid , //输入数据有效;
input wire [15:0] i_din , //输入数据;
output wire o_dout_valid , //输出CRC值有效;
output wire [15:0] o_dout //输出CRC;
);
reg [15:0] r_dout;
wire [15:0] d;
wire [15:0] c;
assign d = i_din;
assign c = r_dout;
always @(posedge i_clk) begin
if (~i_rst_n)
r_dout <= 16'hffff; //初始值为ffff;
else if (i_din_valid)
begin //计算逻辑;
r_dout[0] = d[12] ^ d[11] ^ d[8] ^ d[4] ^ d[0] ^ c[0] ^ c[4] ^ c[8] ^ c[11] ^ c[12];
r_dout[1] = d[13] ^ d[12] ^ d[9] ^ d[5] ^ d[1] ^ c[1] ^ c[5] ^ c[9] ^ c[12] ^ c[13];
r_dout[2] = d[14] ^ d[13] ^ d[10] ^ d[6] ^ d[2] ^ c[2] ^ c[6] ^ c[10] ^ c[13] ^ c[14];
r_dout[3] = d[15] ^ d[14] ^ d[11] ^ d[7] ^ d[3] ^ c[3] ^ c[7] ^ c[11] ^ c[14] ^ c[15];
r_dout[4] = d[15] ^ d[12] ^ d[8] ^ d[4] ^ c[4] ^ c[8] ^ c[12] ^ c[15];
r_dout[5] = d[13] ^ d[12] ^ d[11] ^ d[9] ^ d[8] ^ d[5] ^ d[4] ^ d[0] ^ c[0] ^ c[4] ^ c[5] ^ c[8] ^ c[9] ^ c[11] ^ c[12] ^ c[13];
r_dout[6] = d[14] ^ d[13] ^ d[12] ^ d[10] ^ d[9] ^ d[6] ^ d[5] ^ d[1] ^ c[1] ^ c[5] ^ c[6] ^ c[9] ^ c[10] ^ c[12] ^ c[13] ^ c[14];
r_dout[7] = d[15] ^ d[14] ^ d[13] ^ d[11] ^ d[10] ^ d[7] ^ d[6] ^ d[2] ^ c[2] ^ c[6] ^ c[7] ^ c[10] ^ c[11] ^ c[13] ^ c[14] ^ c[15];
r_dout[8] = d[15] ^ d[14] ^ d[12] ^ d[11] ^ d[8] ^ d[7] ^ d[3] ^ c[3] ^ c[7] ^ c[8] ^ c[11] ^ c[12] ^ c[14] ^ c[15];
r_dout[9] = d[15] ^ d[13] ^ d[12] ^ d[9] ^ d[8] ^ d[4] ^ c[4] ^ c[8] ^ c[9] ^ c[12] ^ c[13] ^ c[15];
r_dout[10] = d[14] ^ d[13] ^ d[10] ^ d[9] ^ d[5] ^ c[5] ^ c[9] ^ c[10] ^ c[13] ^ c[14];
r_dout[11] = d[15] ^ d[14] ^ d[11] ^ d[10] ^ d[6] ^ c[6] ^ c[10] ^ c[11] ^ c[14] ^ c[15];
r_dout[12] = d[15] ^ d[8] ^ d[7] ^ d[4] ^ d[0] ^ c[0] ^ c[4] ^ c[7] ^ c[8] ^ c[15];
r_dout[13] = d[9] ^ d[8] ^ d[5] ^ d[1] ^ c[1] ^ c[5] ^ c[8] ^ c[9];
r_dout[14] = d[10] ^ d[9] ^ d[6] ^ d[2] ^ c[2] ^ c[6] ^ c[9] ^ c[10];
r_dout[15] = d[11] ^ d[10] ^ d[7] ^ d[3] ^ c[3] ^ c[7] ^ c[10] ^ c[11];
end
end
reg r_dout_valid = 0;
always @(posedge i_clk) //输入数据在一个时钟内完成CRC计算,下一个时钟输出;
begin
r_dout_valid <= i_din_valid;
end
assign o_dout_valid = r_dout_valid;
assign o_dout = r_dout ;
endmodule // end the crc16_test model;