Этот фрагмент CRC6 дает неправильные результаты

#c #crc

#c #crc

Вопрос:

Фрагмент кода для генерации CRC6 не дает правильного значения. В чем может быть проблема во фрагменте кода?

 SPI_CRC6 = X6   X4   X3   X   1
  

Начальное начальное значение равно 0x3F
Входные данные: 24 бита.

Несколько проверенных выборочных значений: (не из фрагмента кода)

 24b input: 0xAE0000, CRC6: 0x11
24b input: 0x950055, CRC6: 0x22
  
 /* CRC6 calculation  */
Uint16 crc2(Uint32 datin)
{
    Uint16 byte_idx, bit_idx, crc = (0x3F << 2);//CRC_INITSEED = 0x3f

    /* byte by byte starting from most significant (3-2-1) */
    for (byte_idx = 3; byte_idx >= 1; byte_idx--)
    {
        /* XOR-in new byte from left to right */
        crc ^= ((datin >> (byte_idx << 3)) amp; 0x000000FF);

        /* bit by bit for each byte */
        for (bit_idx = 0; bit_idx < 8; bit_idx  )
        {
            crc = crc << 1 ^ (crc amp; 0x80 ? (0x5B << 2) : 0);//CRC Polynom: 0x5B
        }
    }

    return (crc >> 2 amp; 0x3F); /*restore two bit offset */
}
  

Комментарии:

1. Коду требуется сдвиг вправо, кратный 6 битам (18, 12, 6, 0). Было бы проще, если бы вы сохранили CRC в младших 6 битах crc .

Ответ №1:

Код сдвигает входные данные на 24,16,8 бита, когда они должны сдвигаться на 16,8,0 бита. Один из способов исправить это (и одновременно упростить код) — использовать количество сдвигов в качестве параметра цикла.

Код также обрабатывает входные данные по 8 бит за раз. Это приводит к загадочным сдвигам на 2 по всему коду. Гораздо естественнее обрабатывать 6 бит за раз. В этом случае коду необходимо сместить входные данные на 18,12,6,0 бита.

Итак, я бы написал код следующим образом:

 /* CRC6 calculation  */
Uint16 crc2(Uint32 datin)
{
    int input_shift;
    Uint16 bit_idx, crc = 0x3F;  //CRC seed = 0x3F

    /* 6 bits at a time starting from most significant */
    for (input_shift = 18; input_shift >= 0; input_shift -= 6)
    {
        /* XOR-in new data from left to right */
        crc ^= (datin >> input_shift) amp; 0x3F;

        /* bit by bit for each chunk */
        for (bit_idx = 0; bit_idx < 6; bit_idx  )
        {
            crc <<= 1;
            crc ^= (crc amp; 0x40) ? 0x5B : 0; //CRC polynomial: 0x5B
        }
    }

    return crc amp; 0x3F; //return the 6-bit CRC
}
  

Ответ №2:

ответ пользователя 3386 109 показывает исправленную версию вашего кода, но в этом случае нет необходимости разделять данные на 6-битные поля.

 typedef unsigned short uint16_t;
typedef unsigned int   uint32_t;

uint16_t crc1(uint32_t datin)
{
int i;
uint32_t crc = datin ^ (0x3f << 18);
    for (i = 0; i < 24; i  )
        crc = (crc << 1) ^ ((crc amp; 0x800000) ? (0x5B << 18) : 0);
    return crc >> 18;
}
  

В следующем примере предполагается, что два дополняют математику, используя (-0) = 0x00000000 или (-1) = 0xffffffff в качестве маски, чтобы избежать использования условного кода (тенарного ? : ). Обратите внимание, что оптимизирующий компилятор может использовать математику, чтобы избежать условного кода и для приведенного выше примера (Visual Studio делает это).

 typedef unsigned short uint16_t;
typedef unsigned int   uint32_t;

uint16_t crc1(uint32_t datin)
{
int i;
uint32_t crc = datin ^ (0x3f << 18);
    for (i = 0; i < 24; i  )
        crc = (crc << 1) ^ ((-(crc >> 23)) amp; (0x5B << 18));
    return crc >> 18;
}
  

Трюк с маскировкой часто используется компиляторами, общая последовательность такова:

         ...                     ;eax is zero or non-zero
        neg     eax             ;sets borrow bit if eax != 0
        sbb     eax,eax         ;eax = 0x00000000 or 0xffffffff
        and     eax,...         ;use eax as mask