cancel
Showing results for 
Search instead for 
Did you mean: 

[SOLVED] STM32 CRC calculation unit - I know this is a very old question.

KShim.1738
Associate III

I could not get wanted result by using STM32F7 calculation unit. The code I borrowed for comparison was from WireShark CRC32 function. It was a table based function for speed. And I compared the STM32 CRC result with bitwise algorithm. I found out that STM32 CRC unit by default gives the same result as the named one CRC32Mpeg2.

But I failed to set STM32F7 CRC unit to give the same result of WireShark CRC32 after I tried all reasonable combinations of parameters.

I've read quite a lot online articles and there could be mistakes involved with CRC calculation. The most notorious issue comes from the shifting direction. And the table and the shifting direction mismatch, input data reflection(or inversion from STM32 datasheet), initial CRC value and so on.

And I think there are ambiguities about byte inversion and output data inversion. We can think of more than 1 operation with input data inversion and output data inversion. Especially I wonder when the output inversion is applied, every byte or end of stream? How can the CRC calculation unit know it's the end of byte stream? Or it just inverse the result with every byte or every word?

I tried to find answers to my questions here and from other sources, but I couldn't.

There are at least 6 independent parameters even if we rule out the symmetry of initial CRC and the final XOR value.

  1. shifting direction
  2. polynomial (only 0xEDB88320, 0x04C11DB7 are considered here)
  3. initial CRC value(usually 0xffffffff or 0x00000000. And only two values considered here)
  4. final XOR (usually 0xffffffff or 0x00000000. And only two values considered here)
  5. Input byte reflection
  6. Output CRC reflection

And I wrote a test program after some researches and some codes borrowed from Internet. This program is written so that I can test all six combinations with the above parameters. But the shifting direction should be done in the form of action. So I prepared two functions.

refer to attached source.

calcCRC32std() : CRC shifted right

calcCRC32stm() : CRC shifted left

And both functions accept 5 parameters of CRC as arguments.

