CRC计算:带有字节消息XOR-ing的多项式划分?

发布于 2025-02-06 09:59:23 字数 8998 浏览 1 评论 0 原文

试图从以下Wikipedia页面中的“代码片段3”中了解这一点的伪代码中的含义:

function crc(byte array string[1..len], int len) {
    remainderPolynomial := 0
    // A popular variant complements remainderPolynomial here; see § Preset to −1 below
    for i from 1 to len {
        remainderPolynomial := remainderPolynomial xor polynomialForm(string[i]) * xn−8
        for j from 1 to 8 {    // Assuming 8 bits per byte
            if coefficient of xn−1 of remainderPolynomial = 1 {
                remainderPolynomial := (remainderPolynomial * x) xor generatorPolynomial
            } else {
                remainderPolynomial := (remainderPolynomial * x)
            }
        }
    }
    // A popular variant complements remainderPolynomial here; see § Post-invert below
    return remainderPolynomial
}

文章指出:

在软件中,很方便地注意到,虽然可以将每个位的XOR延迟到最后一刻,但也可以更早地进行。通常,即使在这样的时间实现中,一次执行XOR A字节也很方便:

好的,这听起来很棒。我需要在微控制器上实现CRC-16-IBM,并且不愿使用查找表方法(尽管性能缺点)。探索字节XOR计算的这种可能性,而不是咬合会很有趣。但是,我不太了解它们的意思。

虽然代码将写在汇编器中,但我正在使用Python来探索想法并了解所有细节。据我所知,这是CRC-16代码,据我所知,返回正确的值:

# Flip the bits of an integer
# It will only flip the lower <bits> bits
def flip(x:int, bits:int=16):
    result = 0
    for i in range(bits):  # Reflect reg
        result <<= 1
        temp = x & (0x0001 << i)
        if temp:
            result |= 0x0001
    return result

def test_flip():
    n = 0b1100_1000

    bits = 8
    print(f"\nin: {n:0{bits}b}")
    print(f"out:{flip(n, bits):0{bits}b}")

    bits = 16
    print(f"\nin: {n:0{bits}b}")
    print(f"out:{flip(n,bits):0{bits}b}")

# test_flip()


def crc16(data:bytearray,
            poly:int=0x8005,
            init:int=0x0000,
            ref_in:bool=True,
            ref_out:bool=True,
            xor_out:int=-0x0000,
            debug:bool=False,
            disable_poly_xor:bool=False):
    reg = init                        # Initial CRC value
    for byte in data:                 # For each byte..
        if not ref_in:                # Reflect the input if necessary
            byte = flip(byte, 8)

        for i in range(8):            # Process all 8 bits one at a time
            reg <<= 1                 # Shift register left by one bit

            reg |= byte & 0x01      # Shift data each bit into the register on at a time
                                    # LSB first, unless input is NOT reflected

            if not disable_poly_xor:
                if reg & 0x010000:    # If a 1 shifts out of the 16 bit register,
                    reg ^= poly       # xor with polynomial

            byte >>= 1                # Prepare next bit to shift into register

            if debug:
                reg_str = f"{reg:032b}"
                print(f"{reg_str[-17:-16]} {reg_str[-16:-12]} {reg_str[-12:-8]} {reg_str[-8:-4]} {reg_str[-4:]} < {byte:08b}")

        if debug: print()
        reg &= 0xFFFF                 # This isn't a 16 bit hardware register, get rid of the excess

    if ref_out:
        return flip(reg, 16) ^ xor_out
    else:
        return reg ^ xor_out


# Checking implementations
check_data = bytearray(b"123456789\x00\x00")
check_result_should_be = 0xBB3D

print(f"\ndata: {[hex(n) for n in check_data]}")
print(f"expected CRC: {check_result_should_be: 04x}")

print()

print(f"crc16: {crc16(check_data, ref_in=False, ref_out=False): 04x}")
print(f"crc16: {crc16(check_data, ref_in=False, ref_out=True): 04x}")
print(f"crc16: {crc16(check_data, ref_in=True,  ref_out=False): 04x}")
print(f"crc16: {crc16(check_data, ref_in=True,  ref_out=True): 04x}")

