Skip to main content
Roth.Ian
Associate III
April 30, 2026
Solved

STM32U575VGT CRC checksum through DMA

  • April 30, 2026
  • 8 replies
  • 167 views

I am working on converting projects from STM32F745VET to STM32U575VGT. Part of the startup routine of the projects is checking for flash errors using the CRC peripheral. The STM32F7 projects initialize a DMA channel in the following way:

void MX_DMA_Init(void)
{

/* DMA controller clock enable */
__HAL_RCC_DMA1_CLK_ENABLE();
__HAL_RCC_DMA2_CLK_ENABLE();

/* Configure DMA request hdma_memtomem_dma2_stream1 on DMA2_Stream1 */
hdma_memtomem_dma2_stream1.Instance = DMA2_Stream1;
hdma_memtomem_dma2_stream1.Init.Channel = DMA_CHANNEL_0;
hdma_memtomem_dma2_stream1.Init.Direction = DMA_MEMORY_TO_MEMORY;
hdma_memtomem_dma2_stream1.Init.PeriphInc = DMA_PINC_ENABLE;
hdma_memtomem_dma2_stream1.Init.MemInc = DMA_MINC_DISABLE;
hdma_memtomem_dma2_stream1.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;
hdma_memtomem_dma2_stream1.Init.MemDataAlignment = DMA_MDATAALIGN_WORD;
hdma_memtomem_dma2_stream1.Init.Mode = DMA_NORMAL;
hdma_memtomem_dma2_stream1.Init.Priority = DMA_PRIORITY_LOW;
hdma_memtomem_dma2_stream1.Init.FIFOMode = DMA_FIFOMODE_ENABLE;
hdma_memtomem_dma2_stream1.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_FULL;
hdma_memtomem_dma2_stream1.Init.MemBurst = DMA_MBURST_SINGLE;
hdma_memtomem_dma2_stream1.Init.PeriphBurst = DMA_PBURST_SINGLE;
if (HAL_DMA_Init(&hdma_memtomem_dma2_stream1) != HAL_OK)
{
Error_Handler();
}
}

And then in the code:

__HAL_CRC_DR_RESET(&hcrc);
HAL_DMA_Start_IT(&handle_GPDMA1_Channel12, checkstart, CRC_BASE, checkrange);

where checkstart is defined as 0x08000000 and checkrange is 0x0000FFFC

I tried to use a GPDMA channel on the STM32U5 in the same way, but the calculated checksum does not match. Using the HAL_CRC_Calculate function does result in a checksum match, but this uses the CPU instead of offloading it to DMA.
Is there a way to feed the flash into the CRC peripheral through a DMA channel on the STM32U5?

Best answer by Roth.Ian

@Khaled_DHIF Thank you for this insight, I was not aware of the change in DMA transfer length calculation. Your solution worked for checking flash memory from 0x08000000 to 0x0800FFFB, where 0x0800FFFB is end of word addressing in terms of bytes in 16 bits. I looked at the reference manual for why this was the limit but did not find anything in the memory map. Stepping through the code with the debugger, I realized that although HAL functions passed the transfer length as a 32bit value, it was in fact limited by the size of BNDT in the GPDMA CxBR1 register. To transfer something larger, I would need to use a linked list. I configured in STM32CubeMX a linked list for transferring blocks of 0xFFFC as follows:

/**
 * @brief DMA Linked-list FlashMemory configuration
 * @param None
 * @retval None
 */