I generated 5 kinds of named CRC(names are from http://www.sunshine2k.de/coding/javascript/crc/crc_js.html).

  • CRC32 - WireShark
  • CRCMPeg2 - STM32
  • BZIP2
  • Posix
  • JAMCRC

here is the test run result.

--------- reflection test --------- 
byte reflect8: 0x14 vs. 0x28
word reflect32: 0x01020408 vs. 0x10204080
 
 
poly0x04C11DB7 reflected: 0xEDB88320
 
 
test string: abcdefghijklmnopqrstuvwxyz
 
 ------------ SHIFT R/L ------------ 
CRC32std(0xEDB88320, 0xffffffff, onXOR, offRefIn, offRefOut)  = 0x4c2750bd
CRC32stm(0x04C11DB7, 0xffffffff, offXOR, offRefIn, offRefOut) = 0x88406c69
 
 ------------ CRC32 std ------------ 
CRC32std(0xEDB88320, 0xffffffff, onXOR, offRefIn, offRefOut)  = 0x4c2750bd
CRC32std(0xEDB88320, 0xffffffff, offXOR, onRefIn, onRefOut)   = 0x88406c69
CRC32stm(0x04C11DB7, 0xffffffff, offXOR, offRefIn, offRefOut) = 0x88406c69
CRC32stm(0x04C11DB7, 0xffffffff, onXOR, onRefIn, onRefOut)    = 0x4c2750bd
 
Derived from the two basic ones above!!!
 
 
 ------------ CRC32 BZIP2 ------------ 
CRC32std(0xEDB88320, 0xffffffff, onXOR, onRefIn, onRefOut)    = 0x77bf9396
CRC32stm(0x04C11DB7, 0xffffffff, onXOR, offRefIn, offnRefOut) = 0x77bf9396
 
 ----------- CRC32 Posix ----------- 
CRC32std(0xEDB88320, 0x00000000, onXOR, onRefIn, onRefOut)    = 0xc43d5b9c
CRC32stm(0x04C11DB7, 0x00000000, onXOR, offRefIn, offRefOut)  = 0xc43d5b9c
 
 ----------- CRC32JAMCRC ----------- 
CRC32std(0xEDB88320, 0xffffffff, offXOR, offRefIn, offRefOut) = 0xb3d8af42
CRC32stm(0x04C11DB7, 0xffffffff, offXOR, onRefIn, onRefOut)   = 0xb3d8af42

The thing I would like to show is that whether the shift direction is chosen, we can get the same result with other 5 CRC parameters. So we can get all CRCs of calcCRC32std() by using calcCRC32stm(). Both are some kind of dual relationship or reciprocal, whatever you like.

The default initialization parameters for STM32F7 CRC calculation unit are

 hcrc.Instance = CRC;

 hcrc.Init.DefaultPolynomialUse = DEFAULT_POLYNOMIAL_ENABLE;

 hcrc.Init.DefaultInitValueUse = DEFAULT_INIT_VALUE_ENABLE;

 hcrc.Init.InputDataInversionMode = CRC_INPUTDATA_INVERSION_NONE;

 hcrc.Init.OutputDataInversionMode = CRC_OUTPUTDATA_INVERSION_DISABLE;

 hcrc.InputDataFormat = CRC_INPUTDATA_FORMAT_BYTES;

And I call the functions as follows.

#define CRC_TEST_STRING "abcdefghijklmnopqrstuvwxyz"
 
crc = HAL_CRC_Calculate(&hcrc, (uint32_t *)CRC_TEST_STRING, strlen(CRC_TEST_STRING));

 HAL_CRC_Calculate() gives the same result of 

INF esp/apps/shell.c[92] shell running crc: 0x88406c69!!!

INF esp/apps/shell.c[93] shell XORed crc: 0x77bf9396!!!

CRC32stm(0x04C11DB7, 0xffffffff, offXOR, offRefIn, offRefOut) = 0x88406c69

And 

CRC32std(0xEDB88320, 0xffffffff, offXOR, onRefIn, onRefOut)  = 0x88406c69

From this I guessed that if I changed the final XOR, and input/output reflection, I will get the same CRC as that of WireShark. But STM32 CRC result didn't change at all.

 ------------------ will continue in answer -----------------

8 REPLIES 8
KShim.1738
Associate III

here is the STM32 CRC calculation result with given settings.

test string "abcdefghijklmnopqrstuvwxyz"

--------------------------------------------------------------
 hcrc.Instance = CRC;
 hcrc.Init.DefaultPolynomialUse = DEFAULT_POLYNOMIAL_ENABLE;
 hcrc.Init.DefaultInitValueUse = DEFAULT_INIT_VALUE_ENABLE;
 hcrc.Init.InputDataInversionMode = CRC_INPUTDATA_INVERSION_NONE;
 hcrc.Init.OutputDataInversionMode = CRC_OUTPUTDATA_INVERSION_DISABLE;
 hcrc.InputDataFormat = CRC_INPUTDATA_FORMAT_BYTES;
 
INF esp/apps/shell.c[92] shell running crc: 0x88406c69!!!
INF esp/apps/shell.c[93] shell XORed crc: 0x77bf9396!!!
 
--------------------------------------------------------------
 hcrc.Instance = CRC;
 hcrc.Init.DefaultPolynomialUse = DEFAULT_POLYNOMIAL_ENABLE;
 hcrc.Init.DefaultInitValueUse = DEFAULT_INIT_VALUE_ENABLE;
 hcrc.Init.InputDataInversionMode = CRC_INPUTDATA_INVERSION_BYTE;
 hcrc.Init.OutputDataInversionMode = CRC_OUTPUTDATA_INVERSION_ENABLE;
 hcrc.InputDataFormat = CRC_INPUTDATA_FORMAT_BYTES;
 
INF esp/apps/shell.c[92] shell running crc: 0x88406c69!!!
INF esp/apps/shell.c[93] shell XORed crc: 0x77bf9396!!!
 
--------------------------------------------------------------
 hcrc.Instance = CRC;
 hcrc.Init.DefaultPolynomialUse = DEFAULT_POLYNOMIAL_ENABLE;
 hcrc.Init.DefaultInitValueUse = DEFAULT_INIT_VALUE_ENABLE;
 hcrc.Init.InputDataInversionMode = CRC_INPUTDATA_INVERSION_WORD;
 hcrc.Init.OutputDataInversionMode = CRC_OUTPUTDATA_INVERSION_ENABLE;
 hcrc.InputDataFormat = CRC_INPUTDATA_FORMAT_BYTES;
 
INF esp/apps/shell.c[93] shell XORed crc: 0x77bf9396!!!
INF esp/apps/shell.c[92] shell running crc: 0x88406c69!!!
 
--------------------------------------------------------------
 
 hcrc.Instance = CRC;
 hcrc.Init.DefaultPolynomialUse = DEFAULT_POLYNOMIAL_ENABLE;
 hcrc.Init.DefaultInitValueUse = DEFAULT_INIT_VALUE_ENABLE;
 hcrc.Init.InputDataInversionMode = CRC_INPUTDATA_INVERSION_WORD;
 hcrc.Init.OutputDataInversionMode = CRC_OUTPUTDATA_INVERSION_DISABLE;
 hcrc.InputDataFormat = CRC_INPUTDATA_FORMAT_BYTES;
 
INF esp/apps/shell.c[93] shell XORed crc: 0x77bf9396!!!
INF esp/apps/shell.c[92] shell running crc: 0x88406c69!!!
--------------------------------------------------------------

Please help me how STM32F7 CRC unit works.

Best regards,

Kyle

KShim.1738
Associate III

And the test code of CRC bitwise algorithm.

#include <inttypes.h>
#include <stdio.h>
#include <string.h>
 
#define CRC_TEST_STRING "abcdefghijklmnopqrstuvwxyz"
 
uint8_t reflect8(uint8_t val)
{
    uint8_t resVal = 0;
 
    for(int i = 0; i < 8; i++)
    {
        if ((val & (1 << i)) != 0)
        {
            resVal |= (uint8_t )(1 << (7 - i));
        }
    }
 
    return resVal;
}
 
uint32_t reflect32(uint32_t val)
{
    uint32_t resVal = 0;
 
    for(int i = 0; i < 32; i++)
    {
        if ((val & (1 << i)) != 0)
        {
            resVal |= (uint32_t )(1 << (31 - i));
        }
    }
 
    return resVal;
}
 
 
uint32_t calcCRC32stm(void *data, uint32_t len,
   uint32_t poly, uint32_t seed, uint32_t initCRC, uint32_t inR, uint32_t outR)
{
    const unsigned char *buffer = (const unsigned char*) data;
    unsigned int crc = seed;
    unsigned char byte;
 
    while( len-- )
    {
        byte = *buffer++;
        if(inR) {
            byte = reflect8(byte);
        }
        crc = crc ^ (byte << 24);
        for( int bit = 0; bit < 8; bit++ )
        {
            if( crc & (1L << 31)) crc = (crc << 1) ^ poly;
            else                  crc = (crc << 1);
        }
    }
    if(outR) {
        crc = reflect32(crc);
    }
    if(initCRC == 1)    crc = ~crc;
    return crc;
}
 
uint32_t calcCRC32std(void *data, uint32_t len,
   uint32_t poly, uint32_t seed, uint32_t initCRC, uint32_t inR, uint32_t outR)
{
    uint32_t crc;
    unsigned char* current = (unsigned char*) data; 
    uint8_t byte;
 
    crc = seed;
 
    while (len--) { 
        byte = *current++; 
        if(inR) {
            byte = reflect8(byte);
        }
        crc ^= byte; 
        for (unsigned int j = 0; j < 8; j++) {
            if (crc & 1) 
                crc = (crc >> 1) ^ poly; 
            else 
                crc = crc >> 1; 
        }
 
    } 
    if(outR) {
        crc = reflect32(crc);
    }
    if(initCRC == 1)    crc = ~crc;
    return crc;
}
 
int main(void) {
    char *ptr;
    uint32_t len;
    uint32_t crc;
    uint32_t poly0x04C11DB7;
    uint32_t poly0xEDB88320;
    uint32_t seed;
    uint32_t refIN;
    uint32_t refOUT;
    uint32_t initCRC0x00000000;
    uint32_t initCRC0xffffffff;
 
    uint32_t word;
    uint32_t wordReflected;
    uint32_t byte;
    uint32_t byteReflected;
 
    ptr = CRC_TEST_STRING;
    len = strlen(CRC_TEST_STRING);
    poly0x04C11DB7 = 0x04C11DB7;
    seed = 0xffffffff;
    refIN = 0;
    refOUT = 0;
    initCRC0x00000000 = 0x00000000;
    initCRC0xffffffff = 0xffffffff;
 
    printf("\n --------- reflection test --------- \n");
 
    byte = 0x14;
    byteReflected = reflect8(byte);
    printf("byte reflect8: 0x%02x vs. 0x%02x\n", byte, byteReflected);
 
    word = 0x01020408;
    wordReflected = reflect32(word);
    printf("word reflect32: 0x%08x vs. 0x%08x\n", word, wordReflected);
 
    printf("\n\n");
 
    poly0xEDB88320 = reflect32(poly0x04C11DB7);
    printf("poly0x04C11DB7 reflected: 0x%08X\n", poly0xEDB88320);
 
    printf("\n\n");
 
    printf("test string: %s\n", CRC_TEST_STRING);
 
    printf("\n ------------ SHIFT R/L ------------ \n");
 
    crc = calcCRC32std(ptr, len, poly0xEDB88320, initCRC0xffffffff, 1, 0, 0);
    printf("CRC32std(0xEDB88320, 0xffffffff, onXOR, offRefIn, offRefOut)  = "
           "0x%08x\n", crc);
 
    crc = calcCRC32stm(ptr, len, poly0x04C11DB7, initCRC0xffffffff, 0, 0, 0);
    printf("CRC32stm(0x04C11DB7, 0xffffffff, offXOR, offRefIn, offRefOut) = "
           "0x%08x\n", crc);
 
    printf("\n ------------ CRC32 std ------------ \n");
    crc = calcCRC32std(ptr, len, poly0xEDB88320, initCRC0xffffffff, 1, 0, 0);
    printf("CRC32std(0xEDB88320, 0xffffffff, onXOR, offRefIn, offRefOut)  = "
           "0x%08x\n", crc);
 
    crc = calcCRC32std(ptr, len, poly0xEDB88320, initCRC0xffffffff, 0, 1, 1);
    printf("CRC32std(0xEDB88320, 0xffffffff, offXOR, onRefIn, onRefOut)   = "
           "0x%08x\n", crc);
 
    crc = calcCRC32stm(ptr, len, poly0x04C11DB7, initCRC0xffffffff, 0, 0, 0);
    printf("CRC32stm(0x04C11DB7, 0xffffffff, offXOR, offRefIn, offRefOut) = "
           "0x%08x\n", crc);
 
    crc = calcCRC32stm(ptr, len, poly0x04C11DB7, initCRC0xffffffff, 1, 1, 1);
    printf("CRC32stm(0x04C11DB7, 0xffffffff, onXOR, onRefIn, onRefOut)    = "
           "0x%08x\n", crc);
 
    printf("\nDerived from the two basic ones above!!!\n\n");
 
    printf("\n ------------ CRC32 BZIP2 ------------ \n");
 
    crc = calcCRC32std(ptr, len, poly0xEDB88320, initCRC0xffffffff, 1, 1, 1);
    printf("CRC32std(0xEDB88320, 0xffffffff, onXOR, onRefIn, onRefOut)    = "
           "0x%08x\n", crc);
 
    crc = calcCRC32stm(ptr, len, poly0x04C11DB7, initCRC0xffffffff, 1, 0, 0);
    printf("CRC32stm(0x04C11DB7, 0xffffffff, onXOR, offRefIn, offnRefOut) = "
           "0x%08x\n", crc);
 
    printf("\n ----------- CRC32 Posix ----------- \n");
 
    crc = calcCRC32std(ptr, len, poly0xEDB88320, initCRC0x00000000, 1, 1, 1);
    printf("CRC32std(0xEDB88320, 0x00000000, onXOR, onRefIn, onRefOut)    = "
           "0x%08x\n", crc);
 
    crc = calcCRC32stm(ptr, len, poly0x04C11DB7, initCRC0x00000000, 1, 0, 0);
    printf("CRC32stm(0x04C11DB7, 0x00000000, onXOR, offRefIn, offRefOut)  = "
           "0x%08x\n", crc);
 
    printf("\n ----------- CRC32JAMCRC ----------- \n");
 
    crc = calcCRC32std(ptr, len, poly0xEDB88320, initCRC0xffffffff, 0, 0, 0);
    printf("CRC32std(0xEDB88320, 0xffffffff, offXOR, offRefIn, offRefOut) = "
           "0x%08x\n", crc);
 
    crc = calcCRC32stm(ptr, len, poly0x04C11DB7, initCRC0xffffffff, 0, 1, 1);
    printf("CRC32stm(0x04C11DB7, 0xffffffff, offXOR, onRefIn, onRefOut)   = "
           "0x%08x\n", crc);
 
    return 0;
}

Don't use Cube/HAL, it just adds an unnecessary level of uncertainty. Write your own routine working with CRC registers. After resetting CRC by setting CRC_CR.RESET, read CRC_CR back until RESET bit gets cleared by hardware.

Try one single data word initially. Using 26 bytes might lead to additional confusion.

The STM32 CRC unit is non-standard in that it feeds bits least-significant-bit first, but uses the most-significant byte's bits first. I'm not sure how exactly the individual "swap" options in the CRC unit change this behaviour, you might want to experiment.

JW

TDK
Guru

Here are my notes for the CRC:

The CRC calculation on the STM32 uses:
* definitions from http://www.zorc.breitbandkatze.de/crc.html
* poly = 0x04C11DB7
* init = FFFFFFFF
* final_xor = 0
* reverse_data_byte = false
* reverse_crc_before_xor = false

 That, coupled with the endian-swapping that JW mentioned, should be enough for you to replicate what the STM32 is doing.

If you feel a post has answered your question, please click "Accept as Solution".
KShim.1738
Associate III

Yesterday right after I posted my question, my F7 disco board didn't respond. It looked like the firmware has been programmed and STM32CubeProgrammer reported it was successful. But the SWD debugger module on DISCO board didn't work. The RED LED next to USB socket is left on, don't go off.

My test on F7 disco was WRONG!!! I suspect the FIRMWARE was not properly programmed and gave me the wrong result.

I used other custom board and I got the wanted result.

static void MX_CRC_Init(void)
{
 
  /* USER CODE BEGIN CRC_Init 0 */
 
  /* USER CODE END CRC_Init 0 */
 
  /* USER CODE BEGIN CRC_Init 1 */
 
  /* USER CODE END CRC_Init 1 */
  hcrc.Instance = CRC;
  hcrc.Init.DefaultPolynomialUse = DEFAULT_POLYNOMIAL_ENABLE;
  hcrc.Init.DefaultInitValueUse = DEFAULT_INIT_VALUE_ENABLE;
  hcrc.Init.InputDataInversionMode = CRC_INPUTDATA_INVERSION_BYTE;
  hcrc.Init.OutputDataInversionMode = CRC_OUTPUTDATA_INVERSION_ENABLE;
  hcrc.InputDataFormat = CRC_INPUTDATA_FORMAT_BYTES;
  if (HAL_CRC_Init(&hcrc) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN CRC_Init 2 */
 
  /* USER CODE END CRC_Init 2 */
 
}
#define CRC_TEST_STRING "abcdefghijklmnopqrstuvwxyz"
 
 
            crc = HAL_CRC_Calculate(&hcrc, (uint32_t *)CRC_TEST_STRING, strlen(CRC_TEST_STRING));
            DBG_PRINT(DBG_INFO, "shell running crc: 0x%08x!!!\r\n", crc);
            DBG_PRINT(DBG_INFO, "shell XORed crc: 0x%08x!!!\r\n", ~crc);

This gives me the same CRC value as that of WireShark, which looks like a standard CRC32 to me(I'm not sure about it).

INF esp/apps/shell.c[99] shell XORed crc: 0x4c2750bd!!!

INF esp/apps/shell.c[98] shell running crc: 0xb3d8af42!!!

It is necessary to XOR the CRC returned by HAL_CRC_Calculate(). I didn't check the other 3 named CRCs. But I believe that STM32 F7 CRC unit is able to generate those named CRCs.

Best regards,

Kyle

Thank you for your reply. But it looks safer to use HAL to me. And I would like to be consistent on using STM32 HAL libraries.

Thank you for your reply. But it looks safer to use HAL to me. And I would like to be consistent on using STM32 HAL libraries.

BPrin.1
Associate

The STM32 CRC32 unit is calculating values as if they are big endian. When you load a register on the ARM, it is little endian. This the main defect as well as AN4187 and sample source en.stsw-stm32an4187.zip. As well, it is not document by the product data sheets I have studied.

Here is a github sample that some to the same conclusions.

github.com/dimmykar/stm32-crc32-calculator

You can use either 'swap' or 'rev' to achieve a standard CCITT CRC32, depending on the 'reflected' parameters, before writing to the register. If you do not, the effective polynomial is not as documented.