我遵循此参考文档以产生您在上面看到的实现:

https://zlib.net/crc_v3.txt

我已经浏览了许多语言中的许多CRC实现(包括此处的许多语言)。我还没有看到一个将零附加到数据上的单个,因为论文似乎表明是必要的。我上面的测试数据是 bytearray(b“ 123456789”),但我用16位零填充了它。没有那个,我将无法获得正确的CRC输出值。

根据该论文,我正在实现的CRC-16-IBM版本应输出 0xbb3d ,这正是我在ref_in和ref_out设置为true时得到的。

这将我带到了Wikipedia Bytewise Xor pseudocode。这是我解释伪代码的方式:

def crc16_1(data:bytearray,
            poly:int=0x8005,
            init:int=0x0000,
            ref_in:bool=True,
            ref_out:bool=True,
            xor_out:int=-0x0000,
            debug:bool=False,
            disable_poly_xor:bool=False):
    reg = init                        # Initial CRC value
    for byte in data:                 # For each byte..
        if not ref_in:                # Reflect the input if necessary
            byte = flip(byte, 8)

        reg ^= byte

        for i in range(8):            # Process all 8 bits one at a time
            if not disable_poly_xor:
                if reg & 0x8000:    # If a 1 is going to shift out of the 16 bit register,
                    reg <<= 1
                    reg ^= poly     # xor with polynomial
                else:
                    reg <<= 1       # Shift register left by one bit

            if debug:
                reg_str = f"{reg:032b}"
                print(f"{reg_str[-17:-16]} {reg_str[-16:-12]} {reg_str[-12:-8]} {reg_str[-8:-4]} {reg_str[-4:]} < {byte:08b}")

        if debug: print()
        reg &= 0xFFFF                 # This isn't a 16 bit hardware register, get rid of the excess

    if ref_out:
        return flip(reg, 16) ^ xor_out
    else:
        return reg ^ xor_out

将其添加到我的测试中:

# Checking implementations
check_data = bytearray(b"123456789\x00\x00")
check_result_should_be = 0xBB3D

print(f"\ndata: {[hex(n) for n in check_data]}")
print(f"expected CRC: {check_result_should_be: 04x}")

print()

print(f"crc16: {crc16(check_data, ref_in=False, ref_out=False): 04x}")
print(f"crc16: {crc16(check_data, ref_in=False, ref_out=True): 04x}")
print(f"crc16: {crc16(check_data, ref_in=True,  ref_out=False): 04x}")
print(f"crc16: {crc16(check_data, ref_in=True,  ref_out=True): 04x}")

print()

print(f"crc16_1: {crc16_1(check_data, ref_in=False, ref_out=False): 04x}")
print(f"crc16_1: {crc16_1(check_data, ref_in=False, ref_out=True): 04x}")
print(f"crc16_1: {crc16_1(check_data, ref_in=True,  ref_out=False): 04x}")
print(f"crc16_1: {crc16_1(check_data, ref_in=True,  ref_out=True): 04x}")

我得到:

data: ['0x31', '0x32', '0x33', '0x34', '0x35', '0x36', '0x37', '0x38', '0x39', '0x0', '0x0']
expected CRC:  bb3d

crc16:  fee8
crc16:  177f
crc16:  bcdd
crc16:  bb3d

crc16_1:  5e8b
crc16_1:  d17a
crc16_1:  6a07
crc16_1:  e056

这是不正确的。

我希望有人可以帮助我了解如何从伪代码到可以超过此或任何其他CRC配置的简单测试的真实代码。我要实现的CRC算法的定义是(也是从链接的纸张中):

Name   : "CRC-16"
Width  : 16
Poly   : 8005
Init   : 0000
RefIn  : True
RefOut : True
XorOut : 0000
Check  : BB3D

底部的检查值是我用来确保我的代码正常工作的方法。

我还看一下本文档的第40页的Modbus CRC实现:

这是我的实现:

