前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >CRC校验的FPGA实现

CRC校验的FPGA实现

作者头像
根究FPGA
发布2020-06-30 11:55:45
3.5K0
发布2020-06-30 11:55:45
举报
文章被收录于专栏:根究FPGA

CRC是什么?

CRC定义 CRC(Cyclic Redundancy Check),循环冗余校验,其特征是信息字段和校验字段的长度可以任意选定,CRC编码格式是在k位有效数据之后添加r位校验码,形成总长度为n(K+R)位的CRC码。

生成多项式

生成多项式 即使是CRC8也有多种,但总是有这样一个结果,最低位和最高为均为1,所以可以表达为(mathtype公式插不上来,截图了):

其中,Hn为相关性,为0或1。CRC-X,X为几,校验位就是Xbit,即生成多项式的最高次是几,校验结果的位宽就是几bit。以G(x)=x^5+x^3+1为例,其对应的二进制为5’b101001,其中1代表多项式的系数。

帧校验序列FCS

帧检验序列FCS 帧检验序列简称为FCS(Frame Check Sequence),是为了进行差错检验而添加的冗余码。

生成多项式 在进行CRC校验时,发送方和接收方需要事先约定一个除数,即生成多项式,一般记为G(x),生成多项式的最高位和最低位必须为1。

帧校验码的计算

设信息字段为K位,校验字段为R位,码字长度N=K+R,设双方事先约定了一个R次多项式G(x),则CRC校验码为:V(x)=A(x)G(x)=xRm(x)+r(x) m(x)为K次信息多项式,r(x)为R-1次检验多项式(看晕了的话直接看实例更便于理解,毕竟公式太抽象)。

这里r(x)对应的代码即为冗余码,加在原信息字段后即形成CRC码。r(x)的计算方法为:在K位信息字段的后面添加R个0,再除以g(x)对应的代码序列,得到的余数即为r(x)对应的代码(应为R-1位;若不足,而在高位补0)。

首先是CRC-8,CRC-8的余数是一个8bit数据,这一位是发送设备处理需要发送k为信息码外,还需要发送8bit的校验位,假设信息为16bit,[0110_0010_0100_1100]2,即为设备需要发送的数据,再加上8bit的校验位,则必须发送16+8=24bit的数据。

计算方法有:手算、移位寄存器和并行处理。(并行运算此处不做展开说明,其实并行运算是最复杂的,因为需要进行公式推导,但是好在多项式基本都有自己固定的运算公式,非常固定,用就好了)

手算CRC

以信息码为[10_1011_1011]2 ,使用下面的生成多项式为例:

代码语言:javascript
复制
G(x)=X^4+x+1

a、首先,把信息位左移4bit,结果为[10_1011_1011_0000]2 ,然后做异或除法运算,关于为什么右移4bit,是因为CRC本质是一个取余运算,余数的位宽是最大位宽-1

b、循环异或除法运算:

代码语言:javascript
复制
[10_1011_1011_0000]2
[10_011]2
——————————————————————————
00_1101_1011_0000
[1001_1]2
——————————————————————————
  0100_0011_0000
[100_11]2
——————————————————————————
   00_1111_0000
[1001_1]2
——————————————————————————
    0110_1000
[100_11]2
——————————————————————————
010_0100 
[10_011]2
——————————————————————————
10 

c、最后进行填补,校验位为0010,最终发送到接收端的数据为[10_1011_1011_0010]2 ,接收端再根据多项式对接收数据进行校验,结果为0表示接收正确。对结果4'b0010进行验证:

移位寄存器的实现

原理:

移位寄存器接近于硬件设计,在输入为单bit时很有效,以 ,其对应的二进制形式为:[1_0000_0111]2,哪一位为1就代表哪一位要进行异或操作,最高位的1表示与输入进行异或(参考上面的手动异或运算,输入数据的最高位先进行异或操作),0111就是代表 C0、C1、C2前面都有一次异或操作,用框图表示就是下图:

寄存器逻辑图:

以G(x)=x^4+x+1为例,其二进制形式为5'b1_0011,其框图为:

设C0 C1 C2 C3初始值皆为0,信息码为10_1011_1011,将信息码从高位到低位逐次移入逻辑电路,计算CRC检验结果。

