【FPGA/IC】CRC电路的Verilog实现
温馨提示:这篇文章已超过394天没有更新,请注意相关的内容是否还可用!
前言
在通信过程中由于存在各种各样的干扰因素,可能会导致发送的信息与接收的信息不一致,比如发送数据为 1010_1010,传输过程中由于某些干扰,导致接收方接收的数据却成了0110_1010。为了保证数据传输的正确性,工程师们发明了一些检错方法,比如奇偶校验和CRC校验。
CRC校验(Cyclic Redundancy Check,循环冗余校验)是数据传输过程中常用的一种检错方法,针对要发送的数据,其使用一些特定的多项式可以计算出CRC检验结果,CRC校验结果与原始数据一起传输到接收端。
接收端在接收数据的同时按照相同的多项式对接收数据进行校验预算,并将校验结果和接收的结果进行对比,如果二者相同则认为没有发生传输错误;如果不同则认为发生了传输错误。如果发生了错误,接收端可以舍弃该帧数据并通知发送端重新传输数据。
尽管理论上仍然可能存在传输数据有误,但确无法被CRC校验出来的情况,但是这个概率极低,实际应用过程中基本可以忽略。
简单来讲,CRC校验,就是通信双方事先约定好一个CRC模型(一般来讲这个模型最重要的就是多项式),假如发送的数据除以多项式后得到的余数是X,那么接收方接收到的数据除以同样的多项式后,其得到的余数也应该为A。若二者得到的余数不同,则可认为是传输过程中发生了错误。
CRC参数模型
不同的协议在传输数据时采用了不同的CRC参数模型,模型由以下参数来定义。
-
NAME:模型名称,比如CRC-8,CRC-16/USB,CRC-32等。
-
WIDTH:宽度,即生成的CRC校验位宽,如CRC-8生成的结果为8位。
-
POLY:生成多项式的简写(16进制表示)。省略最高位1,如CRC-8的多项式为 x8 + x2 + x + 1,则二进制为1_0000_0111,省略最高位1,二进制0000_0111转换为十六进制为0x07。
-
INIT:这是算法开始时寄存器的初始化值,十六进制表示。
-
REFIN:true或false,在进行计算之前,原始数据是否翻转。原始数据:0xAA = 1010_1010,如果REFIN为true,则翻转后为0101_0101 = 0x55。
-
XOROUT:计算结果与此参数进行异或运算后得到的CRC值,和WIDTH位宽一致。
-
REFOUT:true或false,运算完成之后,得到的CRC值是否进行翻转,方法同上。
下面是一些常见的CRC参数模型:
CRC算法的完整过程
假设通信双方约定的CRC模型为CRC-5/USB,发送的数据为8bits数据0xAA,即1010_1010。
-
CRC对应的多项式为0x05,补上省略的高位的1,则为10_0101。
-
REFIN为ture,所以需要翻转数据,即1010_1010 -> 0101_0101。
-
多项式的最高项为5,所以在发送数据末端补5个0,即0101_0101_00000。
-
发送数据除以多项式,即二者进行模2除法,过程如下(注意需要先异或一次初始值,由于很多模型的初始值为0,因为任何数异或0都等于它本身,所以这一步被省略掉了)。得到结果为00011。
-
将该结果与XOROUT值做异或运算,即 00011 ^ 11111 = 11100。
-
REFOUT为ture,所以需要翻转数据,即11100 -> 00111。即最终的CRC校验值为00111。
CRC算法的Verilog实现
一般有两种电路可以用来实现CRC算法,串行CRC电路和并行CRC电路。
串行CRC
串行CRC电路以LFSR为基础,每个时钟周期输入一个数据的单bit,在数据宽度个周期后移位寄存器的值即为CRC校验值。下图是CRC8-CCITT模型的电路示意图。
接下来仍然以CRC-8模型为例,编写RTL代码(CRC-8的模型比较简单,没有反转那些步骤):
//串行CRC-8:宽度8,多项式0x07(x^8+x^2+x+1),初始值00,XOROUT00,REFIN/REFOUT均为FALSE module test( input clk, //时钟 input rst, //异步复位 input ser_in, //串行输入数据 input ser_in_val, //串行输入数据有效 output [7:0] crc_data, //CRC校验数据 output crc_data_val //CRC校验数据有效 ); //定义寄存器变量 reg [7:0] crc; //LFSR寄存器实现移位、异或操作 reg [2:0] cnt; //串行输入数据计数 reg data_val; //数据输出有效信号 //连接端口 assign crc_data = crc; assign crc_data_val = data_val; //构建串行输入LFSR always @(posedge clk or posedge rst)begin if(rst) crc
-





