Skip to main content
Visitor II
June 20, 2025
Solved

Unexpected results with iterative use of STM32H5 CRC unit

  • June 20, 2025
  • 3 replies
  • 590 views

Hi,

I am trying to use the STM32H5 CRC unit via HAL to calculate a CRC32 (ISO-HDLC) over multiple chunks.

This works correctly via:

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();
}

// calculate CRC32 over two identical chunks, HCRC keeps state!
temp = HAL_CRC_Calculate(&hcrc, (uint32_t *)data, strlen(data));	// 0x444a273c
crc = ~temp;							// 0xbbb5d8c3 OK
temp = HAL_CRC_Accumulate(&hcrc, (uint32_t *)data, strlen(data));	// 0x405e36b1
crc = ~temp;						// 0xbfa1c94e OK

Now I need to do something similiar as in https://community.st.com/t5/stm32-mcus-products/hal-function-crc-accumulate-re-init-and-calculate/td-p/672395: the CRC unit is shared for different things and I need to reinitialize it between the chunks. Where is the missed trick in that solution?

hcrc.Instance->INIT = 0xffffffff;
temp = HAL_CRC_Calculate(&hcrc, (uint32_t *)data, strlen(data));	// 0x444a273c
crc = ~temp;							// 0xbbb5d8c3 OK

// ...

// restart with former DR register content
hcrc.Instance->INIT = temp;
temp = HAL_CRC_Calculate(&hcrc, (uint32_t *)data, strlen(data)); 	// 0x3ce52cc3 unexpected!
crc = ~temp;							// 0xc31ad33c

For the 2nd chunk I do not invert the prior result. What am I missing? I want both code snippets to give the same results.

This is my reference:

import crcmod

# Create CRC function with HDLC parameters
crc32_hdlc = crcmod.mkCrcFun(0x104C11DB7, initCrc=0, rev=True, xorOut=0xFFFFFFFF)

crc = crc32_hdlc(chunk)
print(f"CRC32/HDLC: {crc:08X}")

crc = crc32_hdlc(chunk,crc)
print(f"CRC32/HDLC: {crc:08X}")

#Prints:
#CRC32/HDLC: BBB5D8C3
#CRC32/HDLC: BFA1C94E

Thanks!

Matthias

    This topic has been closed for replies.
    Best answer by Tesla DeLorean

    I did this with a STM32U5, but is illustrative

    void CRCTest(void)
    {
     uint32_t temp, crc, goodcrc;
     CRC_HandleTypeDef hcrc = {0};
     char data[] = "test";
    
     __HAL_RCC_CRC_CLK_ENABLE();
    
     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;
    
     hcrc.Init.CRCLength = CRC_POLYLENGTH_32B;
    
     if (HAL_CRC_Init(&hcrc) != HAL_OK)
     {
     Error_Handler();
     }
    
     // calculate CRC32 over two identical chunks, HCRC keeps state!
     temp = HAL_CRC_Calculate(&hcrc, (uint32_t *)data, strlen(data));
     crc = ~temp;
     printf("%08X-%08X\n", crc, temp);
     printf("%08X DR\n", hcrc.Instance->DR); // Same as temp going into second phase
     temp = HAL_CRC_Accumulate(&hcrc, (uint32_t *)data, strlen(data));
     crc = ~temp;
     printf("%08X-%08X\n", crc, temp);
     putchar('\n');
    
     goodcrc = crc;
    
     // Alternate, reinitialize method
    
     hcrc.Instance->INIT = 0xffffffff;
     temp = HAL_CRC_Calculate(&hcrc, (uint32_t *)data, strlen(data));
     crc = ~temp;
     printf("%08X-%08X\n", crc, temp);
    
    // ...
    
     // restart with former DR register content
     hcrc.Instance->INIT = __RBIT(temp); // Reverse bit order of value
     printf("%08X %08X INIT,DR\n", hcrc.Instance->INIT, hcrc.Instance->DR); // To understand the bit reversal/mirroring
    
     temp = HAL_CRC_Calculate(&hcrc, (uint32_t *)data, strlen(data));
     crc = ~temp;
     printf("%08X-%08X\n", crc, temp);
    
     if (crc == goodcrc)
     puts("== PASS ==");
     else
     puts("** FAIL **");
    } // sourcer32@gmail.com

    3 replies

    Graduate II
    June 20, 2025

    How's "data" defined? Provide complete test patterns

    Initialized as 0x00000000 or 0xFFFFFFFF

    Might need to bit reverse, not invert, to push in intermediate context.

    Graduate II
    June 20, 2025

    I did this with a STM32U5, but is illustrative

    void CRCTest(void)
    {
     uint32_t temp, crc, goodcrc;
     CRC_HandleTypeDef hcrc = {0};
     char data[] = "test";
    
     __HAL_RCC_CRC_CLK_ENABLE();
    
     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;
    
     hcrc.Init.CRCLength = CRC_POLYLENGTH_32B;
    
     if (HAL_CRC_Init(&hcrc) != HAL_OK)
     {
     Error_Handler();
     }
    
     // calculate CRC32 over two identical chunks, HCRC keeps state!
     temp = HAL_CRC_Calculate(&hcrc, (uint32_t *)data, strlen(data));
     crc = ~temp;
     printf("%08X-%08X\n", crc, temp);
     printf("%08X DR\n", hcrc.Instance->DR); // Same as temp going into second phase
     temp = HAL_CRC_Accumulate(&hcrc, (uint32_t *)data, strlen(data));
     crc = ~temp;
     printf("%08X-%08X\n", crc, temp);
     putchar('\n');
    
     goodcrc = crc;
    
     // Alternate, reinitialize method
    
     hcrc.Instance->INIT = 0xffffffff;
     temp = HAL_CRC_Calculate(&hcrc, (uint32_t *)data, strlen(data));
     crc = ~temp;
     printf("%08X-%08X\n", crc, temp);
    
    // ...
    
     // restart with former DR register content
     hcrc.Instance->INIT = __RBIT(temp); // Reverse bit order of value
     printf("%08X %08X INIT,DR\n", hcrc.Instance->INIT, hcrc.Instance->DR); // To understand the bit reversal/mirroring
    
     temp = HAL_CRC_Calculate(&hcrc, (uint32_t *)data, strlen(data));
     crc = ~temp;
     printf("%08X-%08X\n", crc, temp);
    
     if (crc == goodcrc)
     puts("== PASS ==");
     else
     puts("** FAIL **");
    } // sourcer32@gmail.com
    MFuch.3Author
    Visitor II
    June 23, 2025

    Cool! That did it. 

    THX

    Super User
    June 20, 2025

    And if you avoid Cube/HAL? Using the CRC unit at register level is easy, just mind the endianness and written data width. For a few-words experiment, doing it entirely in debugger with no code is sufficient and fun.

    JW

    Super User
    June 22, 2025

    Ooooh, I see it now, CRC_CR.REV_OUT does nothing else just inserts a bit swapper between the real internal data register and the APB readout interface!

    Logical, but the consequence is somewhat surprising.

    JW

    Graduate II
    July 2, 2025

    Yes, they have the CRC always LEFT shifting internally, and either flip the register on read, or flip the data upon injection.

    When I did the 5-bit, 24-bit, etc hacks I'd push the most significant bits of the register and polynomial up there, and then shift back the DR at the end to recover the bits of interest.

    Now ST does say that poly's need to be ODD, ie .. + x + 1, but that's not really how the HW works, and they can be shifted to fit provided the feed-back term does it's thing