def crc16_2(data:bytearray,
            poly:int=0x8005,
            init:int=0x0000,
            ref_in:bool=True,
            ref_out:bool=True,
            xor_out:int=-0x0000,
            debug:bool=False,
            disable_poly_xor:bool=False):
    reg = init                        # Initial CRC value
    for byte in data:                 # For each byte..
        if not ref_in:                # Reflect the input if necessary
            byte = flip(byte, 8)

        reg ^= byte

        for i in range(8):            # Process all 8 bits one at a time
            if not disable_poly_xor:
                if reg & 0x0001:    # If a 1 is going to shift out of the 16 bit register,
                    reg >>= 1
                    reg ^= poly     # xor with polynomial
                else:
                    reg >>= 1       # Shift register left by one bit

            if debug:
                reg_str = f"{reg:032b}"
                print(f"{reg_str[-17:-16]} {reg_str[-16:-12]} {reg_str[-12:-8]} {reg_str[-8:-4]} {reg_str[-4:]} < {byte:08b}")

        if debug: print()
        reg &= 0xFFFF                 # This isn't a 16 bit hardware register, get rid of the excess

    if ref_out:
        return flip(reg, 16) ^ xor_out
    else:
        return reg ^ xor_out

请注意,它们可以翻转多项式并向右移动,而不是向左移动。他们一次做一个字节,这很有趣。

再一次,没有配置会产生预期的CRC值:

data: ['0x31', '0x32', '0x33', '0x34', '0x35', '0x36', '0x37', '0x38', '0x39', '0x0', '0x0']
expected CRC:  bb3d

crc16:  fee8 < correct
crc16:  177f < correct
crc16:  bcdd < correct
crc16:  bb3d < correct

crc16_1:  5e8b
crc16_1:  d17a
crc16_1:  6a07
crc16_1:  e056

crc16_2:  295
crc16_2:  a940
crc16_2:  44ad
crc16_2:  b522

编辑:

这是Mark Adler贡献的非常好的资源:

https://github.com/madler/crcany

Trying to understand what is meant in this bit of pseudocode from "Code fragment 3" in the following Wikipedia page:

function crc(byte array string[1..len], int len) {
    remainderPolynomial := 0
    // A popular variant complements remainderPolynomial here; see § Preset to −1 below
    for i from 1 to len {
        remainderPolynomial := remainderPolynomial xor polynomialForm(string[i]) * xn−8
        for j from 1 to 8 {    // Assuming 8 bits per byte
            if coefficient of xn−1 of remainderPolynomial = 1 {
                remainderPolynomial := (remainderPolynomial * x) xor generatorPolynomial
            } else {
                remainderPolynomial := (remainderPolynomial * x)
            }
        }
    }
    // A popular variant complements remainderPolynomial here; see § Post-invert below
    return remainderPolynomial
}

The article states:

In software, it is convenient to note that while one may delay the xor of each bit until the very last moment, it is also possible to do it earlier. It is usually convenient to perform the xor a byte at a time, even in a bit-at-a-time implementation like this:

OK, that sounds great. I need to implement CRC-16-IBM on a microcontroller and would prefer not to use the lookup table method (despite performance disadvantages). It would be interesting to explore this possibility of byte-wise xor calculation, rather than bit-by-bite. Yet, I don't quite understand what they mean.

While the code will be written in assembler, I am using Python to explore ideas and understand all the details. Here's CRC-16 code I wrote that, as far as I can tell, returns correct values:

# Flip the bits of an integer
# It will only flip the lower <bits> bits
def flip(x:int, bits:int=16):
    result = 0
    for i in range(bits):  # Reflect reg
        result <<= 1
        temp = x & (0x0001 << i)
        if temp:
            result |= 0x0001
    return result

def test_flip():
    n = 0b1100_1000

    bits = 8
    print(f"\nin: {n:0{bits}b}")
    print(f"out:{flip(n, bits):0{bits}b}")

    bits = 16
    print(f"\nin: {n:0{bits}b}")
    print(f"out:{flip(n,bits):0{bits}b}")

# test_flip()