HAL_StatusTypeDef MX_FlashMemory_Config(void)
{
 HAL_StatusTypeDef ret = HAL_OK;
 /* DMA node configuration declaration */
 DMA_NodeConfTypeDef pNodeConfig;

 /* Set node configuration ################################################*/
 pNodeConfig.NodeType = DMA_GPDMA_LINEAR_NODE;
 pNodeConfig.Init.Request = DMA_REQUEST_SW;
 pNodeConfig.Init.BlkHWRequest = DMA_BREQ_SINGLE_BURST;
 pNodeConfig.Init.Direction = DMA_MEMORY_TO_MEMORY;
 pNodeConfig.Init.SrcInc = DMA_SINC_INCREMENTED;
 pNodeConfig.Init.DestInc = DMA_DINC_FIXED;
 pNodeConfig.Init.SrcDataWidth = DMA_SRC_DATAWIDTH_WORD;
 pNodeConfig.Init.DestDataWidth = DMA_DEST_DATAWIDTH_WORD;
 pNodeConfig.Init.SrcBurstLength = 1;
 pNodeConfig.Init.DestBurstLength = 1;
 pNodeConfig.Init.TransferAllocatedPort = DMA_SRC_ALLOCATED_PORT1|DMA_DEST_ALLOCATED_PORT0;
 pNodeConfig.Init.TransferEventMode = DMA_TCEM_BLOCK_TRANSFER;
 pNodeConfig.TriggerConfig.TriggerPolarity = DMA_TRIG_POLARITY_MASKED;
 pNodeConfig.DataHandlingConfig.DataExchange = DMA_EXCHANGE_NONE;
 pNodeConfig.DataHandlingConfig.DataAlignment = DMA_DATA_RIGHTALIGN_ZEROPADDED;
 pNodeConfig.SrcAddress = 0x08000000;
 pNodeConfig.DstAddress = CRC_BASE;
 pNodeConfig.DataSize = 0xFFFC;

 /* Build Block1 Node */
 ret |= HAL_DMAEx_List_BuildNode(&pNodeConfig, &Block1);

 /* Insert Block1 to Queue */
 ret |= HAL_DMAEx_List_InsertNode_Tail(&FlashMemory, &Block1);

 /* Set node configuration ################################################*/
 pNodeConfig.SrcAddress = 0x0800FFFC;

 /* Build Block2 Node */
 ret |= HAL_DMAEx_List_BuildNode(&pNodeConfig, &Block2);

 /* Insert Block2 to Queue */
 ret |= HAL_DMAEx_List_InsertNode_Tail(&FlashMemory, &Block2);

 return ret;
}

And in the code I started the transfer as follows:

MX_FlashMemory_Config();
HAL_DMAEx_List_LinkQ(&handle_GPDMA1_Channel12, &FlashMemory); 
__HAL_CRC_DR_RESET(&hcrc);
HAL_DMAEx_List_Start_IT(&handle_GPDMA1_Channel12);

This works if the IAR project calculates a checksum from 0x08000000 to 0x0801FFF7. I hope this helps anyone trying to run a checksum on the STM32U5 line.

8 replies

waclawek.jan
Super User
May 1, 2026

And does DMA and "manual" checksum match, if you use e.g. checkrange=1?

If not, what are the content of CRC registers for the two cases?

JW

Roth.Ian
Roth.IanAuthor
Associate III
May 5, 2026

I set the end address to 0x08000003 in the IAR EWARM linker dialog. If I set checkrange to 1, the HAL_CRC_Calculate method comes up with a match. The DMA version does not match.

Expected value calculated by EWARM: 0x29C17239

Calculated value from CRC with DMA input: 0x6800520C

waclawek.jan
Super User
May 5, 2026

I meant, comparing all CRC registers' content, and perhaps looking at DMA's registers, too.

What value is at 0x0800'0000?

JW

 

Roth.Ian
Roth.IanAuthor
Associate III
May 5, 2026

The DR register is reset to 0xFFFFFFFF before the DMA transfer or HAL_CRC_Calculate command. Changing between the two does not change the value at 0x0800000, which in the memory map of EWARM is listed as 08 53 01 20.

Technical Moderator
May 7, 2026

Hello @Roth.Ian 

Could you please provide your DMA configuration?

"To give better visibility on the answered topics, please click on ""Accept as Solution"" on the reply which solved your issue or answered your question.Saket_Om"
Roth.Ian
Roth.IanAuthor
Associate III
May 7, 2026

HI @Saket_Om 

Please see the STM32F7 code from above. Here is the STM32U5 configuration where I try to replicate it:

/* GPDMA1 init function */
void MX_GPDMA1_Init(void)
{

 /* USER CODE BEGIN GPDMA1_Init 0 */

 /* USER CODE END GPDMA1_Init 0 */

 /* Peripheral clock enable */
 __HAL_RCC_GPDMA1_CLK_ENABLE();

 /* GPDMA1 interrupt Init */
 HAL_NVIC_SetPriority(GPDMA1_Channel1_IRQn, 8, 0);
 HAL_NVIC_EnableIRQ(GPDMA1_Channel1_IRQn);
 HAL_NVIC_SetPriority(GPDMA1_Channel2_IRQn, 7, 0);
 HAL_NVIC_EnableIRQ(GPDMA1_Channel2_IRQn);
 HAL_NVIC_SetPriority(GPDMA1_Channel3_IRQn, 7, 0);
 HAL_NVIC_EnableIRQ(GPDMA1_Channel3_IRQn);
 HAL_NVIC_SetPriority(GPDMA1_Channel4_IRQn, 7, 0);
 HAL_NVIC_EnableIRQ(GPDMA1_Channel4_IRQn);
 HAL_NVIC_SetPriority(GPDMA1_Channel5_IRQn, 7, 0);
 HAL_NVIC_EnableIRQ(GPDMA1_Channel5_IRQn);
 HAL_NVIC_SetPriority(GPDMA1_Channel7_IRQn, 7, 0);
 HAL_NVIC_EnableIRQ(GPDMA1_Channel7_IRQn);
 HAL_NVIC_SetPriority(GPDMA1_Channel8_IRQn, 8, 0);
 HAL_NVIC_EnableIRQ(GPDMA1_Channel8_IRQn);
 HAL_NVIC_SetPriority(GPDMA1_Channel12_IRQn, 5, 0);
 HAL_NVIC_EnableIRQ(GPDMA1_Channel12_IRQn);

 /* USER CODE BEGIN GPDMA1_Init 1 */

 /* USER CODE END GPDMA1_Init 1 */
 handle_GPDMA1_Channel12.Instance = GPDMA1_Channel12;
 handle_GPDMA1_Channel12.Init.Request = DMA_REQUEST_SW;
 handle_GPDMA1_Channel12.Init.BlkHWRequest = DMA_BREQ_SINGLE_BURST;
 handle_GPDMA1_Channel12.Init.Direction = DMA_MEMORY_TO_MEMORY;
 handle_GPDMA1_Channel12.Init.SrcInc = DMA_SINC_INCREMENTED;
 handle_GPDMA1_Channel12.Init.DestInc = DMA_DINC_FIXED;
 handle_GPDMA1_Channel12.Init.SrcDataWidth = DMA_SRC_DATAWIDTH_WORD;
 handle_GPDMA1_Channel12.Init.DestDataWidth = DMA_DEST_DATAWIDTH_WORD;
 handle_GPDMA1_Channel12.Init.Priority = DMA_LOW_PRIORITY_LOW_WEIGHT;
 handle_GPDMA1_Channel12.Init.SrcBurstLength = 1;
 handle_GPDMA1_Channel12.Init.DestBurstLength = 1;
 handle_GPDMA1_Channel12.Init.TransferAllocatedPort = DMA_SRC_ALLOCATED_PORT1|DMA_DEST_ALLOCATED_PORT0;
 handle_GPDMA1_Channel12.Init.TransferEventMode = DMA_TCEM_BLOCK_TRANSFER;
 handle_GPDMA1_Channel12.Init.Mode = DMA_NORMAL;
 if (HAL_DMA_Init(&handle_GPDMA1_Channel12) != HAL_OK)
 {
 Error_Handler();
 }
 if (HAL_DMA_ConfigChannelAttributes(&handle_GPDMA1_Channel12, DMA_CHANNEL_NPRIV) != HAL_OK)
 {
 Error_Handler();
 }
 handle_GPDMA1_Channel7.Instance = GPDMA1_Channel7;
 handle_GPDMA1_Channel7.Init.Request = DMA_REQUEST_SW;
 handle_GPDMA1_Channel7.Init.BlkHWRequest = DMA_BREQ_SINGLE_BURST;
 handle_GPDMA1_Channel7.Init.Direction = DMA_MEMORY_TO_MEMORY;
 handle_GPDMA1_Channel7.Init.SrcInc = DMA_SINC_INCREMENTED;
 handle_GPDMA1_Channel7.Init.DestInc = DMA_DINC_FIXED;
 handle_GPDMA1_Channel7.Init.SrcDataWidth = DMA_SRC_DATAWIDTH_WORD;
 handle_GPDMA1_Channel7.Init.DestDataWidth = DMA_DEST_DATAWIDTH_WORD;
 handle_GPDMA1_Channel7.Init.Priority = DMA_LOW_PRIORITY_LOW_WEIGHT;
 handle_GPDMA1_Channel7.Init.SrcBurstLength = 1;
 handle_GPDMA1_Channel7.Init.DestBurstLength = 1;
 handle_GPDMA1_Channel7.Init.TransferAllocatedPort = DMA_SRC_ALLOCATED_PORT1|DMA_DEST_ALLOCATED_PORT0;
 handle_GPDMA1_Channel7.Init.TransferEventMode = DMA_TCEM_BLOCK_TRANSFER;
 handle_GPDMA1_Channel7.Init.Mode = DMA_NORMAL;
 if (HAL_DMA_Init(&handle_GPDMA1_Channel7) != HAL_OK)
 {
 Error_Handler();
 }
 if (HAL_DMA_ConfigChannelAttributes(&handle_GPDMA1_Channel7, DMA_CHANNEL_NPRIV) != HAL_OK)
 {
 Error_Handler();
 }
 /* USER CODE BEGIN GPDMA1_Init 2 */

 /* USER CODE END GPDMA1_Init 2 */

}

