一、CRC校验码的产生原理
循环冗余编码(CRC)是二进制通信系统中一种常用的差错检测方法,它是通过在原始数据后面添加冗余校验码来实现检测差错的目的。CRC编码的基本原理就是:CRC可由原始数据流的二进制去除以一称作为生成多项式的常数,将得到的商抛弃,余数加到原始数据流二进制数的后面得到,在接收端,用接收的数据再去除以该生成多项式,如果余数为0则校验通过,否则校验不通过。
这里我们采用CRC16编码作为典型例子进行讲解,首先我们先讲解一下二进制数的多项式表示,二进制数可以表示成多项式的系数,如10 0100 0101用多项式就可以表示成:。另外,在CRC校验码的生成过程中需要用到除法操作,那么对于非常大的数字时,用数字逻辑来实现除法操作既消耗资源且操作麻烦,所以我们就需要将除数二进制数按照前面所讲解的规则转换为多项式,然后利用该多项式借助线性移位寄存器来实现复杂的除法操作。
国际上对于不同类型的CRC校验编码是有相应的国际标准的,比如CRC8,CRC16,在这些标准中对除数(或者叫做生成多项式)都有明确的规定,我们只需要查阅相关标准手册,然后根据相应的生成多项式编码实现不同类型的CRC校验码,这里以CRC16为例,CRC16的生成多项式可以表示为:
该生成多项式对应的线性反馈移位寄存器可以表示为:
图中的crc[0]-crc[15]分别代表CRC16校验码的第0-15位,data表示输入数据流,待校验码生成完毕之后,将16位校验码加到输入数据流的末尾就得到了生成的发送序列,图中的数据运算为异或操作,通过如上图所示的线性移位寄存器就实现了除法操作。
二、CRC16的Verilog实现
编码如下:
module crc16_para (
//system signals
input sclk ,
input s_rst_n ,
//
input [7:0] data ,
output reg [15:0] out
);
reg [15:0] crc16_out ;
reg [15:0] crc_reg ;
reg temp;
integer i,j,m,n ;
always @ (posedge sclk or negedge s_rst_n) begin
if(s_rst_n == 1'b0)
crc16_out<=16'b0;
else
crc16_out<=crc_reg;
end
always @ (crc16_out or data) begin
crc_reg=crc16_out;
for (i = 7; i >= 0; i = i - 1)
begin
temp=data[i]^crc_reg[15];
for (j = 15; j > 12; j = j - 1)
begin
crc_reg[j]=crc_reg[j-1];
end
crc_reg[12]=temp^crc_reg[11];
for (m = 11; m >5 ; m = m - 1)
begin
crc_reg[m]=crc_reg[m-1];
end
crc_reg[5]=temp^crc_reg[4];
for (n = 4; n >0 ; n = n - 1)
begin
crc_reg[n]=crc_reg[n-1];
end
crc_reg[0]=temp^0;
end
out=crc_reg;
end
endmodule
testbench如下:
`timescale 1ns/1ps
module tb ();
reg sclk;
reg s_rst_n;
reg [7:0] data ;
wire [15:0] out ;
initial
begin
sclk=1'b1;
s_rst_n=1'b1;
data=8'b1011_0110;
#5 s_rst_n=1'b0;
#5 s_rst_n=1'b1;
end
always #5 sclk=~sclk;
crc16_para crc16_para_instance(
//system signals
.sclk(sclk),
.s_rst_n(s_rst_n),
.data(data),
.out(out)
);
endmodule
Modelsim仿真结果:
CRC计算器计算结果:
由两图中结果比较可知,计算结果正确,校验通过。