数据帧CRC32校验算法实现
本文设计思想采用明德扬至简设计法。由于本人项目需要进行光纤数据传输,为了保证通信质量要对数据进行校验。在校验算法中,最简单最成熟的非CRC校验莫属了。
得出一个数的CRC校验码还是比较简单的:
- 选定一个CRC生成多项式G(x);
- 将发送数据左移K位,右侧补零(其中K为生成多项式最高次幂);
- 用移位补零后的数据对G(x)进行模2除法(其实就是异或运算);
- 用得到的余数即为该数据的CRC校验码;
发送端将移位补零后数据的低K位0替换成CRC校验码组成新的数据发送出去,接收端对带有校验码的数据对同样的G(x)做模2除法。由于发送端将余数加入在数据尾部,相当于已做了“去余”处理,故若数据传输正确时,接收端的模2除运算余数应为0。其中校验位数和生成多项式不是随便选定的,一般采用常用的标准形式。其中CRCK是指有K为校验位,不同位数对应不同的纠检错能力。之前本人在网上找到一篇关于CRC校验原理的文章,比较详细且浅显易懂:http://mp.weixin.qq.com/s/RNHLZGPD9Ysbxb1FNDn6EA
当刚看完这些资料,对CRC有了大概认识之后,我和很多初学的朋友们一样充满疑惑。CRC如何用硬件实现呢?如何对包含多个数据的数据帧进行校验呢?FCS又是如何在帧尾的下一个时钟周期就得到结果的呢?
最原始的实现方式是采用LFSR(线性反馈移位寄存器)来完成校验功能,以下是结构示意图:
寄存器个数等于G(x)最高次幂,图中gx表示链路通断,与多项式系数相对应:系数为1时连接,0则断开。数据在每个时钟周期从右侧输入1bit,且寄存器内数据右移一位。如此移位,反馈异或的过程即为待发送数据移位后对生成多项式做模2除的过程,故当全部数据位输入完毕,寄存器内部的值即为CRC校验码。我们以较简单的CRC8为例,其G(x) = x^8 + x^2 + x^1 + 1,根据上述分析得到:
reg2(i) = reg1(i-1)^reg7(i-1)^d(i)
reg1(i) = reg0(i-1)^reg7(i-1)^d(i)
reg0(i) = reg7(i-1)^d(i)
regk(i) = regk-1(i-1) (k!=0,1,2)
其中,i表示当前时刻,i-1表示上一时刻。数据位宽定为4bit,经过四个节拍,寄存器内部数据变化过程见表:
根据上述传递方程推导得出四个节拍后reg7~reg0保存的数值,试想一下:既然每个触发器内保存数值表达式已知,那么如果直接将第四行表达式赋值寄存器,下一个时钟节拍即可得到最终校验结果,而无需等待四个时钟节拍。这就是CRC校验的并行实现方式了!传统的CRC校验算法已经非常成熟,在使用过程中不需要完全自己推导公式,了解基本原理即可。至于代码,有做好的线上生成工具 Easics CRC Tool OutputLogic.com » CRC Generator CRC(循环冗余校验)在线计算_ip33.com(在线计算)我们选定CRC8,并将数据位宽定义为4bit,验证上述推导过程是否正确
// Purpose : synthesizable CRC function
// * polynomial: x^8 + x^2 + x^1 + 1
// * data width: 4
module CRC8_D4;
// polynomial: x^8 + x^2 + x^1 + 1
// data width: 4
// convention: the first serial bit is D[3]
function [7:0] nextCRC8_D4;
input [3:0] Data;
input [7:0] crc;
reg [3:0] d;
reg [7:0] c;
reg [7:0] newcrc;
begin
d = Data;
c = crc;
newcrc[0] = d[0] ^ c[4];
newcrc[1] = d[1] ^ d[0] ^ c[4] ^ c[5];
newcrc[2] = d[2] ^ d[1] ^ d[0] ^ c[4] ^ c[5] ^ c[6];
newcrc[3] = d[3] ^ d[2] ^ d[1] ^ c[5] ^ c[6] ^ c[7];
newcrc[4] = d[3] ^ d[2] ^ c[0] ^ c[6] ^ c[7];
newcrc[5] = d[3] ^ c[1] ^ c[7];
newcrc[6] = c[2];
newcrc[7] = c[3];
nextCRC8_D4 = newcrc;
end
endfunction
endmodule