2

I have a device that adds a CRC32 checksum to data it sends. It is calculated using a lookup table as follows:

uint32_t CrcFromBuffer(uint8_t* buffer, uint16_t lenght) {
    uint32_t crc32 = 0xffffffff;

    if (buffer == NULL) {
        return 0;
    } else {
        while (lenght != 0) {
            uint8_t crc32_table_index = ((uint8_t)(crc32) ^ (*buffer));
            crc32 = crc32 >> 8;
            crc32 = crc32_table[crc32_table_index] ^ crc32;
            buffer++;
            lenght--;
        }
    }

    return ~crc32;
}

I'm using an STM32F401 microcontroller, which has hardware support for calculating CRC with the same polynomial but only with a 32-bit input data size. It works fine if the data length is a multiple of 4 bytes. Here's an example:

bool eight_bytes_random_crc() {
  uint8_t array8[8] = {0xAB, 0x21, 0x32, 0x47, 0x01, 0xFF, 0x00, 0x99};
  uint32_t array32[2] = {__RBIT(0x473221AB), __RBIT(0x9900FF01)}; // need to reverse bit order
  uint32_t hard_crc = __RBIT(~HAL_CRC_Calculate(&hcrc, array32, 2)); // again reverse bits and negate the result
  uint32_t soft_crc = CrcFromBuffer(array8, 8); 
  return hard_crc == soft_crc; 
}

How can I use the STM32 hardware CRC support for data arrays with a length that is not a multiple of 4 bytes?

I tried a brute-force search for a value that would produce the same CRC for a single byte but couldn't find an obvious way to get that value, given only the 0xAB input:

uint32_t one_byte_crc() {
  uint8_t array8[1] = {0xAB};
  uint32_t soft_crc = CrcFromBuffer(array8, 1); 
  for (uint32_t i = 0; i <= 0xFFFFFFFF; i++ ) {
      uint32_t array32[1] = {__RBIT(i)};
      uint32_t hard_crc = __RBIT(~HAL_CRC_Calculate(&hcrc, array32, 1));
      if (hard_crc == soft_crc) {
        return i; // gives 0xF7D1D97E
      }
  }    
} 

Any suggestions on how to use STM32 hardware CRC for non-multiple of 4 byte length data arrays?

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
mactro
  • 518
  • 7
  • 13
  • 1
    Why not use the hardware to calculate the CRC on all but the last 0 to 3 bytes, and then use software to finish off the CRC on those last few bytes? – Mark Adler Apr 16 '23 at 23:11
  • 1
    @MarkAdler The CRC calculation are a part of bootloader with very limited space for program binary. The main reason I was looking into hardware solution was to get rid of 1kB lookup table. But I might go with "algorithmic" calculations of the last 3 bytes, that might do the trick. – mactro Apr 17 '23 at 06:18
  • 1
    You don't need the table if you're ok with more instructions per byte. What is the polynomial? If you don't know, what is the 129th entry of the table? – Mark Adler Apr 17 '23 at 13:52

1 Answers1

4

The only thing you can do is either a) use the hardware to calculate the CRC up to a multiple of four bytes of your data, and then use software to finish up the CRC with the last 0 to 3 bytes, or b) use the hardware to calculate the CRC of all of your data padded out to a multiple of four with zeros, and then use a reverse CRC table to un-calculate the last 0 to 3 zeros from the CRC.

The most efficient might be to use a) if there are 0 or 1 bytes left, and b) if there are 3 bytes left, so un-calculating 1 byte. For 2 bytes left, you'd pick the faster one, but I'm not sure which one that would be. Could go either way.

Mark Adler
  • 101,978
  • 13
  • 118
  • 158
  • 1
    The linked answer will not work. It is designed for the STM32G family, which supports byte, half-word and word CRC operations. The STM32F family only supports word-size (32 bit) operations. – Codo Apr 18 '23 at 15:13
  • 1
    Accepting the answer despite the bottom line was "not possible". For completeness sake - I solved a problem by generating the lookup table on the initialization - I was having issues only with flash memory, but had plenty of RAM available. – mactro May 12 '23 at 12:59