深入理解CRC算法

  • 一.生成多项式
  • 二.CRC运算
  • 1.基本介绍
  • 2.运算实例
  • 三.CRC算法解析(零填充)
  • 1.C代码
  • 2.流程图(可用FPGA实现)
  • 3.原理分析
  • 四.CRC算法解析(无需零填充)
  • 1.C代码
  • 2.流程图(可用FPGA实现)
  • 3.原理证明(数学归纳法)
  • (1) 起点:(n=0)
  • (2) n个比特 => n+1个比特
  • 五.其他
  • 1.总结
  • 2.快速查表法
  • 3.串行CRC应用场合


一.生成多项式

以比特流 11001001为例,其生成多项式为:
G(x) = x^7 + x^6 + x^3 + 1
至于为何使用指数的形式,是因为生成多项式引用了多项式运算,x^n在n不同时不可以合并,这样可以保证一个多项式与比特流的关系是一一对应的,不同比特位不会合并。
CRC校验使用的生成多项式的最高位与最低位必须为1,由于最高位一定为1,所以CRC校验使用的多项式的位宽 = 总比特数 - 1。
例如,G(x) = x^7 + x^6 + x^3 + 1的多项式位宽 = 8 - 1 = 7,省去最高位,其余位简记为
7’b1001001 ( 0x49 ), 该CRC运算称为CRC-7。

二.CRC运算

1.基本介绍

CRC校验,就是在一串比特流之后添加一定比特数的CRC校验位(冗余信息),使得整个比特流除以某个特定的生成多项式,就可得到余数为0。
如果余数不为0,则说明数据传输过程中有错误发生。

在CRC运算空间中,加减法使用“按位异或”运算(即模2加),乘除法可以由多步加减法(按位异或)实现,以下异或运算符均用⊕表示。

2.运算实例

例如: 原始比特流:101
生成多项式:10011 (保留最高位)(去掉最高位后为4bits的0011,即0x3)
CRC-4
运算流程如下:

为101后面补4个0,之后除10011


crc全算法java crc算法详解_多项式

算出校验值后(此例中为4位),添加至比特流101尾部,得到
1011111,发送方将该比特流发送出去,接收方接收到后,如果除以10011后的余数为0,则接收方判定接收到的比特流无错误。

三.CRC算法解析(零填充)

1.C代码

以CRC运算中的例子(需要在比特流末尾填充4个0)为例,C代码如下:

u8 fun( bool bitArray[], int bitArrayNum )//bitArray[]已经在末尾添加4个0
{
	u8 crcRes = 0;//初始化为0,有效比特空间设为4比特
	
 	int i;
	for ( i = 0; i < bitArrayNum; i ++ )
	{
		u8 curBit = bitArray[ i ] ;
		u8 msb = (crcRes & 0x8)>>3;//取出最高位crcRes[3]

		crcRes  = (crcRes << 1)| curBit ; //左移1位( crcRes[3:0] = { crcRes[2:0], curBit  }),
                                           //低比特进入最低位
		if (  msb  )//最高位为1
		{
			crcRes  = crcRes  ^ 0x3;//0x3为生成多项式去掉最高位,4比特
		}
	}
}

2.流程图(可用FPGA实现)

crc全算法java crc算法详解_CRC_02


图中的4位reg 对应crcRes 的4位存储空间。

3.原理分析

接下来进行代码详细原理分析:
一开始,crcRes [3:0] 的最高位为0,因此,只需左移。
经过4次左移运算后,输入比特流第一位的‘1’抵达最高位,
此时,crcRes [3:0] = 1010。
当最高位为1时,被减数10100需要减去(异或)10011,由于最高位相减为0,所以可以无视它,因此,在获取crcRes [3:0] = 1010中的最高位后,将crcRes 左移,并添加新比特0。这时crcRes =0100,恰好为被减数的低4位。因此10100⊕10011只需要低4位进行异或运算(0100⊕0011),就可以得到新的CRC运算结果(0111),这就是为什么经常将生成多项式最高位省略的原因。
之后如果再碰到最高位为1的情况,以此类推。
如果又碰到最高位为0的情况,只需要左移crcRes ,添加新比特进入低比特。

至此,算法已剖析完毕。

四.CRC算法解析(无需零填充)

