关于C#:CRC比特顺序混乱

CRC bit-order confusion

我正在逐位计算CCITT CRC-16。我这样做是因为它是一个原型,以后应移植到VHDL并最终在硬件中检查串行比特流。

在网上我发现了一位CRC-16更新步骤代码。编写了一个测试程序,它可以工作。除了一件奇怪的事情:我必须从最低位到最高位提供一个字节的位。如果以这种方式执行操作,我将获得正确的结果。

在CRC-16的CCITT定义中,这些位应从最高位到最低位。我要从中计算CRC的数据流也采用这种格式,因此我的当前代码对我来说是无用的。

我很困惑。我不希望以错误的方式喂入这些位完全可以起作用。

问题:为什么可以编写CRC来以两个不同的位顺序获取数据,为什么我要转换它首先接受数据MSB的位更新代码?

供参考,以下是相关代码。为了使示例简短,已删除了初始化和最终检查:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
typedef unsigned char bit;

void update_crc_single_bit (bit * crc, bit data)
{
  // update CRC for a single bit:
  bit temp[16];
  int i;

  temp[0] = data ^ crc[15];
  temp[1] = crc[0];
  temp[2] = crc[1];
  temp[3] = crc[2];
  temp[4] = crc[3];
  temp[5] = data ^ crc[4] ^ crc[15];
  temp[6] = crc[5];
  temp[7] = crc[6];
  temp[8] = crc[7];
  temp[9] = crc[8];
  temp[10] = crc[9];
  temp[11] = crc[10];
  temp[12] = data ^ crc[11] ^ crc[15];
  temp[13] = crc[12];
  temp[14] = crc[13];
  temp[15] = crc[14];

  for (i=0; i<16; i++)
    crc[i] = temp[i];
}

void update_crc_byte (bit * crc, unsigned char data)
{
  int j;
  // calculate CRC lowest bit first
  for (j=0; j<8; j++)
  {
    bit b = (data>>j)&1;
    update_crc_single_bit(crc, b);
  }
}

编辑:由于这里存在一些混乱:我必须逐位计算CRC,并且首先为每个字节计算MSB。我不能简单地存储这些位,因为上面显示的代码是某种最终将在硬件中(没有内存)的原型。

如果我按以下顺序馈入比特流,则上面显示的代码将产生正确的结果(显示为接收到的比特的索引。每个字节首先被发送MSB):

1
2
|- first byte -|-   second byte     -|-  third byte
7,6,5,4,3,2,1,0,15,14,13,12,11,10,9,8,....

我需要对单个更新循环进行转换,使其使用自然顺序(例如,按接收到的顺序)生成相同的CRC:

1
2
|- first byte -|-   second byte     -|-  third byte
0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,....


如果您查看RevEng 16位CRC目录,则会发现有两种不同的CRC称为" CCITT ",其中一种标记为" CCITT-False "。在此过程中,有人对CCITT 16位CRC是什么感到困惑,并且这种困惑广泛传播。这样就描述了这两个CRC,第一个(KERMIT)是真实的CCITT CRC:

1
2
3
KERMIT

width=16 poly=0x1021 init=0x0000 refin=true refout=true xorout=0x0000 check=0x2189 name="KERMIT"

1
2
3
CRC-16/CCITT-FALSE

width=16 poly=0x1021 init=0xffff refin=false refout=false xorout=0x0000 check=0x29b1 name="CRC-16/CCITT-FALSE"

您会注意到,真实的反映出来了,而虚假的则没有反映出来,并且初始化还有另一个区别。在反射的CRC中,首先处理数据的最低位,因此您似乎正在尝试计算真实的CCITT CRC。

当反映CRC时,多项式中与寄存器异或的位的顺序也是如此,因此0x1021变为0x8408。这是一个可以检查的简单C实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <stddef.h>

#define POLY 0x8408

unsigned crc16_ccitt(unsigned crc, unsigned char *buf, size_t len)
{
    int k;

    while (len--) {
        crc ^= *buf++;
        for (k = 0; k < 8; k++)
            crc = crc & 1 ? (crc >> 1) ^ POLY : crc >> 1;
    }
    return crc;
}

我不知道您的意思是"尽管在CRC-16的CCITT定义中,这些位应该从最高位到最低位馈送"。您指的是什么定义?

在此Altera文档中,您可以看到CRC的移位寄存器实现(用于硬件实现)。这是该图的副本:

CCITT

对于您的代码,您需要反转寄存器temp[]的索引。 temp[0]temp[15],依此类推。


通常,首先在行最低有效位上传输位。因此,如果您有一个字节数组,则第一位是第一个字节的最低有效位,然后是下一个最低有效位...所以直到第一个字节的最高有效位,然后才是最低有效位下一个字节的这是您要进行的多项式除法中位(系数)的顺序。在https://github.com/mojadita/crc.git上尝试我的例程(您有CRC16-CCITT的表)


更新-如果您查看:

RevEng 16位CRC目录

有一个链接到:

在线CRC计算器

标记为CRC-CCITT的前三个对使用多项式0x11021向LSB发送或接收的MSB数据进行运算。唯一的区别是起始值:

CRC-CCITT(XModem)-crc初始化为0x0000,与前缀0x0000相同。

CRC-CCITT(0xFFFF)-crc初始化为0xFFFF,与前缀0x84CF相同。

CRC-CCITT(0x1D0F)-crc初始化为0x1D0F,与前缀0xFFFF相同。

所以我的猜测是您要使用这三个之一。