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.

This discussion is locked. Please start a new topic to ask your question.
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