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

The CRC peripheral on the STM32 parts is awkward, to say the least, there are issues of endian byte/bit ordering, shift direction (crc and data), and polynomial application.

I've got a pretty low opinion on web based calculators, and generally prefer to work the math myself, and compare that to examples from actual protocol documents, ideally ones that clearly define the CRC in use, and how they apply data to it.

I'd expect it to take several hours to get the hardware settings into a viable state for CRC that don't neatly fit the way ST expects them.

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

I used multiple third party calculators to verify that the MCU result was the one that was incorrect. What I was ultimately trying to implement in this case was CRC calculation for ST's hash-based MAC filter, which requires the bitwise inverted, un-reversed CRC of the 6 destination MAC bytes with the default polynomial and initial value. The input bytes themselves must be reversed.

It doesn't appear to be possible to compute this with the HAL code, and the LL library gives no help in working out how to feed the peripheral properly. It needs to either be done uniformly in 16- or 8-bit chunks (the HAL will split it into 32- and 16-bit chunks), or the input reverse mode needs to be changed in the middle of the calculation (which the HAL doesn't do).

The reference manual states "The data size can be dynamically adjusted to minimize the number of write accesses for a given number of bytes. For instance, a CRC for 5 bytes can be computed with a word write followed by a byte write."

My expectation from this was that the reverse-word and reverse-halfword settings would fall back to smaller data sizes when the DR register was written with halfwords or bytes. The writer of the HAL code appears to have had the same expectation.

I wrote a post on this years ago

https://community.st.com/s/question/0D50X00009Xkb6ySAB/calculating-ethernet-multicast-filter-hash-value-

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

Clive,

I believe that what cjameshuff1.5360733614419258E12 tries to tell is not that the CRC module is broken (although probably might use some better description - or an appnote? - your post linked above is sort of such an appnote, btw...), but that the way how Cube/HAL uses it is broken.

JW

Understood, but the CRC peripheral is like a Rubik's Cube, it can take some work to get the settings right so the 1, 2 and 4 byte combinations function correctly.

Some combinations won't work properly, and CRC's are sensitive to bit patterns and byte order. I'm not willing to call this as a bug vs not-bug, nor willing to spend hours to prove I can solve the puzzle.

I think I worked an 8-bit one within the last month, but I'd need to dig. Probably a Dallas/1-Wire thing, where the real solution is to fix the polynomial. ST's problem is really the unnatural endian ordering which makes everything else work backarsewards.

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

The issue is that when bit reversal by word is specified, the bit reversal is done on the full 32 bits of the register regardless of the write size. It appears the high bits are cleared to zero, so when the reversal size is larger than the data size being written, the CRC unit throws out the given data and accumulates zeros instead.

With CRC_INPUTDATA_INVERSION_WORD and CRC_INPUTDATA_FORMAT_BYTES, the last two bytes of:

  uint8_t data[6] = {0x1F, 0x52, 0x41, 0x9C, 0xB6, 0xAF};

...are ignored, and the CRC calculated is of {0x1F, 0x52, 0x41, 0x9C, 0x00, 0x00}.

This seems unlikely to be desirable, and is not documented in the reference manual or HAL.

Just some extra info to add to the post.  I was seeing the same thing as cjameshuff on an F7 project when trying to use the CRC for a Modbus calculation.  I ran the same code on a G0 processor and it works fine.  IMHO it appears there was an issue on F7 that was fixed on later CPU releases.  So if you're doing a 16 bit Modbus style CRC on F7, I recommend using a software implementation instead. 

I would love to be corrected, because I would prefer to use the F7 hardware CRC. 

Not sure, perhaps it's a CM7 related issue or speculatively writing.

I did MODBUS CRC16 on the F3's, might try to port to the F7. Do we collectively have test case / vector to have a definitive working / not-working example to push at ST? I'll take the cjameshuff one for starters.

>>I would love to be corrected, because I would prefer to use the F7 hardware CRC.

The HW interactions across the buses isn't exactly fast, and it's not thread / restart safe.

A tight SW nibble implementation could have the entire table cached.

Which F7? The F746, F767 and F722 are different die. The core in the F746 is deprecated due to a number for flaws, the other CM7 (F7, H7) from ST have different patch levels

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

> I was seeing the same thing as cjameshuff on an F7 project when trying to use the CRC for a Modbus calculation. I ran the same code on a G0 processor and it works fine.

Show the code which works on 'G0 and fails on 'F7.

JW