强调有效的CRC-CCITT-16实施

时间:2019-11-11 03:59:36

标签: c crc crc16 coding-efficiency

我遇到了一个据称非常有效且优雅的CRC实现,并且我试图真正理解所有步骤。我了解CRC-CCITT 0x1021的实现会在每个位上进行迭代,但是我一直在努力获取这一位。这是代码。

/*
* Original Code by Ashley Roll 
*/

uint16_t crc16(uint8_t* data, uint16_t length){

  uint8_t x;
  uint16_t crc = 0xFFFF;

  while (length--){
      x = crc >> 8 ^ *data++;
      x ^= x>>4;
      crc = (crc << 8) ^ ((uint16_t)(x << 12)) ^ ((uint16_t)(x <<5)) ^ ((uint16_t)x);
  }

  return crc;
}

有人可以向我进一步解释x变量的情况吗?这是我到目前为止所能掌握的

x = crc >> 8 ^ *data++; // Here, we take the high byte of the register
                        // and we XOR it with the next 8 bits of data. Why?
x ^= x>>4;              // Then, we XOR it with its last four bits?
                        // And we always remain with 4 possible non-zero bits, right?
                        // Why do we do this?
crc = (crc << 8) ^ ((uint16_t)(x << 12)) ^ ((uint16_t)(x <<5)) ^ ((uint16_t)x);
                        // Finally, we drop the oldest (highest) byte from the register
                        // And XOR it with rotations of x, according to the polynomial
                        // 0x1021, which is x^16 + x^12 + x^5 + 1
                        // Is (16-12) = 4 the reason why x has 4 possible non-zero bits?

我猜想该算法仅相当于按位算法的8个循环,但我希望对此进行一些澄清。感谢您的宝贵时间。

2 个答案:

答案 0 :(得分:4)

如果代码中包含x是使用二进制无借位除法一次处理8位的商的注释,那将会有所帮助。

crc将数据+ 16个填充位(零)除以多项式,其余为CRC。

poly = x^16 + x^12 + x^5 + 1 = 0x11021 = 10001000000100001 (binary)

要一次处理8位,每个步骤将aaaabbbb0000000000000000位除以10001000000100001,其中aaaabbbb是crc的高8位与下一个数据字节进行异或。这可以在一个除法步骤中完成,方法是注意商= x =(aaaabbbb)^(aaaabbbb >> 4)= aaaacccc,其中c = a ^ b,所以a ^ c = b,而a ^ b ^ c = 0:

                                    aaaacccc
                  --------------------------
10001000000100001 | aaaabbbb0000000000000000
                                    aaaacccc
                               aaaacccc
                        aaaacccc
                    aaaacccc
                            ----------------
                            cccbaaacbbbacccc

在问题代码中,不会生成高于x ^ 16的位,因为已知它们会抵消x ^ 16到x ^ 23的位。左移12位移出高4位,对应于x ^ 16至x ^ 19位,并且没有左移16。


example: data = 0x31 0x32 = 00110001 00110010

crc = 1111111111111111
x = (crc>>8)^00110001 = 11001110
q = (x)^(x>>4) = 11000010

                                   11000010
                  -------------------------
10001000000100001 |110011100000000000000000
                                   11000010
                              11000010
                       11000010
                   11000010
                           ----------------
                           0011100010000010

crc = (crc<<8)^0011100010000010 = 1100011110000010
x = (crc>>8) ^ 00110010 = 11110101
q = (x)^(x>>4) = 11111010

                                   11111010
                  -------------------------
10001000000100001 |111101010000000000000000
                                   11111010
                              11111010
                       11111010
                   11111010
                           ----------------
                           1011111110111010

crc = (crc<<8)^1011111110111010 = 0011110110111010 = 0x3dba

如前所述,表查找应该更快。如果cpu具有快速的无进位乘法,例如X86 pclmulqdq,则可以使用速度更快的静态汇编程序,但是它的长度超过500行(使用xmm寄存器一次“折叠” 128个字节)。下面的汇编代码未记录常量(rk ...);除了rk7 =“(2 ^ n)/ polynomial”和rk8 =多项式外,它们是2的幂乘以用于“折叠”的多项式的模。

https://www.intel.com/content/dam/www/public/us/en/documents/white-papers/fast-crc-computation-generic-polynomials-pclmulqdq-paper.pdf

https://github.com/intel/isa-l/blob/master/crc/crc16_t10dif_01.asm

我将代码转换为Visual Studio ML64.EXE(MASM)和Windows,并且具有crc16,crc32,crc64,非反射(非反向)和反射)的源代码。

答案 1 :(得分:2)

这有点晚了,我很感激。我开发了基于2002年的原始代码。此后,我将该网站删除了,但是Internet存档有一个副本。这里有一些为什么以及如何工作的细节:

https://web.archive.org/web/20160408050435/http://www.digitalnemesis.com/info/codesamples/embeddedcrc16/

这最初是一个4位表,并且是为仅具有数百字节RAM的8位PIC单片机开发的。

该链接包含原始说明,表生成器程序源以及PIC的C和ASM实现。

当然,如果您要在现代PC上执行此操作,那么这是可怕的代码。与替代方案相比,它“高效”,可以在目标设备上一点一点地进行计算。