C1的输入是上一个时钟的C0与本拍的输入进行异或操作,最后将操作结果的左边作为最低位,右边作为最高位,即为CRC校验值:

需要注意的是寄存器的初始值大多是确定的(就目前这个例子来说),因为我试了试1111,根本不对~~~~~~~

example

我对乐鑫一道题的题干保持怀疑态度,因为自己见识比较少,未曾见过这种形式的CRC校验,所以对于该题我仅给出个人理解,如果有实际应用中单bit输出使用过的朋友欢迎一起讨论!

改题目从公众号《数字IC自修室》中看到,我认为串行输出的结果应该为8bit,所以以下代码以8bit的校验结果输出编写:

主程序:

代码语言:javascript
复制
module CRC8(
input clk,
input rst_n,
input data,              //输入数据
input data_valid,        //data valid指示,该信号为1表示输入数据有效
input crc_start,        //CRC校验开始控制
output reg [7:0]crc_out,   //串行CRC输出
output reg [5:0] num,      //该信号用于观察校验进程,无实际作用
output reg crc_valid       //串行CRC输出有效信号
);

reg [3:0]state;  //状态机

reg [7:0]crc;  //中间寄存器

//reg [5:0]num;  //计数器

wire [7:0]crc_new;

always@(posedge clk or negedge rst_n)
if(~rst_n)
 crc<=8'h00;
else if(crc_start)
 crc<=8'h00;
else if(data_valid)
begin
/*
  比较之下此处还是使用时序语句+data_valid控制比较好,
  因为假设串行数据是不连续的,使用组合逻辑就容易发生错误
*/
 crc[0]<=crc[7]^data;   
 crc[1]<=crc[0]^crc[7]^data; 
 crc[2]<=crc[1]^crc[7]^data; 
 crc[3]<=crc[2];  
 crc[4]<=crc[3]; 
 crc[5]<=crc[4]; 
 crc[6]<=crc[5]; 
 crc[7]<=crc[6]; 
 end

always@(posedge clk or negedge rst_n)
 if(~rst_n )
  num<=6'd0;
 else if(crc_start )  //假设被中途打断,将计数器清零
  num<=6'd0;
 else if(num==6'd32)  //32个数据校验完毕,将计数器清零
  num<=6'd0;
 else if(data_valid)  //每次数据有效时,将计数器加一
  num<=num+1'b1;

always@(posedge clk or negedge rst_n)
if(~rst_n)
 begin
  crc_valid<=1'b0;
  crc_out<=1'b0;
 end 
else if(num==32) 
 begin
  crc_valid<=1'b1;
  crc_out<=crc;
 end

endmodule

仿真文件:

代码语言:javascript
复制
`timescale 1ns/1ns
`define clk_period 20

module c_tb();

reg clk, rst_n, data, data_valid, crc_start;  //CRC校验开始控制
wire [7:0]crc_out;  //串行CRC输出
wire crc_valid; //串行CRC输出有效信号
wire [5:0] num;
CRC8 c0(
.clk(clk),
.rst_n(rst_n),
.data(data),  //输入数据
.data_valid(data_valid),  //data valid指示,该信号为1表示输入数据有效
.crc_start(crc_start),  //CRC校验开始控制
.crc_out(crc_out),  //串行CRC输出
.num(num),
.crc_valid(crc_valid) //串行CRC输出有效信号
);

initial begin
clk=1'b0;
end 
always #(`clk_period/2) clk=~clk;

initial begin
 rst_n=1'b0;
 data =1'b1;
 data_valid=1'b0;
 crc_start=1'b0;
 #100;
 rst_n=1'b1;
 crc_start=1'b1;
 #20;
 crc_start=1'b0;
 #50;
 data_valid=1'b1;
 #(`clk_period*16);
 data=1'b0;
 #(`clk_period*16);
 data_valid=1'b0;
 #50;
 $stop;
end 

endmodule 

仿真结果:

运行结果为:8'hFA

对比:

为了验证代码正确性,打开在线CRC校验工具,输入32'hffff_0000,查看运算结果,与仿真一致,说明代码正确。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-06-19,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 根究FPGA 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档