我遇到了一个据称非常有效且优雅的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个循环,但我希望对此进行一些澄清。感谢您的宝贵时间。
答案 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://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存档有一个副本。这里有一些为什么以及如何工作的细节:
这最初是一个4位表,并且是为仅具有数百字节RAM的8位PIC单片机开发的。
该链接包含原始说明,表生成器程序源以及PIC的C和ASM实现。
当然,如果您要在现代PC上执行此操作,那么这是可怕的代码。与替代方案相比,它“高效”,可以在目标设备上一点一点地进行计算。