def crc16(data:bytearray,
            poly:int=0x8005,
            init:int=0x0000,
            ref_in:bool=True,
            ref_out:bool=True,
            xor_out:int=-0x0000,
            debug:bool=False,
            disable_poly_xor:bool=False):
    reg = init                        # Initial CRC value
    for byte in data:                 # For each byte..
        if not ref_in:                # Reflect the input if necessary
            byte = flip(byte, 8)

        for i in range(8):            # Process all 8 bits one at a time
            reg <<= 1                 # Shift register left by one bit

            reg |= byte & 0x01      # Shift data each bit into the register on at a time
                                    # LSB first, unless input is NOT reflected

            if not disable_poly_xor:
                if reg & 0x010000:    # If a 1 shifts out of the 16 bit register,
                    reg ^= poly       # xor with polynomial

            byte >>= 1                # Prepare next bit to shift into register

            if debug:
                reg_str = f"{reg:032b}"
                print(f"{reg_str[-17:-16]} {reg_str[-16:-12]} {reg_str[-12:-8]} {reg_str[-8:-4]} {reg_str[-4:]} < {byte:08b}")

        if debug: print()
        reg &= 0xFFFF                 # This isn't a 16 bit hardware register, get rid of the excess

    if ref_out:
        return flip(reg, 16) ^ xor_out
    else:
        return reg ^ xor_out


# Checking implementations
check_data = bytearray(b"123456789\x00\x00")
check_result_should_be = 0xBB3D

print(f"\ndata: {[hex(n) for n in check_data]}")
print(f"expected CRC: {check_result_should_be: 04x}")

print()

print(f"crc16: {crc16(check_data, ref_in=False, ref_out=False): 04x}")
print(f"crc16: {crc16(check_data, ref_in=False, ref_out=True): 04x}")
print(f"crc16: {crc16(check_data, ref_in=True,  ref_out=False): 04x}")
print(f"crc16: {crc16(check_data, ref_in=True,  ref_out=True): 04x}")

I followed this reference document in order to produce the implementation you see above:

https://zlib.net/crc_v3.txt

I have looked through many CRC implementations in a range of languages (including many here on SO). I have not seen a single one that appends zeros to the data, as the paper seems to indicate is necessary. My test data above is bytearray(b"123456789"), yet I padded it with 16 bits of zeros. Without that I do not get the right CRC output values.

Per that paper, the CRC-16-IBM version I am implementing should output 0xBB3D, which is exactly what I get when the ref_in and ref_out are set to True.

Which brings me to the Wikipedia bytewise xor pseudocode. Here's how I interpret the pseudocode:

def crc16_1(data:bytearray,
            poly:int=0x8005,
            init:int=0x0000,
            ref_in:bool=True,
            ref_out:bool=True,
            xor_out:int=-0x0000,
            debug:bool=False,
            disable_poly_xor:bool=False):
    reg = init                        # Initial CRC value
    for byte in data:                 # For each byte..
        if not ref_in:                # Reflect the input if necessary
            byte = flip(byte, 8)

        reg ^= byte

        for i in range(8):            # Process all 8 bits one at a time
            if not disable_poly_xor:
                if reg & 0x8000:    # If a 1 is going to shift out of the 16 bit register,
                    reg <<= 1
                    reg ^= poly     # xor with polynomial
                else:
                    reg <<= 1       # Shift register left by one bit

            if debug:
                reg_str = f"{reg:032b}"
                print(f"{reg_str[-17:-16]} {reg_str[-16:-12]} {reg_str[-12:-8]} {reg_str[-8:-4]} {reg_str[-4:]} < {byte:08b}")

        if debug: print()
        reg &= 0xFFFF                 # This isn't a 16 bit hardware register, get rid of the excess

    if ref_out:
        return flip(reg, 16) ^ xor_out
    else:
        return reg ^ xor_out

Adding this to my test:

# Checking implementations
check_data = bytearray(b"123456789\x00\x00")
check_result_should_be = 0xBB3D

print(f"\ndata: {[hex(n) for n in check_data]}")
print(f"expected CRC: {check_result_should_be: 04x}")

print()

print(f"crc16: {crc16(check_data, ref_in=False, ref_out=False): 04x}")
print(f"crc16: {crc16(check_data, ref_in=False, ref_out=True): 04x}")
print(f"crc16: {crc16(check_data, ref_in=True,  ref_out=False): 04x}")
print(f"crc16: {crc16(check_data, ref_in=True,  ref_out=True): 04x}")

