FPGA(一)——基于FPGA的CRC算法实现

一. CRC算法基本原理

1. CRC算法介绍

循环冗余检验(Cyclic Redundancy Check,CRC)模块,是数据通信中一种常用的,纠错能力很强的数据检错模块,,该检验编码常用于串行通信。CRC校验码的基本思想是利用线性编码的原理,在发送端根据要传送的k位二进制码序列,以一定规则产生一个校验用的监督码(CRC码)r位,并附在信息后面组成一个k+r位的发送码。在接收端,根据信息码和CRC码之间所遵循的规则进行检验,以确定传送过程是否出错。接收端处理方法通常有两种:

  1. 计算k位序列的CRC码,与接受端的CRC比较,若二者一致则接收正确;
  2. 计算整个k+r位的CRC码,按计算规则进行模2除法运算,若运算结果为0,则传输接收正确

模2运算规则:

- 模2加法:0+0=0 0+1=1 1+0=1 1+1=0
- 模2减法:0-0=0 0-1=1 1-0=1 1-1=0
- 模2乘法:0x0=0 0x1=0 1x0=0 1x1=1
- 模2除法:0/1=0 1/1=1

2. 信息代码多项式和生成多项式

信息代码多项式: 在纠错编码代数中,把以二进制数字表示的一个数据系列堪称一个多项式。例如,二进制序列10011,用多项式可以表示成:在这里插入图片描述
M(x)称为信息代码多项式。

生成多项式: 当进行CRC检验时,发送方和接收方需事先约定一个除数,即生成多项式G(x),生成多项式的最高位和最低位必须是1,下面给出几种标准的CRC码生成多项式:

CRC码 生成多项式G(x)
CRC-4 在这里插入图片描述
CRC-5 在这里插入图片描述
CRC-8 在这里插入图片描述
CRC-9 在这里插入图片描述
CRC-12 在这里插入图片描述
CRC-16 在这里插入图片描述
CRC-CCITT 在这里插入图片描述
CRC-32 在这里插入图片描述

国际通行标准查看
在数据存储和数据通讯领域,CRC检验无处不在:著名的通讯协议X.25的FCS(帧检错序列)采用CRC算法检错,以及CCITT,ARJ,LHA等压缩工具软件采用CRC32,磁盘驱动器的读写采用CRC16,通用的图像存储格式GIF,TIFF等也都采用CRC 作为检测手段。

3. CRC校验码计算规则

CRC校验方法:通过生成多项式,借助多项式除法,所得余数为校验字段,则发出数据信息字段+校验字段构成;接收方采用相同的生成多项式对发出数据进行检验,利用多项式除法,若能够除尽则传输正确。

例:

  1. 信息字段:1011001;对应多项式
    在这里插入图片描述

  2. 选取生成多项式:CRC-4, 11001;

  3. 对应的被除数代码: 10110010000;对应多项式
    在这里插入图片描述

  4. 采用多项式除法:10110010000 / 11001得余数为1010,即1010为检验字段,因此发出得传输字段为:1011001|1010 (信息字段|检验字段)
    FPGA(一)——基于FPGA的CRC算法实现_第1张图片

  5. 接收方采用传输字段除以相同的生成码CRC-4: 11001,即10110011010 / 11001 ,所得结果余数为0,证明传输正确。
    FPGA(一)——基于FPGA的CRC算法实现_第2张图片

二. 案例与FPGA实现(源码)

1. 案例描述

利用CRC原理,基于FPGA设计一个8位CRC,要求输入din为64 bit 数据,生成多项式为CRC-8 : 100000111,输出crc_o 为8 bit 校验字段:

  • 输入数据 din :64 bit ;
  • 生成多项式 poly :100000111 ;
  • 输出数据 crc_o :8 bit ;
  • 置位端 rst_n :高电平触发 ;

2. 设计思路

  1. 通过上述crc算法求解校验字段代码和模2除法规律可知,模2除法在可除的情况下,等效于利用生成多项式与相应的位数进行异或运算,因此考虑可以将64 bit 输入数据依次进行移位,并逐次与规定的生成多项式CRC-8进行异或运算,这个是很好实现的;

  2. 为了使求得余数比生成多项式少一位,即输出crc_o为8位,应在对输入数据进行运算前在其后补充8个“0”;

  3. 若用生成多项式与数据位数进行逐位异或将显得十分繁琐(如下):

