cancel
Showing results for 
Search instead for 
Did you mean: 

STM32F7 CRC unit: is behavior of bit reversal settings on 16- and 8-bit data sizes intentional?

cjameshuff
Associate II

If LL_CRC_INDATA_REVERSE_WORD is set and 8-bit data is provided via LL_CRC_FeedData8(), the resulting CRC is not what is expected. It appears the combination of word-reversal and byte-submission mangles the submitted data in some way, when I had expected it to fall back to byte reversal.

The datasheet does not indicate this is a concern, and the HAL code freely mixes word, half-word, and byte sizes, giving results that do not match third party calculators when dealing with data that isn't a multiple of 4 bytes in length. For example, these three all give correct results (22C644CD):

   uint8_t data8[8] = {0x1F, 0x52, 0x41, 0x9C, 0xB6, 0xAF, 0, 0};

   uint16_t * data16 = (uint16_t *)data8;

   uint32_t * data32 = (uint32_t *)data8;

   LL_CRC_SetInputDataReverseMode(CRC, LL_CRC_INDATA_REVERSE_BYTE);

   LL_CRC_ResetCRCCalculationUnit(CRC);

   LL_CRC_FeedData8(CRC, data8[0]);

   LL_CRC_FeedData8(CRC, data8[1]);

   LL_CRC_FeedData8(CRC, data8[2]);

   LL_CRC_FeedData8(CRC, data8[3]);

   LL_CRC_FeedData8(CRC, data8[4]);

   LL_CRC_FeedData8(CRC, data8[5]);

   crc = ~LL_CRC_ReadData32(CRC);

   printf("rev by byte: %08lX\n", crc);

   LL_CRC_ResetCRCCalculationUnit(CRC);

   LL_CRC_SetInputDataReverseMode(CRC, LL_CRC_INDATA_REVERSE_WORD);

   LL_CRC_FeedData32(CRC, data32[0]);

   LL_CRC_SetInputDataReverseMode(CRC, LL_CRC_INDATA_REVERSE_HALFWORD);

   LL_CRC_FeedData16(CRC, data16[2]);

   crc = ~LL_CRC_ReadData32(CRC);

   printf("rev by word/halfword: %08lX\n", crc);

   LL_CRC_ResetCRCCalculationUnit(CRC);

   LL_CRC_SetInputDataReverseMode(CRC, LL_CRC_INDATA_REVERSE_WORD);

   LL_CRC_FeedData32(CRC, data32[0]);

   LL_CRC_SetInputDataReverseMode(CRC, LL_CRC_INDATA_REVERSE_BYTE);

   LL_CRC_FeedData8(CRC, data8[4]);

   LL_CRC_FeedData8(CRC, data8[5]);

   crc = ~LL_CRC_ReadData32(CRC);

   printf("rev by word/byte: %08lX\n", crc);

The HAL code does the equivalent of this, which gives incorrect results (B1C2A1A3):

   LL_CRC_ResetCRCCalculationUnit(CRC);

   LL_CRC_SetInputDataReverseMode(CRC, LL_CRC_INDATA_REVERSE_WORD);

   LL_CRC_FeedData32(CRC, data32[0]);

   LL_CRC_FeedData8(CRC, data8[4]);

   LL_CRC_FeedData8(CRC, data8[5]);

   crc = ~LL_CRC_ReadData32(CRC);

   printf("rev by word: %08lX\n", crc);

LL_CRC_INDATA_REVERSE_NONE only gives the correct results if the bytes are reversed before sending to the CRC hardware. The HAL code appears to be quite broken when handling bytes: CRC_Handle_8()'s byte ordering requires bit reversal on input, but its mixing of data sizes without changing the reversal settings requires unreversed input.

12 REPLIES 12

Thanks for the follow up!

The MCU is STM32F767.

Not much to the code, but here it is:  It works on the F7 if you are on 4-byte boundary (ie the first branch only).  If you get down to byte access the calculation fails.

Cube MX settings:

BobbyHayes_0-1708598174058.png

Code (I could not figure out how to format this better).  Sorry for the sloppy indentations.

u16_t Crc_Pfm_CalcCrc(u8_t *pu8_buf, u32_t u32_len)
{
u16_t u16_crc = K_NUM_START_CRC;
if (pu8_buf)
{
CRC->INIT = u16_crc;
u32_t u32_i = 0;
/* The crc dr register can handle multiple bytes in the form of a u32. You have to be
* very specific about the access. Hence all of the typecasting around the set */
while (u32_len >= 4)
{
(*((volatile u32_t *)(&(CRC->DR)))) = *(u32_t *)(&(pu8_buf[u32_i]));
u32_len -= 4;
u32_i += 4;
}
while (u32_len >= 2)
{
(*((volatile u16_t *)(&(CRC->DR)))) = *(u16_t *)(&(pu8_buf[u32_i]));
u32_len -= 2;
u32_i += 2;
}
while (u32_len >= 1)
{
(*((volatile u8_t *)(&(CRC->DR)))) = *(u8_t *)(&(pu8_buf[u32_i]));
u32_len--;
u32_i++;
}
u16_crc = CRC->DR;
}
else
{
/* null pointer */
}
return u16_crc;
}

 

Ok, so an 0x8005 polynomial, left shifting

Yes, I'd expect you'd need to change how it handles the byte-endian switching for half-words. Or perhaps writing half-words at CRC->DR + 2

The memory read via the pointer is little-endian, and the CRC is ingesting big-endian

On an MCU level the use of REV or REV16 might be simpler to manage than the peripheral

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..

Are you suggesting I change the CRC->DR to a (u8 *) and do a +2 for u16 and +3 for u8?  If you think that might work, I'll give it a shot.

The software implementation I have works fine, but using the peripheral is much, much faster.  I took some measurements on the G0 calculating a CRC of a small array at 64MHz actually.  See below:

Times reflect each test was ran at a multiple of 100
TestMin (µs)Max (µs)Avg (µs)
Software CRC458346074585
Hardware CRC607627609