print()

print(f"crc16_1: {crc16_1(check_data, ref_in=False, ref_out=False): 04x}")
print(f"crc16_1: {crc16_1(check_data, ref_in=False, ref_out=True): 04x}")
print(f"crc16_1: {crc16_1(check_data, ref_in=True,  ref_out=False): 04x}")
print(f"crc16_1: {crc16_1(check_data, ref_in=True,  ref_out=True): 04x}")

I get:

data: ['0x31', '0x32', '0x33', '0x34', '0x35', '0x36', '0x37', '0x38', '0x39', '0x0', '0x0']
expected CRC:  bb3d

crc16:  fee8
crc16:  177f
crc16:  bcdd
crc16:  bb3d

crc16_1:  5e8b
crc16_1:  d17a
crc16_1:  6a07
crc16_1:  e056

Which isn't correct.

I hope someone can help me understand how to get from their pseudocode to real code that can past the simple test for this or any other CRC configuration. The definition for the CRC algorithm I am trying to implement is (also from the linked paper):

Name   : "CRC-16"
Width  : 16
Poly   : 8005
Init   : 0000
RefIn  : True
RefOut : True
XorOut : 0000
Check  : BB3D

The check value at the bottom is what I am using to ensure my code is working correctly.

I also took a look at the MODBUS CRC implementation, per page 40 of this document:

https://www.modbus.org/docs/Modbus_over_serial_line_V1_02.pdf

Here's my implementation:

def crc16_2(data:bytearray,
            poly:int=0x8005,
            init:int=0x0000,
            ref_in:bool=True,
            ref_out:bool=True,
            xor_out:int=-0x0000,
            debug:bool=False,
            disable_poly_xor:bool=False):
    reg = init                        # Initial CRC value
    for byte in data:                 # For each byte..
        if not ref_in:                # Reflect the input if necessary
            byte = flip(byte, 8)

        reg ^= byte

        for i in range(8):            # Process all 8 bits one at a time
            if not disable_poly_xor:
                if reg & 0x0001:    # If a 1 is going to shift out of the 16 bit register,
                    reg >>= 1
                    reg ^= poly     # xor with polynomial
                else:
                    reg >>= 1       # Shift register left by one bit

            if debug:
                reg_str = f"{reg:032b}"
                print(f"{reg_str[-17:-16]} {reg_str[-16:-12]} {reg_str[-12:-8]} {reg_str[-8:-4]} {reg_str[-4:]} < {byte:08b}")

        if debug: print()
        reg &= 0xFFFF                 # This isn't a 16 bit hardware register, get rid of the excess

    if ref_out:
        return flip(reg, 16) ^ xor_out
    else:
        return reg ^ xor_out

Note that they flip the polynomial and shift right, rather than left. They do xor one byte at a time, which is interesting.

Once again, no configuration produces the expected CRC value:

data: ['0x31', '0x32', '0x33', '0x34', '0x35', '0x36', '0x37', '0x38', '0x39', '0x0', '0x0']
expected CRC:  bb3d

crc16:  fee8 < correct
crc16:  177f < correct
crc16:  bcdd < correct
crc16:  bb3d < correct

crc16_1:  5e8b
crc16_1:  d17a
crc16_1:  6a07
crc16_1:  e056

crc16_2:  295
crc16_2:  a940
crc16_2:  44ad
crc16_2:  b522

EDIT:

This is a really nice resource contributed by Mark Adler:

https://github.com/madler/crcany

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

逆光飞翔i 2025-02-13 09:59:23

