CRC实现

CRC实现@TOC

CRC原理

crc原理是通过与固定的值(poly)异或运算得到余数实现的。具体推导过程可参考《A PAINLESS GUIDE TO CRC ERROR DETECTION ALGORITHMS》
如下所示:
数据:1010110011
固定值(poly):1001 x^3+1
计算如下:首先将数据向左移动3位,低位补零
在这里插入图片描述
#运算过程分析:可参考博客http://www.cnblogs.com/esestt/archive/2007/08/09/848856.html第一部分算法原理,写的很好;总之目的就是通过异或运算使发送数据从高位开始的首个1变为0,直到得到的数值小于poly为止。上述运算可以看成是一直左移poly,但运算过程中我们消掉了几个1无法确定,所以移动的位数也无从确定;既然我们可以移动poly那我们可不可以移动发送数据呢?具体分析可以参考上述链接博客,在此引用一段移动发送数据的分析:
通过示例,可以发现一些规律,依据这些规律调整算法:

  1. 每次迭代,根据 gk 的首位决定 b,b 是与 gk 进行运算的二进制码。若 gk 的首位是1,则 b=h;若
    gk 的首位是0,则 b=0,或者跳过此次迭代,上面的例子中就是碰到0后直接跳到后面的非零位。
    在这里插入图片描述
  2. 每次迭代,gk 的首位将会被移出,所以只需考虑第2位后计算即可。这样就可以舍弃 h 的首位,将 b
    取 h 的后 m 位。比如 CRC-8的 h 是111010101,b 只需是11010101。

在这里插入图片描述
3. 每次迭代,受到影响的是 gk 的前 m 位,所以构建一个 m 位的寄存器 S,此寄存器储存 gk 的前 m 位。
每次迭代计算前先将 S 的首位抛弃,将寄存器左移一位,同时将 g 的后一位加入寄存器。若使用此种方
法,计算步骤如下:
在这里插入图片描述
通过上述分析,很容易可以通过C实现该单bit移动的算法:

#include <stdio.h>

int main()
{	
	int data = 0x2b3 ;
	int poly = 0x1 ;
	int crc = 0x0 ;
	int index = 0 ;
	
	data <<= 3 ;//13bit 左移3bit
	poly <<= 10;//左移10bit将001与数据位对齐
	//data 1_0101_1001_1000
	//poly 0_0100_0000_0000
	for(index = 0 ;index < 10; index++)
	{
		if((data & 0x1000)==0x1000)
		{
			data = (data << 1) & 0x1fff ;
			data = (data ^ poly) & 0x1fff;
		}
		else 
		{
			data = (data << 1) & 0x1fff ;
		}
	}
	
	crc = data >> 10 ;
	printf("crc is %#x\n",crc);

	return 0 ;
}

##查表法
如上所述,单比特移动数据目的是消掉第一个出现的1,那么可不可以按照多比特移动数据将移动的多比特都消掉呢?在上述运算过程中每一次运算都与poly相异或得到结果再与poly异或循环往复,那么我们可不可以先把所有的poly异或掉再与数据异或呢?通过异或运算的结合律是可以的:A ^ B ^ C = A ^ (B ^ C) = (A ^ B) ^C ;再看上述运算可推导如下:

在这里插入图片描述
如上图所示,先将poly相异或再与原来的数据相异或可以消掉多位为1的bit,由此我们可以设想:通过先将poly移位异或,异或的值等于我们要移动位数的数据对应值时在与数据异或那么我们可以消掉移动的数据位中所有的1bit位,因为A ^ A = 0 ;如假设我们左移4位数据,对应的值为A,我们可以先将poly移动并异或得到高四位数据X结果为A,那么此时数据与X异或高4位就消掉了。那么我们选择多少位比较好呢?由于计算机处理时按字节比较方便,所以我们可以按照字节来计算,每次数据移动一个字节;接下来的问题是,我们怎么来预先计算出poly对应的X值呢?一个字节8bit对应0~255 256个数据,我们可以预先通过poly来算出这256个数值对应的X值存在一个表中。具体推导过程可以参考http://www.cnblogs.com/esestt/archive/2007/08/09/848856.html第二部分查表法,这里不做摘抄了。
我们接下来用标准的CRC-32来推导出计算:
CRC-32 POLY 0x04C11DB7
我们可以借用上述单bit运算,C实现如下:

void crc32_table(unsigned int *table)
{	
	unsigned int row = 0 ;//0~255数据
	unsigned int column = 0 ;//每个数据的bit数量
	unsigned int temp = 0 ;//数据移位后的值
	for(row = 0; row < 255; row ++)
	{
		temp = (row << 24);//将数据移动到最高位,与poly对齐
		for(column = 0;column<8;column++)
		{
			if((row & 0x80000000)==0x80000000)//判断数据最高位是否为1,是1就消掉
			{
				temp <<= 1 ;
				temp ^= POLY ;
			}
			else //否则跳过
			{
				temp <<= 1 ;
			}
		}
 	*table = temp ;
 	table++ ;
	}
}
	

