Rant[+solution]: STM32F0 CRC engine - Page 1
Forum section: electronics / general

author: poorchava
author_rank: Super Contributor
timestamp: March 22, 2017, 08:03:53 am
content: Hi, so I have this device that used STM32F030. I did use its CRC module before, but it was being verified against crc also generated by the MCU itself (user setting integrity checks and such).This time i wanted to use this to verify firmware image uploaded via a custom bootloader. Obviously the CRC is being calculated on PC. Since The datasheet claims to use standard Ethernet CRC, I thought i was golden. Boy was I wrong.I've downloaded some CRC32 code for c# and generated a CRC from few random bytes from this and from the MCU. No match.I've checked several other implementations for the PC program as well as online calculators and they all aggreed, result from MCU is different.4 hour oflater, I couldn't find any set of settings for the MCU CRC engine that would generate remotely similar result.I then started brute-forcing all the combinations of all possible CRC engine settings. To no avail. And then I tried the same thing, but also reversing all the bits in the output ( xor 0xffffffff). One combination worked.How to make it work?-set input inversion to 8/16/32 bits, which one it doesn;t matter as long as you're doing byte-wise CRC. No idea about 16- or 32 bits at a time, only that they produce different output.-set output inversion to enabled-flip all the bits in the resulting CRC, the output will match the commonly available algorithms<rant>What the f** were they thinking. If they needed to implement the algorithm in some f**ed up way, they could've just published an appnote saying "do this, in order to convert out weird crc into something standard".</rant>

author: AndyC_772
author_rank: Super Contributor
timestamp: March 22, 2017, 08:25:50 am
content: It's pretty common to find that line protocols which use a CRC don't transmit it using the byte ordering and/or bit sense that you're expecting. There's almost certainly nothing wrong, odd or unusual about the STM32 CRC engine.IIRC I found exactly the same thing when implementing a serial HDLC controller in FPGA. The generator polynomial was well known. Less well known was the fact that the bytes in the CRC were transmitted with the bit ordering reversed (or something... it was a long time ago and I forget the exact details) compared to the data bytes they related to.Consider it a fun little cryptography challenge. Expect bit and byte ordering to be changed, and spend half an hour working out what you need to do to make your code match a known sample.

author: bktemp
author_rank: Super Contributor
timestamp: March 22, 2017, 08:40:46 am
content: I have the same problems with most CRCs. Giving the polynomial isn't enought, because there are so many variants how to apply this polynomial. I can't understand why manufacturers just don't put some example C code next to the polynomial.I used this code and it gave me the correct results for STM32F0:https://community.st.com/thread/21851

author: dgtl
author_rank: Regular Contributor
timestamp: March 22, 2017, 09:26:34 am
content: There are so many different CRCs, that it is not suprising they do not use the same. I like to keep the protocols standard CRC32, so I'm doing the conversions on the uc side. You'll need to reverse bit order of all words and invert the output to get standard CRC32. This means that you'll have to use sw and can't use DMA. There is ARM ASM instruction RBIT for that, use CMSIS macro __RBIT. (Some newer parts have the bit order reversal configuration in hw, too).There are much more difficult issues:1) Calculating CRC of data that is not multiple of 4 bytes long. A bit of math helps in this case.2) Setting the CRC value back to a pre-defined value (in case I need to calculate 2 CRCs interleaved). Some more math to write the correct value to get the required start state in data register.3) For some unknown reason, the reset of the CRC fails if there is high DMA load on both DMA controllers (at least on F107). The rest works, so I'll just use the math in 2) to set the data register to required state.The best I've come up so far is following code. I used this thread as reference:https://community.st.com/message/49784Code:static uint32_t crc32_step(uint32_t crc, uint8_t data) { // single bytecrc = ~crc ^ data;CRC->DR = (~CRC->DR) ^ __RBIT(~(crc << 24));return (crc >> 8) ^ ~__RBIT(CRC->DR);}static uint32_t crc32_4(uint32_t crc, const uint32_t *data, uint32_t cnt) { // word-wiseif (cnt == 0) return crc;CRC->DR = (~CRC->DR) ^ __RBIT(*data++ ^ crc);cnt--;while(cnt--) {CRC->DR = __RBIT(*data++);}return ~__RBIT(CRC->DR);}uint32_t crc32(uint32_t crc, const void *data, uint32_t cnt) {uint8_t* d = (uint8_t*) data;__CRC_CLK_ENABLE();// handle first bytes until the buffer gets word-alignedwhile((((uint32_t)d) & 3) != 0 && cnt > 0) {crc = crc32_step(crc, *d++);cnt--;}// handle the 4-byte wordsuint32_t cnt4 = cnt / 4;crc = crc32_4(crc, (const uint32_t *)d, cnt4);cnt4 = cnt4 * 4;d += cnt4;cnt -= cnt4;// handle the rest of the byteswhile(cnt) {crc = crc32_step(crc, *d++);cnt--;}return crc;}

author: poorchava
author_rank: Super Contributor
timestamp: March 22, 2017, 01:45:28 pm
content: I have tried __RBIT, but it appears, that CM0 does not support that instruction (ie. cmsis headers make it available only on M3/M4/M7).I remember having used the CRC on some F4 for the same exact purpose, and it was pretty painful.Still, since Ethernet variant of CRC32 is really popular and well documented, they could've gone to the effort of implementing it properly.Not so long ago I was implementing similar stuff using TMS320's built in CRC engine and I was able to coerce it into generating standard CRC16-CCITT without much problem. And this is considering that TI has a reputation for implementing stuff ass-backwards

author: FrankBuss
author_rank: Unknown
timestamp: March 22, 2017, 03:03:46 pm
content: Quote from: poorchava on March 22, 2017, 08:03:53 amTesting a bootloader is not time critical, I probably would have implemented it in software, would be no problem for the 48 MHz ARM core. You could even implement the bitwise algorithm without a lookup table, if code size is a problem. Might be less code all in all than using the hardware CRC engine.

author: dgtl
author_rank: Regular Contributor
timestamp: March 22, 2017, 05:01:23 pm
content: Quote from: poorchava on March 22, 2017, 01:45:28 pmCorrect, CM0 and CM0+ do not have RBIT. Missed that in your postQuote from: poorchava on March 22, 2017, 01:45:28 pmAt least they seem to have changed it in newer parts. A more common CRC would have been much more useful; theirs looks like the MPEG2 variant with the same polynomial but different bit order.In case of M0, it is either use it as-is (change the other end to the same as you did) or use software CRC. Reversing bit order in software is too slow.

author: poorchava
author_rank: Super Contributor
timestamp: March 22, 2017, 08:08:53 pm
content: As far as i understand reversing the bit order is done by setting configuration bits in CRC control register, so no software overhead is involved. The only software operation required seems to be the xor at the end.On the other hand i've made a comparison between setting bit-reverse options in the control register, and not setting them, but reversing the input data manually instead - the results were different.