我在研究SD卡的SDIO接口时,接触到CRC-7与CRC-16。
由于查到的原理图的算法不需要在比特流后面进行0填充,所以,我对这种不需0填充的
CRC算法进行研究:

1.C代码

u8 fun( bool bitArray[], int bitArrayNum )//bitArray[]无需填充0
{
    u8 crcRes = 0;//初始化为0,有限空间设为4比特
    int i;
    for ( i = 0; i < bitArrayNum; i ++ )
    {
        u8 curBit = bitArray[ i ] ;
        u8 msb = (crcRes & 0x8)>>3;//取出最高位crcRes [3]
        
        crcRes  = (crcRes   << 1); //左移1位( crcRes[3:0] = { crcRes[2:0], 1'b0 } )
        
        if (  msb ^ curBit )//最高位⊕新输入比特 == 1
        {
            crcRes  = crcRes ^ 0x3;//0x3为生成多项式去掉最高位,4比特   
        }
    }
 }

2.流程图(可用FPGA实现)

crc全算法java crc算法详解_CRC_03


图中的4位reg 对应crcRes 的4位存储空间。

3.原理证明(数学归纳法)

(1) 起点:(n=0)

当没有数据需要检验时(0个比特),为了使余数为0,校验位必定全为0。

(2) n个比特 => n+1个比特

设n个比特(XXXXXX)(n>=0)的CRC已经算出,CRC检验位为4’bABCD。
因此,XXXXXXABCD必定可以整除10011,
用 XXXXXXABCD 乘多项式 10(x^1 + x^0 = x),
得到的 XXXXXXABCD0 也可以被 10011 整除,
此时,将XXXXXXABCD0 ⊕ 10011 后的结果仍然可以整除10011。
在 XXXXXXABCD0 中,新比特(第n+1比特)位置与A比特位相同,
为此,我们只需讨论不同的A与加不加10011会在原A比特位置产生新的0还是1

A = 0

⊕10011

新比特为1

A = 0

⊕00000

新比特为0

A = 1

⊕10011

新比特为0

A = 1

⊕00000

新比特为1

归纳可得,

新比特 ⊕ A == 1

(n+1)个比特的CRC校验值 = “ABCD0⊕ 10011” 的后4位

新比特 ⊕ A == 0

(n+1)个比特的CRC校验值 = “ABCD0⊕ 00000” 的后4位

其中,A为上一步计算得到的n个比特的CRC校验值的最高位。

又如之前带0填充CRC校验的算法,该无需0填充的CRC算法,利用最高位判断完条件后舍弃最高位,只需生成多项式的后4位参与异或运算。

因此,第n步的CRC校验值左移1位,
之后根据newBitIn ⊕ A的值进行选择。
当 newBitIn ⊕ A == 1时,BCD0 与 4’b0011(0x3) 进行异或运算,就可得到第n+1步的CRC校验值;否则,BCD0 就是第n+1步的CRC校验值。

五.其他

1.总结

以上就是本人最近研究CRC基础的心得。共讨论了两种串行CRC算法的原理。
从填充0到不填充0的算法改进,我认为是质的飞越,因为后者可以根据当前的CRC直接算出多一个比特的CRC,不必重新进行0填充,计算周期更短。
不填0的CRC结构学名为LFSR(线性反馈移位寄存器),运行效率高,同时继承填0算法中节省reg空间资源的优点(本例中,不需要5个比特的reg,只需要4个比特的reg)。

希望大家通过我的文章,能够深入理解CRC算法的真谛。把握原理后就可以轻松的应对其他各式各样的生成多项式,也能够独立的画出流程图。

2.快速查表法

关于查表法计算CRC,大家可参考其他博主的文章,本文章只提供一个依据:
n个比特的CRC可以直接算出加入新比特后,(n+1)比特的CRC,
因此,用n个比特的CRC也可以算出(n+8)个比特的CRC,此时将新的8比特制成查找表,就可以进行快速CRC算法。

3.串行CRC应用场合

并非串行的CRC计算一定不好。首先,某些偏底层硬件的场合,比如SD卡读写的CRC校验,数据以串行方式传输,所以不需要非常快的速度,可以来一个比特算一次CRC。其次,使用串行方式可以节省存储空间,还可节省FPGA的逻辑资源,结构简单。