通过上述运算在table中存储的数据就是消掉0~255对应的结果。
##接下来就通过上述表和移动1个字节来实现计算,C实现代码如下:

unsigned int crc32_calculate(char *datin, int len)
{
	int loop = 0 ;
	unsigned int index = 0 ;//查表索引值
	unsigned int crc = 0 ;//crc计算结果
	unsigned int reg = 0 ;//定义一个寄存器,初值为0
	for(loop = 0; loop < len; loop++)
	{
		index = (reg >> 24) & 0xff ;
  		reg = (reg << 8) | *datin ;
		reg = reg ^ table[index];		
		datin++;
	}
}

但目前还面临着一个问题就是怎样将传进来的数据datin末尾要加32bit 的0;来解决该问题我们有两种方案:#第一个方案我们可以在最后补充上这32bit的0;C实现如下:

unsigned int crc32_calculate(char *datin, int len)
{	
	int loop = 0 ;
	unsigned int index = 0 ;
	unsigned int crc = 0 ;
	unsigned int reg = 0 ;
	for(loop = 0 ;loop < len ;loop++)
	{
		index = (reg >> 24) & 0xff ;
		reg = (reg << 8) | *datin ;
		reg = reg ^ table[index];
		datin++;
	}
	for(loop = 0;loop < 4;loop++)//填充4byte的0
	{
		reg = (reg << 8) ^ table[(reg >> 24) & 0xFF]; 
	}	
	crc = reg ;	
	return crc ;
}

#第二种方案我们可以调整运算顺序来实现不需要在数据末尾填充4Byte的0;具体分析如下:
1.填充0并不影响计算结果,因为任何数与0异或都保持原来的值,并且这4Byte 0不再会被移出寄存器了;
2.最后寄存器填充4Byte 0 的目的是使滞留在寄存器中的有效4Byte数据参与运算;
3.如果寄存器的初始值为0,那么前四次移位的作用只是将有效数据的前四个字节移入寄存器,未做任何运算;
4.即便是寄存器初始值不为0,那么前四次移位的作用是将有效数据前四个字节移入寄存器,并将整个结果异或了一个常量(初始值)。
综上所述:结合异或运算的交换律,我们可以不用将数据通过从右往左移入寄存器,而是将数据与寄存器的最高字节异或后作为查表的索引,查到的数据再与寄存器中的值异或。C实现如下所示:

unsigned int crc32_calculate(char *datin, int len)
{ 
 int loop = 0 ;
 unsigned int index = 0 ;
 unsigned int crc = 0 ;
 unsigned int reg = 0 ;
 for(loop = 0 ;loop < len ;loop++)
 {
  index = (reg >> 24) ^ *datin ;
  reg <<= 8 ;
  reg = reg ^ table[index];
  datin++;
 }
 crc = reg ; 
 return crc ;
}

但目前还是不能得到CRC-32网络数据包校验的正确结果,因为CRC校验对寄存器初始值和输出结果都有规定
待续。。。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
CRC(循环冗余校验)是一种常用的错误检测技术,用于检查数据传输过程中的错误。而Verilog是一种硬件描述语言,可以用于设计和实现数字电路。 要实现CRC功能,可以采用Verilog语言来描述和实现CRC模块。首先需要定义CRC生成多项式以及初始值。然后,根据CRC的计算规则,设计一个计算模块来对输入数据进行校验。 在Verilog中,可以使用shift register(移位寄存器)实现CRC计算。移位寄存器可以用于存储数据,并通过移位操作来实现数据的移动。通过对数据进行位移和异或运算,即可实现CRC校验的计算。 CRC Verilog模块的输入包括待校验数据和生成多项式,输出为校验结果。在Verilog中,可以使用for循环来实现多位数的移位和异或运算。通过连续迭代,每次循环对输入数据进行异或和移位操作,最终得到校验结果。 为了测试CRC模块,可以使用测试平台进行模拟运行。在测试平台中,可以为CRC模块提供输入数据,并捕获和验证输出的校验结果。通过对不同数据和生成多项式进行测试,可以验证CRC模块的正确性和可靠性。 总之,通过Verilog语言实现CRC模块可以有效地检测数据传输中的错误。通过定义生成多项式和初始值,并使用移位寄存器进行移位和异或运算,可以实现CRC校验的计算。使用测试平台对模块进行验证,可以确保CRC模块的正确性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值