CRC位序混淆

3
我正在逐位计算CCITT CRC-16位。我这样做是因为它是一个原型,后来应该被移植到VHDL并最终成为硬件,以检查串行比特流。
在网上,我找到了一个单比特CRC-16更新步骤代码。编写了一个测试程序,它可以工作。除了一件奇怪的事情:我必须从低位到高位提供一个字节的位。如果我这样做,我会得到正确的结果。
在CRC-16的CCITT定义中,比特应该从最高位到最低位喂入。我要计算CRC的数据流也是这种格式,所以我的当前代码对我来说有点无用。
我很困惑。我本来不希望错误地输入位能够起作用。
问题:为什么CRC可以编写为采用两种不同的位顺序,并且如何转换我的单比特更新代码,使其接受MSB优先的数据?
以下是相关代码(已删除初始化和最终检查以使示例简短):
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)输入比特流,则上面显示的代码将生成正确的结果:

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

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

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

不太清楚你的问题。如果你想让它以相反的方向运行,只需将位循环的方向切换即可。 - Oliver Charlesworth
@OliverCharlesworth 我可以用C语言做到这一点,但是我无法在硬件上实现,因为我没有办法在接收数据时存储位。 - Nils Pipenbrinck
抱歉,我不明白!你是说上面的代码运行的方向与你想要的相反吗?(即它与传输顺序不匹配?)如果是这样的话,只需在代码中翻转顺序,使其匹配即可。 - Oliver Charlesworth
@OliverCharlesworth 我添加了一些更多的信息,希望这样能更清楚明白。 - Nils Pipenbrinck
这个问题涉及太多硬件相关的内容。Stack Overflow上的人群根本不理解我在问什么。我打算去news://comp.dsp试试运气。 - Nils Pipenbrinck
兄弟,说真的,如果你现在需要以最高有效位(MSB)顺序提供比特数据,那就简单地改变循环顺序:http://ideone.com/4yE2qu。如果你指的不是这个问题,那不是因为没有人理解硬件(我懂),而是因为你没有足够清楚地解释问题... - Oliver Charlesworth
3个回答

6
如果您查看RevEng 16-bit CRC目录,您会发现有两种不同的CRC被称为“CCITT”,其中一个被标记为“CCITT-False”。在某个地方,有人对CCITT 16位CRC产生了困惑,并且这种困惑被广泛传播。这两个CRC被描述如下,第一个(KERMIT)是真正的CCITT CRC:
KERMIT

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

并且

CRC-16/CCITT-FALSE

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

请注意,真实的CRC被反映出来,而虚假的则没有,并且在初始化方面还有另一个区别。在反射CRC中,数据的最低位首先被处理,因此看起来您正在尝试计算真正的CCITT CRC。
当CRC被反映时,异或到寄存器中的多项式的位顺序也会被反映,因此0x1021变为0x8408。这是一个简单的C实现,您可以进行检查:
#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;
}

我不知道你所说的“在CCITT CRC-16定义中,位应该从高位到低位进行输入”。你指的是哪个定义?

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

CCITT CRC shift register with taps at register 16, 10, and 3

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


请注意,Altera文档中的LFSR向右移位,但LFSR的最低有效位(XOR0)在左侧,而LFSR的最高有效位(XOR16)在右侧(在寄存器0下方)。目前尚不清楚Data In是以最高有效位优先还是最低有效位优先。 - rcgldr
下一个问题是CRC如何附加到数据中(最高有效位优先还是最低有效位优先)?如果我使用RevEng提供的CRC计算器,将十六进制输入0x80转换为CRC值时,结果是十六进制值0884,即8408颠倒顺序。 - rcgldr
原始帖子要求实现一个以最高有效位(MSB)为先的循环冗余校验(CRC),由于CRC是线性映射(类似于使用1位值和异或操作而不是加法进行的矩阵乘法),它可以完成,但我认为这可能会很复杂。 - rcgldr
CRC是以最低有效位先发送的。 - Mark Adler
原帖中的说法“CCITT对CRC-16的定义是应该从最高位到最低位进行计算”的观点是错误的。 - Mark Adler
显示剩余3条评论

0

更新 - 如果你看一下:

RevEng 16位CRC目录

那里有一个链接到:

在线CRC计算器

前三个都标记为CRC-CCITT,使用多项式0x11021以MSB到LSB的方式发送或接收数据。唯一的区别在于起始值:

CRC-CCITT (XModem) - crc初始化为0x0000,与在前面加上0x0000相同。

CRC-CCITT (0xFFFF) - crc初始化为0xFFFF,与在前面加上0x84CF相同。

CRC-CCITT (0x1D0F) - crc初始化为0x1D0F,与在前面加上0xFFFF相同。

所以我的猜测是你想要使用其中的一个。


CCITT(即ITU-T)包括许多标准,其中包括多个CRC-16标准 - CCITT:维基百科CRC标准。因此,如果您有一个示例输入和生成的CRC,将有助于确定使用的CRC-16 - CCITT版本。 - rcgldr

0
通常情况下,位元是以最低有效位(LSB)优先的方式传输。因此,如果你有一个字节数组,第一个字节的最低有效位是第一个位元,然后是次低有效位...一直到第一个字节的最高有效位,然后是下一个字节的最低有效位。这就是你进行多项式除法时位元(系数)的顺序。请尝试我的程序https://github.com/mojadita/crc.git(你可以在那里找到CRC16-CCITT的表格)。

1
这是错误的。RS232首先传输最低有效位(LSB)。I²C首先传输最高有效位(MSB)。I²S(串行音频编解码器格式)首先传输最高有效位(MSB)。SPI有选择,但大多数情况下是最高有效位(MSB)。 - Nils Pipenbrinck
1
CAN总线(汽车中的那个东西)首先传输最高有效位(MSB)。USB则首先传输最低有效位(LSB)。我认为没有明确的“通常比特是以最低有效位优先传输”的规定。这取决于接口。 - Nils Pipenbrinck
@NilsPipenbrinck 是的...以太网、串行RS232、RS422、HDLC、Bisync、令牌环等协议都是最低有效位(LSB)优先传输的。这种历史上的明显不对称可能源于旧硬件的使用。我并没有说世界上的每个字节都是以LSB传输的,只是考虑了许多采用LSB传输的协议。我不打算参与这场争论,也不认为这是一个下投票的有效理由。我提供的库适用于LSB传输,也许你可以提出另一个支持MSB传输的库,这样我们就完整了。无论如何,谢谢。 - Luis Colorado

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接