Skip to main content
Associate II
September 10, 2026
Question

STM32H7RS UART RX with circular linked-list GPDMA: DTE Error resolved by adding __DSB() before ENABLE

Hi everyone,

I'm using UART Receive-to-IDLE with circular linked-list GPDMA on an STM32H7RS and ran into a problem where reception stopped with HAL_DMA_ERROR_DTE.

The DMA node and RX buffer are both in DMA-accessible, non-cacheable RAM and aligned to 32 bytes.

The DMA handle and queue structure are in normal BSS. Everything is global/static and zero-initialized.

I checked the queue/node connection and the source address in LinkRegisters[3], which correctly pointed to UART RDR. The node address reconstructed from CLBAR and CLLR was also correct when checked.

However, these were the actual DMA register values in the working and failing cases:

         Working       Failing
CBR1 0x00000080 0x0000049B
CSAR 0x40007824 0x00000005
CDAR 0x24001960 0x200065A0

After the DMA error, UART error handling stopped reception and disabled the IDLE interrupt, so I stopped getting HAL_UARTEx_RxEventCallback() calls.

What fixed it was adding __DSB() inside HAL_DMAEx_List_Start_IT(), after the CLBAR/CLLR configuration and immediately before ENABLE:

__DSB();
__HAL_DMA_ENABLE(hdma);

So far, the issue has not returned.

My understanding is that the UART HAL updates the node before calling this function. With the initial transfer count at zero and a valid link, the DMA then fetches the node and loads its transfer registers in hardware after ENABLE. The barrier ensures the earlier CPU writes have completed before that happens, even though they were made in the calling function.

This looks like a memory synchronization issue, but I haven't established exactly where those incorrect register values came from.

Is this barrier expected here, and should it already be part of the HAL? Are there other DMA start/restart paths or descriptor updates where I should add one?

I'm also wondering about peripherals with their own DMA engines, particularly Ethernet and SDMMC DMA. Do their HAL drivers already handle the required synchronization, or are there points where the application needs to add barriers too?

4 replies

ST Employee
October 2, 2026

Hello ​@Huell,

Could you share the STM32H7RS part number and the HAL driver version you are using?

 

In order to give better visibility on the answered topics, please click on 'Best answer' on the reply which solved your issue or answered your question. Dor_RH
HuellAuthor
Associate II
October 2, 2026

Hello ​@Dor_RH,

 

Part number is stm32h7s3l8h6

and the HAL driver version is v1.3.0

ST Employee
October 6, 2026

Hello ​@Huell,

Could you please share the MPU configuration of your application?

Also, have you had a look at UART_TwoBoards_ComDMAlinkedlist? It is a relevant reference and should be useful for your application.

In order to give better visibility on the answered topics, please click on 'Best answer' on the reply which solved your issue or answered your question. Dor_RH
HuellAuthor
Associate II
October 14, 2026

Hello ​@Dor_RH,

 

MPU configuration of our application is given as below:

void MPU_Init(void)
{
MPU_Region_InitTypeDef MPU_InitStruct = {0};
uint32_t mpu_region = MPU_REGION_NUMBER0;

HAL_MPU_Disable();

// Initialize the background region
MPU_InitStruct.Enable = MPU_REGION_ENABLE;
MPU_InitStruct.Number = mpu_region;
MPU_InitStruct.BaseAddress = 0x0;
MPU_InitStruct.Size = MPU_REGION_SIZE_4GB;
MPU_InitStruct.SubRegionDisable = 0x87;
MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL0;
MPU_InitStruct.AccessPermission = MPU_REGION_NO_ACCESS;
MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_DISABLE;
MPU_InitStruct.IsShareable = MPU_ACCESS_SHAREABLE;
MPU_InitStruct.IsCacheable = MPU_ACCESS_CACHEABLE;
MPU_InitStruct.IsBufferable = MPU_ACCESS_BUFFERABLE;
HAL_MPU_ConfigRegion(&MPU_InitStruct);
mpu_debug("Region:%d, BaseAddress:0x%08X, Size:%5d GB (Background)", mpu_region, MPU_InitStruct.BaseAddress, 4);
mpu_region++;

MPU_InitStruct.Enable = MPU_REGION_ENABLE;
MPU_InitStruct.Number = mpu_region;
MPU_InitStruct.SubRegionDisable = 0;
MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL1;
MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_ENABLE;
MPU_InitStruct.IsShareable = MPU_ACCESS_NOT_SHAREABLE;
MPU_InitStruct.IsCacheable = MPU_ACCESS_CACHEABLE;
MPU_InitStruct.IsBufferable = MPU_ACCESS_BUFFERABLE;
MPU_AdjustRegionAddressSize(EXTERNAL_FLASH_ADDRESS, EXTERNAL_FLASH_SIZE, &MPU_InitStruct);
HAL_MPU_ConfigRegion(&MPU_InitStruct);
mpu_debug("Region:%d, BaseAddress:0x%08X, Size:%5d MB (Flash)", mpu_region, MPU_InitStruct.BaseAddress, EXTERNAL_FLASH_SIZE / (1024 * 1024));
mpu_region++;

if (DMA_RAM_AREA_SIZE != 0)
{
MPU_InitStruct.Enable = MPU_REGION_ENABLE;
MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
MPU_InitStruct.IsBufferable = MPU_ACCESS_NOT_BUFFERABLE;
MPU_InitStruct.IsCacheable = MPU_ACCESS_NOT_CACHEABLE;
MPU_InitStruct.IsShareable = MPU_ACCESS_NOT_SHAREABLE;
MPU_InitStruct.Number = mpu_region;
MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL1;
MPU_InitStruct.SubRegionDisable = 0x00;
MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_DISABLE;
MPU_AdjustRegionAddressSize(DMA_RAM_AREA_BASE, DMA_RAM_AREA_SIZE, &MPU_InitStruct);
mpu_debug("Region:%d, BaseAddress:0x%08X, Size:%5d KB (DMA Data)", mpu_region, MPU_InitStruct.BaseAddress, DMA_RAM_AREA_SIZE / 1024);
HAL_MPU_ConfigRegion(&MPU_InitStruct);
mpu_region++;
}

#ifdef CONFIG_PSRAM
MPU_InitStruct.Enable = MPU_REGION_ENABLE;
MPU_InitStruct.Number = mpu_region;
MPU_InitStruct.SubRegionDisable = 0;
MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL1;
MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_DISABLE;
MPU_InitStruct.IsShareable = MPU_ACCESS_NOT_SHAREABLE;
MPU_InitStruct.IsCacheable = MPU_ACCESS_CACHEABLE;
MPU_InitStruct.IsBufferable = MPU_ACCESS_BUFFERABLE;
MPU_AdjustRegionAddressSize(EXTERNAL_PSRAM_ADDRESS, EXTERNAL_PSRAM_SIZE, &MPU_InitStruct);
mpu_debug("Region:%d, BaseAddress:0x%08X, Size:%5d MB (PSRAM)", mpu_region, MPU_InitStruct.BaseAddress, EXTERNAL_PSRAM_SIZE / (1024 * 1024));
HAL_MPU_ConfigRegion(&MPU_InitStruct);
mpu_region++;
#endif

// Errata GFXMMU Cache Line Fill Deadlock
MPU_InitStruct.Enable = MPU_REGION_ENABLE;
MPU_InitStruct.Number = mpu_region;
MPU_InitStruct.BaseAddress = 0x25000000;
MPU_InitStruct.Size = MPU_REGION_SIZE_16MB;
MPU_InitStruct.SubRegionDisable = 0x00;
MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL0;
MPU_InitStruct.AccessPermission = MPU_REGION_NO_ACCESS;
MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_DISABLE;
MPU_InitStruct.IsShareable = MPU_ACCESS_NOT_SHAREABLE;
MPU_InitStruct.IsCacheable = MPU_ACCESS_NOT_CACHEABLE;
MPU_InitStruct.IsBufferable = MPU_ACCESS_NOT_BUFFERABLE;
HAL_MPU_ConfigRegion(&MPU_InitStruct);
mpu_debug("Region:%d, BaseAddress:0x%08X, Size:%5d MB (GFXMMU-Errata)", mpu_region, MPU_InitStruct.BaseAddress, 16);
mpu_region++;

// Code and RO DATA | Cache Policy: Non-cacheable, T=1, B=0, C=0, S=0 AN4838
MPU_InitStruct.Enable = MPU_REGION_ENABLE;
MPU_InitStruct.Number = mpu_region;
MPU_AdjustRegionAddressSize(INTERNAL_ITCM_BASE, INTERNAL_ITCM_SIZE, &MPU_InitStruct);
MPU_InitStruct.SubRegionDisable = 0x00;
MPU_InitStruct.AccessPermission = MPU_REGION_PRIV_RO;
MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_ENABLE;
MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL1;
MPU_InitStruct.IsShareable = MPU_ACCESS_NOT_SHAREABLE;
MPU_InitStruct.IsCacheable = MPU_ACCESS_NOT_CACHEABLE;
MPU_InitStruct.IsBufferable = MPU_ACCESS_NOT_BUFFERABLE;
HAL_MPU_ConfigRegion(&MPU_InitStruct);
mpu_debug("Region:%d, BaseAddress:0x%08X, Size:%5d KB (ITCM Code/RO)", mpu_region, MPU_InitStruct.BaseAddress, INTERNAL_ITCM_SIZE / 1024);
mpu_region++;

for (; mpu_region < __MPU_REGIONCOUNT; mpu_region++)
{
MPU_InitStruct.Enable = MPU_REGION_DISABLE;
MPU_InitStruct.Number = mpu_region;
HAL_MPU_ConfigRegion(&MPU_InitStruct);
}

HAL_MPU_Enable(MPU_PRIVILEGED_DEFAULT);

}

Yes, we have checked the relevant references. It seems only thing that appears to be working reliably is DSB solution. So far, none of the other approaches we have tried have resolved the issue.