I have tried both Channel 7 and 12 to see if there was a difference with a larger FIFO. I have changed the ports around based on my understand of Figure 2 in AN5593. Circular mode for the source did not seem to lead to good results.

 

Technical Moderator
May 12, 2026

Hello @Roth.Ian 

Are the DMA HTC and TC interrupt flags triggered correctly?

"To give better visibility on the answered topics, please click on ""Accept as Solution"" on the reply which solved your issue or answered your question.Saket_Om"
waclawek.jan
Super User
May 12, 2026

Also, what happens if you define an array in memory and in the DMA command change the destination address to that array? Is the first word from FLASH stored there?

JW

Roth.Ian
Roth.IanAuthor
Associate III
May 14, 2026

Hello @Saket_Om ,

They are triggered correctly.

ST Employee
May 15, 2026

Hello all, 

On STM32U5, CRC has no dedicated GPDMA request, so the working approach is a software-triggered GPDMA transfer that writes directly to CRC->DR.

The main point to check is the transfer length unit:
HAL_CRC_Calculate() uses a word count when the input format is words, while HAL_DMA_Start_IT() on U5 expects the transfer size in bytes.

So, if your DMA call is:

 

HAL_DMA_Start_IT(&handle_GPDMA1_Channel12, checkstart, (uint32_t)&CRC->DR, checkrange);

and checkrange is a word count, the DMA length is wrong. In that case, try:

__HAL_CRC_DR_RESET(&hcrc);
HAL_DMA_Start_IT(&handle_GPDMA1_Channel12, checkstart,(uint32_t)&CRC->DR, checkrange * 4U);

 

And the recommended GPDMA settings are:
- Request = DMA_REQUEST_SW
- Direction = DMA_MEMORY_TO_MEMORY
- SrcInc = DMA_SINC_INCREMENTED
- DestInc = DMA_DINC_FIXED
- SrcDataWidth = WORD
- DestDataWidth = WORD

 

If the byte/word length mismatch was the issue, both results should then match.

Kind regards, 

DHIF Khaled

 

"Please mark my answer as best by clicking on the “Accept as solution"" button if it fully answered your question. This will help other users find this solution faster.​"
Roth.Ian
Roth.IanAuthorAnswer
Associate III
May 20, 2026

@Khaled_DHIF Thank you for this insight, I was not aware of the change in DMA transfer length calculation. Your solution worked for checking flash memory from 0x08000000 to 0x0800FFFB, where 0x0800FFFB is end of word addressing in terms of bytes in 16 bits. I looked at the reference manual for why this was the limit but did not find anything in the memory map. Stepping through the code with the debugger, I realized that although HAL functions passed the transfer length as a 32bit value, it was in fact limited by the size of BNDT in the GPDMA CxBR1 register. To transfer something larger, I would need to use a linked list. I configured in STM32CubeMX a linked list for transferring blocks of 0xFFFC as follows:

/**
 * @brief DMA Linked-list FlashMemory configuration
 * @param None
 * @retval None
 */
HAL_StatusTypeDef MX_FlashMemory_Config(void)
{
 HAL_StatusTypeDef ret = HAL_OK;
 /* DMA node configuration declaration */
 DMA_NodeConfTypeDef pNodeConfig;

 /* Set node configuration ################################################*/
 pNodeConfig.NodeType = DMA_GPDMA_LINEAR_NODE;
 pNodeConfig.Init.Request = DMA_REQUEST_SW;
 pNodeConfig.Init.BlkHWRequest = DMA_BREQ_SINGLE_BURST;
 pNodeConfig.Init.Direction = DMA_MEMORY_TO_MEMORY;
 pNodeConfig.Init.SrcInc = DMA_SINC_INCREMENTED;
 pNodeConfig.Init.DestInc = DMA_DINC_FIXED;
 pNodeConfig.Init.SrcDataWidth = DMA_SRC_DATAWIDTH_WORD;
 pNodeConfig.Init.DestDataWidth = DMA_DEST_DATAWIDTH_WORD;
 pNodeConfig.Init.SrcBurstLength = 1;
 pNodeConfig.Init.DestBurstLength = 1;
 pNodeConfig.Init.TransferAllocatedPort = DMA_SRC_ALLOCATED_PORT1|DMA_DEST_ALLOCATED_PORT0;
 pNodeConfig.Init.TransferEventMode = DMA_TCEM_BLOCK_TRANSFER;
 pNodeConfig.TriggerConfig.TriggerPolarity = DMA_TRIG_POLARITY_MASKED;
 pNodeConfig.DataHandlingConfig.DataExchange = DMA_EXCHANGE_NONE;
 pNodeConfig.DataHandlingConfig.DataAlignment = DMA_DATA_RIGHTALIGN_ZEROPADDED;
 pNodeConfig.SrcAddress = 0x08000000;
 pNodeConfig.DstAddress = CRC_BASE;
 pNodeConfig.DataSize = 0xFFFC;

 /* Build Block1 Node */
 ret |= HAL_DMAEx_List_BuildNode(&pNodeConfig, &Block1);

 /* Insert Block1 to Queue */
 ret |= HAL_DMAEx_List_InsertNode_Tail(&FlashMemory, &Block1);

 /* Set node configuration ################################################*/
 pNodeConfig.SrcAddress = 0x0800FFFC;

 /* Build Block2 Node */
 ret |= HAL_DMAEx_List_BuildNode(&pNodeConfig, &Block2);

 /* Insert Block2 to Queue */
 ret |= HAL_DMAEx_List_InsertNode_Tail(&FlashMemory, &Block2);

 return ret;
}

And in the code I started the transfer as follows:

MX_FlashMemory_Config();
HAL_DMAEx_List_LinkQ(&handle_GPDMA1_Channel12, &FlashMemory); 
__HAL_CRC_DR_RESET(&hcrc);
HAL_DMAEx_List_Start_IT(&handle_GPDMA1_Channel12);

This works if the IAR project calculates a checksum from 0x08000000 to 0x0801FFF7. I hope this helps anyone trying to run a checksum on the STM32U5 line.

waclawek.jan
Super User
May 21, 2026

Thanks for coming back with the detailed solution.

This may be helpful not just in 'U5, but also all newer families, which as DMA have the GPDMA (and presumably also LPDMA?) modules.

JW

waclawek.jan
Super User
May 15, 2026

Oh, indeed - if a CRC calculator is fed by one byte only (0x80), the result is 0x6800520C as reported.

A nasty gotcha, changing the Cube/HAL's API between families! I see that this matches GPDMA's BNDT behaviour but still - Cube/HAL is supposed to be there to facilitate porting, which it fails here obviously.

Nonetheless, in this case, the DMA has been set to transfer words at both ports, wasn't it. And the RM says this:

waclawekjan_0-1778849682852.png

so GPDMA shouldn't have transferred even that one single byte, but should've thrown the user setting error, which doesn't appear to have happened.

@Khaled_DHIF, can you please comment?

Thanks,

JW