2025-06-20 4:30 AM
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
Solved! Go to Solution.
2025-06-20 11:51 AM
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
2025-06-20 5:01 AM
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.
2025-06-20 10:01 AM
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
2025-06-20 11:51 AM
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
2025-06-22 10:06 AM
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
2025-06-23 12:50 AM
Cool! That did it.
THX
2025-07-02 8:57 AM
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