always @ (posedge clk or negedge rst_n)
       begin 
          if(!rst_n)             
				  begin
               crc_o<=0;               
					temp<=stemp; 
              end
           else 
               begin
               if(temp[71]) temp[71:63]<=temp[71:63]^polynomial;               
					else if(temp[70]) temp[70:62]<=temp[70:62]^polynomial;
					else if(temp[69]) temp[69:61]<=temp[69:61]^polynomial;
               else if(temp[68]) temp[68:60]<=temp[68:60]^polynomial;
               else if(temp[67]) temp[67:59]<=temp[67:59]^polynomial;
               else if(temp[66]) temp[66:58]<=temp[66:58]^polynomial;
               else if(temp[65]) temp[65:57]<=temp[65:57]^polynomial;
               else if(temp[64]) temp[64:56]<=temp[64:56]^polynomial;
					else if(temp[63]) temp[63:55]<=temp[63:55]^polynomial;
					else if(temp[62]) temp[62:54]<=temp[62:54]^polynomial;
					else if(temp[61]) temp[61:53]<=temp[61:53]^polynomial;
					else if(temp[60]) temp[60:52]<=temp[60:52]^polynomial;
					else if(temp[59]) temp[59:51]<=temp[59:51]^polynomial;
               else if(temp[58]) temp[58:50]<=temp[58:50]^polynomial;
               else if(temp[57]) temp[57:49]<=temp[57:49]^polynomial;
               else if(temp[56]) temp[56:48]<=temp[56:48]^polynomial;
               else if(temp[55]) temp[55:47]<=temp[55:47]^polynomial;
               else if(temp[54]) temp[54:46]<=temp[54:46]^polynomial;
					else if(temp[53]) temp[53:45]<=temp[53:45]^polynomial;
					else if(temp[52]) temp[52:44]<=temp[52:44]^polynomial;
					else if(temp[51]) temp[51:43]<=temp[51:43]^polynomial;
					else if(temp[50]) temp[50:42]<=temp[50:42]^polynomial;
					else if(temp[49]) temp[49:41]<=temp[49:41]^polynomial;
               else if(temp[48]) temp[48:40]<=temp[48:40]^polynomial;
               else if(temp[47]) temp[47:39]<=temp[47:39]^polynomial;
               else if(temp[46]) temp[46:38]<=temp[46:38]^polynomial;
               else if(temp[45]) temp[45:37]<=temp[45:37]^polynomial;
               else if(temp[44]) temp[44:36]<=temp[44:36]^polynomial;
					else if(temp[43]) temp[43:35]<=temp[44:35]^polynomial;
					else if(temp[42]) temp[42:34]<=temp[44:34]^polynomial;
					else if(temp[41]) temp[41:33]<=temp[44:33]^polynomial;
					else if(temp[40]) temp[40:32]<=temp[40:32]^polynomial;
					else if(temp[39]) temp[39:31]<=temp[39:31]^polynomial;
               else if(temp[38]) temp[38:30]<=temp[38:30]^polynomial;
               else if(temp[37]) temp[37:29]<=temp[37:29]^polynomial;
               else if(temp[36]) temp[36:28]<=temp[36:28]^polynomial;
               else if(temp[35]) temp[35:27]<=temp[35:27]^polynomial;
               else if(temp[34]) temp[34:26]<=temp[34:26]^polynomial;
					else if(temp[33]) temp[33:25]<=temp[33:25]^polynomial;
					else if(temp[32]) temp[32:24]<=temp[32:24]^polynomial;
					else if(temp[31]) temp[31:23]<=temp[31:23]^polynomial;
					else if(temp[30]) temp[30:22]<=temp[30:22]^polynomial;
					else if(temp[29]) temp[29:21]<=temp[29:21]^polynomial;
               else if(temp[28]) temp[28:20]<=temp[28:20]^polynomial;
               else if(temp[27]) temp[27:19]<=temp[27:19]^polynomial;
               else if(temp[26]) temp[26:18]<=temp[26:18]^polynomial;
               else if(temp[25]) temp[25:17]<=temp[25:17]^polynomial;
               else if(temp[24]) temp[24:16]<=temp[24:16]^polynomial;
					else if(temp[23]) temp[23:15]<=temp[23:15]^polynomial;
					else if(temp[22]) temp[22:14]<=temp[22:14]^polynomial;
					else if(temp[21]) temp[21:13]<=temp[21:13]^polynomial;
					else if(temp[20]) temp[20:12]<=temp[20:12]^polynomial;
					else if(temp[19]) temp[19:11]<=temp[19:11]^polynomial;
               else if(temp[18]) temp[18:10]<=temp[18:10]^polynomial;
               else if(temp[17]) temp[17:9]<=temp[17:9]^polynomial;
               else if(temp[16]) temp[16:8]<=temp[16:8]^polynomial;
               else if(temp[15]) temp[15:7]<=temp[15:7]^polynomial;
               else if(temp[14]) temp[14:6]<=temp[14:6]^polynomial;
			   else if(temp[13]) temp[13:5]<=temp[13:5]^polynomial;
			   else if(temp[12]) temp[12:4]<=temp[12:4]^polynomial;
		   	   else if(temp[11]) temp[11:3]<=temp[11:3]^polynomial;
			   else if(temp[10]) temp[10:2]<=temp[10:2]^polynomial;
			   else if(temp[9]) temp[9:1]<=temp[9:1]^polynomial;
               else if(temp[8]) temp[8:0]<=temp[8:0]^polynomial;
               else   crc_o<=temp[7:0];                  
					end
              end  