我只有Python 2.7。这似乎有效。

    def flip(x=0, bits=16):
        result = 0
        for i in range(bits):  # Reflect reg
            result <<= 1
            temp = x & (0x0001 << i)
            if temp:
                result |= 0x0001
        return result
    
    def crc16(bytearray,
                poly=0x8005,
                init=0x0000,
                ref_in=True,
                ref_out=True,
                xor_out=0x0000):
        reg = init
        for byte in data:
            if ref_in:
                byte = flip(byte, 8)
            reg ^= byte << 8
            for i in range(8):
                if reg & 0x08000:
                    reg = (reg << 1) ^ poly
                else:
                    reg = (reg << 1)
                reg &= 0xffff
        if ref_out:
            return flip(reg, 16) ^ xor_out
        else:
            return reg ^ xor_out
    
    data = [0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39]
    
    print('0x%04x' % crc16(data, 0x8005, 0x0000, True, True, 0x0000))

由于此处的目标是反射的CRC16,因此这是一个更简单的右移动CRC函数,它消除了钻头的需要。这意味着正确的大多数数据和CRC寄存器是逻辑上最重要的位。多项式也从0x8005到0xA001反映。

def crc16r(bytearray,
            poly=0xa001,
            init=0x0000,
            xor_out=0x0000):
    reg = init
    for byte in data:
        reg ^= byte
        for i in range(8):
            if reg & 0x0001:
                reg = (reg >> 1) ^ poly
            else:
                reg = (reg >> 1)
    return reg ^ xor_out

data = [0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39]

print('0x%04x' % crc16r(data, 0xa001, 0x0000, 0x0000))

I only have Python 2.7. This seems to work.

    def flip(x=0, bits=16):
        result = 0
        for i in range(bits):  # Reflect reg
            result <<= 1
            temp = x & (0x0001 << i)
            if temp:
                result |= 0x0001
        return result
    
    def crc16(bytearray,
                poly=0x8005,
                init=0x0000,
                ref_in=True,
                ref_out=True,
                xor_out=0x0000):
        reg = init
        for byte in data:
            if ref_in:
                byte = flip(byte, 8)
            reg ^= byte << 8
            for i in range(8):
                if reg & 0x08000:
                    reg = (reg << 1) ^ poly
                else:
                    reg = (reg << 1)
                reg &= 0xffff
        if ref_out:
            return flip(reg, 16) ^ xor_out
        else:
            return reg ^ xor_out
    
    data = [0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39]
    
    print('0x%04x' % crc16(data, 0x8005, 0x0000, True, True, 0x0000))

Since the goal here is a reflected CRC16, this is a simpler right shifting CRC function that eliminates the need to do to bit flips. This means the right most bit of data and the CRC register are the logical most significant bits. The polynomial is also reflected from 0x8005 to 0xa001.

def crc16r(bytearray,
            poly=0xa001,
            init=0x0000,
            xor_out=0x0000):
    reg = init
    for byte in data:
        reg ^= byte
        for i in range(8):
            if reg & 0x0001:
                reg = (reg >> 1) ^ poly
            else:
                reg = (reg >> 1)
    return reg ^ xor_out

data = [0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39]

print('0x%04x' % crc16r(data, 0xa001, 0x0000, 0x0000))
梦里兽 2025-02-13 09:59:23

在您链接的论文中,这一切都很好。我不确定您阅读了整个内容,因为它可以解释为什么以及如何与零在实施中使用零。它还解释了整个字节中的专有性以及如何专用于CRC的工作,而不是一次。

无论如何,您的问题是您没有反映反射CRC的多项式。 0x8005 变为 0a001

def crc16(data):
    crc = 0
    for byte in data:
        crc ^= byte
        for _ in range(8):
            crc = (crc >> 1) ^ 0xa001 if crc & 1 else crc >> 1
    return crc

print(hex(crc16(b'123456789')))

印刷:

0xbb3d

It's all very well explained in the paper you linked. I'm not sure you read the whole thing, since it explains why and how you don't need to pad with zeros in the implementation. It also explains why and how exclusive-oring the entire byte into the CRC works, instead of a bit at a time.

Anyway, your problem is that you're not reflecting the polynomial for a reflected CRC. 0x8005 becomes 0a001.

def crc16(data):
    crc = 0
    for byte in data:
        crc ^= byte
        for _ in range(8):
            crc = (crc >> 1) ^ 0xa001 if crc & 1 else crc >> 1
    return crc

print(hex(crc16(b'123456789')))

prints:

0xbb3d
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文