2023-04-24 05:31 AM - edited 2023-11-20 07:19 AM
I am using the DMA2D to transfer colour data to an external SRAM using the FMC with an 8-bit parallel hardware setup. I can use the following line of code on the CM7 CPU to write to any in-range address (0x68000000 base) with any pointer type (uint8_t, uint16_t, uint32_t, uint64_t) with no issues:
uint16_t * const externalRAM = (uint16_t*)0x68000000;
externalRAM[INDEX] = 0xFFFF;
The logic analyser output of a write with INDEX set to 2U (No issues):
To produce no issues when writing with the CPU, the MPU has been used to set up the external SRAM buffer region as non-cacheable (see config below). If this is NOT done, the FMC will write to a full 64-bit region even if a single-byte write attempt is made. The remaining bytes will be corrupted (suspected cache related).
MPU_Region_InitTypeDef MPU_InitStruct = {0};
/* Disables the MPU */
HAL_MPU_Disable();
/** Initializes and configures the Region and the memory to be protected
*/
MPU_InitStruct.Enable = MPU_REGION_ENABLE;
MPU_InitStruct.Number = MPU_REGION_NUMBER0;
MPU_InitStruct.BaseAddress = 0x68000000;
MPU_InitStruct.Size = MPU_REGION_SIZE_512KB;
MPU_InitStruct.SubRegionDisable = 0x0;
MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL0;
MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_DISABLE;
MPU_InitStruct.IsShareable = MPU_ACCESS_SHAREABLE;
MPU_InitStruct.IsCacheable = MPU_ACCESS_NOT_CACHEABLE;
MPU_InitStruct.IsBufferable = MPU_ACCESS_BUFFERABLE;
HAL_MPU_ConfigRegion(&MPU_InitStruct);
/* Enables the MPU */
HAL_MPU_Enable(MPU_HFNMI_PRIVDEF);
When using the DMA2D to write to the external buffer (with the MPU configured correctly), if the start or end addresses of a transaction don't align with a 64-bit boundary, the remaining bytes in that 64-bit region will be corrupted. This does not occur if the internal memory is used for the target buffer. See the following logic analyser output when starting a DMA2D transfer with the target address set to 0x68000002 and colour data set to 0xFFFF. The FMC is writing garbage data (0x00) to the first 2 bytes of the 64-bit region. If the DMA2D target address was set to 0x68000003, the first 3 bytes would become corrupted.
I have tried enabling DCache and manually clearing the cache before the start of each DMA2D transfer, but this has had no affect:
const uint32_t colourData = 0xFFFFFFFF;
const uint32_t baseAddress = 0x68000000;
volatile uint32_t bufferOffset = 0x02;
const uint32_t width = 40U;
const uint32_t height = 1U;
SCB_CleanDCache_by_Addr((uint32_t*)0x68000000, 261120);
__DSB();
if (HAL_DMA2D_Start(&_hdma2d, colourData, (baseAddress + bufferOffset), width, height) == HAL_OK)
{
HAL_DMA2D_PollForTransfer(&_hdma2d, 10);
}
__DSB();
SCB_InvalidateDCache_by_Addr((uint32_t*)0x68000000, 261120);
I have tried slowing down the FMC timings, also to no effect. Manual write/read tests from the CM7 CPU pass for the full external buffer range. See FMC setup:
FunctionStatus_t FMC_Init(void)
{
FunctionStatus_t statusReturn = FUNCTION_OK;
FMC_NORSRAM_TimingTypeDef Timing = {0};
/** Perform the SRAM1 memory initialization sequence
*/
hsram1.Instance = FMC_NORSRAM_DEVICE;
hsram1.Extended = FMC_NORSRAM_EXTENDED_DEVICE;
/* hsram1.Init */
hsram1.Init.NSBank = FMC_NORSRAM_BANK3;
hsram1.Init.DataAddressMux = FMC_DATA_ADDRESS_MUX_DISABLE;
hsram1.Init.MemoryType = FMC_MEMORY_TYPE_SRAM;
hsram1.Init.MemoryDataWidth = FMC_NORSRAM_MEM_BUS_WIDTH_8;
hsram1.Init.BurstAccessMode = FMC_BURST_ACCESS_MODE_DISABLE;
hsram1.Init.WaitSignalPolarity = FMC_WAIT_SIGNAL_POLARITY_LOW;
hsram1.Init.WaitSignalActive = FMC_WAIT_TIMING_BEFORE_WS;
hsram1.Init.WriteOperation = FMC_WRITE_OPERATION_ENABLE;
hsram1.Init.WaitSignal = FMC_WAIT_SIGNAL_DISABLE;
hsram1.Init.ExtendedMode = FMC_EXTENDED_MODE_DISABLE;
hsram1.Init.AsynchronousWait = FMC_ASYNCHRONOUS_WAIT_DISABLE;
hsram1.Init.WriteBurst = FMC_WRITE_BURST_DISABLE;
hsram1.Init.ContinuousClock = FMC_CONTINUOUS_CLOCK_SYNC_ONLY;
hsram1.Init.WriteFifo = FMC_WRITE_FIFO_DISABLE;
hsram1.Init.PageSize = FMC_PAGE_SIZE_NONE;
/* Bus Timing Configuration */
Timing.AddressSetupTime = 1;
Timing.AddressHoldTime = 0; /* No Impact */
Timing.DataSetupTime = 5;
Timing.BusTurnAroundDuration = 0; /* No Impact */
Timing.CLKDivision = 0; /* No Impact */
Timing.DataLatency = 0; /* No Impact */
Timing.AccessMode = FMC_ACCESS_MODE_A;
/* Init */
if (HAL_SRAM_Init(&hsram1, &Timing, NULL) != HAL_OK)
{
statusReturn = FUNCTION_ERROR;
}
return (statusReturn);
}
I have tried using the following line to swap the external buffer base address to 0xC8000000 with no change in corrupted output after DMA2D transfer:
HAL_SetFMCMemorySwappingConfig(FMC_SWAPBMAP_SDRAM_SRAM);
DMA2D setup for reference:
_hdma2d.Instance = DMA2D;
_hdma2d.Init.Mode = DMA2D_R2M;
_hdma2d.Init.ColorMode = DMA2D_OUTPUT_RGB565;
_hdma2d.Init.OutputOffset = 0;
HAL_NVIC_SetPriority(DMA2D_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(DMA2D_IRQn);
if (HAL_DMA2D_Init(&_hdma2d) != HAL_OK)
{
Error_Handler();
}
External SRAM MPN:
RMLV0408EGSP-4S2#CA1
Any guidance/advice is greatly appreciated.
2023-04-25 04:16 AM
I could adjust any line/rectangle draw functions to manually write the bytes that are not 64-bit aligned and then use the DMA2D for the remainder. However, the issue occurs whenever a new line starts/ends on a DMA2D rectangle draw before/after the offset from OOR is applied - making the rectangle draw functionality useless if the draw starts or ends on a non-64-bit aligned address.
Would it be possible to get someone from ST to confirm whether this behavior is expected when using the DMA2D + FMC + External SRAM with 8-bit hardware?
It feels like there is a low-level interoperability issue between the DMA2D and External SRAM but it would be great to be proven wrong. Thanks.