因此为了简便代码,这里将72 bit 写进数组后,对数组内的数依次向左侧进行移位,而保证始终使前9位与poly生成多项式进行异或,这样省去了繁琐的代码,仅用两行代码便可实现(见FPGA源码部分);

  1. 通过进一步计算发现,并不需要对每一位都进行一次异或运算,由于生成多项式最高位始终为1,因此只有当遇到左移的前9位中,最高位为1时方才需要进行异或运算,若最高位为0则可直接进行移位,这样使算法效率提高。因此对于是否需要进行异或运算,只需对移位后的最高位,即[71]位进行判断是否为1,同时生成多项式最高位为1,因此若需要进行异或运算,最高位的结果始终是0。这样,只需判断最高位是否为1,若为1,将后8位与生成多项式后8位进行异或运算后向前移一位;若为0,则不需进行异或运算,直接左移一位。

3. FPGA源码

FPGA实现代码如下:


module crc_8 (    clk,
                rst_n,
                din,
                crc_start,
                crc_vld,
                crc_o
                );
                
input           clk;
input           rst_n;
input   [63:0]  din;
input           crc_start;
output          crc_vld;
output  [7:0]   crc_o;

parameter poly = 8'h07;

reg             crc_vld;
reg     [71:0]  din_tmp;
reg     [5:0]   cnt;
reg             pro_flag;
wire    [7:0]   crc_o;

//pro_flag
always @ (posedge clk or negedge rst_n)
if (~rst_n)
    pro_flag <= 1'b0;
else if (crc_start)
    pro_flag <= 1'b1;
else if (cnt == 6'd63)
    pro_flag <= 1'b0;
    
//cnt
always @ (posedge clk or negedge rst_n)
if (~rst_n)
    cnt <= 6'b0;
else if (pro_flag)
    cnt <= cnt + 1'b1;
else
    cnt <= 6'b0;
    
//din_tmp
always @ (posedge clk or negedge rst_n)
if (~rst_n)
    din_tmp <= 72'b0;
else if (crc_start)
    din_tmp <= {din, 8'b0};
else if (pro_flag)  
	begin
    din_tmp[71:64] <= din_tmp[71] ? din_tmp[70:63] ^ poly : din_tmp[70:63];
    din_tmp[63:0] <= {din_tmp[62:0],1'b0};
    end

//crc_vld
always @ (posedge clk or negedge rst_n)
if (~rst_n)
    crc_vld <= 1'b0;
else if (cnt == 6'd63)
    crc_vld <= 1'b1;
else
    crc_vld <= 1'b0;
    
assign crc_o = crc_vld ? din_tmp[71:64] : 'b0;

endmodule    

testbench:

`timescale 1ns/1ns

module tb_crc();

reg             clk;
reg             rst_n;
reg     [63:0]  din;
reg             crc_start;
wire            crc_vld;
wire    [7:0]   crc_o;



crc i_crc  (    .clk            (clk      ),
                .rst_n          (rst_n    ),
                .din            (din      ),
                .crc_start      (crc_start),
                .crc_vld        (crc_vld  ),
                .crc_o          (crc_o    )
                );
                
always #5 clk = ~clk;

initial begin
    clk = 1'b0;
    rst_n = 1'b0;
    din = 'b0;
    crc_start = 1'b0;
    
    #100;
    rst_n = 1'b1;
    
    @(posedge clk) ;
    din = 64'h12345678;
    crc_start = 1'b1;
    @(posedge clk) ;
    crc_start = 1'b0;
    
    @(posedge crc_vld);
    #2;
    
    if (crc_o == 8'h1c) 
    	$display("Correct !");
    else  
    	$display("Incorrect !");
    
    #50;
    $stop;
    
end

endmodule
              

你可能感兴趣的:(FPGA)