2024-06-03 01:46 AM
Hello,
I have a project on STM32H750 where the code runs from external qspi flash in memory mapped mode. I need to write the flash occasionally and for that purpose I have some functions in RAM which disable interrupts, disable caching, abort memory mapped mode, do the actual write and then reenable all again.
The described method is working well for me until now when I need DMA for my UART transfers. I found that the DMA somehow collides with my flash writing ending in hardfault.
I don't know why but I suppose that I have to pause the DMA during the flash write process (IRQs disabled?). Can anyone give me an advice what's the correct way to do that? I think I remember I already read something on this topic. some appnote or forum thread, but I cannot find it anymore.
best regards
Jan
2024-06-04 08:20 AM
Hello @JanPohanka and welcome to the Community
Please wait for the end of the transfer before abort the memory mapped mode.
Also could you please try to pause or disable the DMA transfers before starting the flash write process and then resume them afterward.
Could you please take a look at these applications notes may help you.
- Quad-SPI interface on STM32 microcontrollers and microprocessors - Application note
Thank you.
Kaouthar
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.
2024-06-04 08:35 AM
HAL_QSPI_Abort(&hqspi)
For DMA be aware of cache coherency. Use the ByAddr methods DCache Clean flushes the cache, Invalidate trashes it, loses pending writes, dangerous if done blindly. Cache lines a 32-byte sided/aligned.
Ensure the QSPI Write Page operation completes
2024-06-05 12:09 AM
Thank you for answer.
> HAL_QSPI_Abort(&hqspi)
I know this :), I just don't know how to correctly handle the collision with DMA.
I have the following logic now, which works well, until I started to need DMA enabled UART. I'd like to solve it in some general way if possible.
static void __ramfunc qspi_abort()
{
SET_BIT(QUADSPI->CR, QUADSPI_CR_ABORT);
while ((QUADSPI->SR & QSPI_FLAG_TC) == 0) ; // TODO timeout
QUADSPI->FCR |= QSPI_FLAG_TC;
while ((QUADSPI->SR & QSPI_FLAG_BUSY) != 0) ; // TODO timeout
}
#define RAM_TRAMPOLINE_DISABLE_CACHE
static uint32_t __qspi_abort_mm(struct flash_qspi_dev_data *devdata)
{
__disable_irq();
#if defined(RAM_TRAMPOLINE_DISABLE_CACHE)
SCB_DisableDCache();
SCB_DisableICache();
#endif
uint32_t ccr_backup = QUADSPI->CCR;
qspi_abort();
devdata->memory_mapped = false;
devdata->busy = 0;
return ccr_backup;
}
static void __qspi_start_mm(struct flash_qspi_dev_data *devdata, bool restore, uint32_t ccr_backup)
{
if (devdata->busy != 0)
qspi_abort();
devdata->busy = 1;
// enable memory mapped again
while ((QUADSPI->SR & QSPI_FLAG_BUSY) != 0) ; // TODO timeout
QUADSPI->CCR = ccr_backup;
#if defined(RAM_TRAMPOLINE_DISABLE_CACHE)
SCB_EnableICache();
SCB_EnableDCache();
#endif
__enable_irq();
devdata->memory_mapped = true;
}
__ramfunc static int flash_qspi_write(struct flash_qspi_dev_data *devdata, off_t offset,
const void *data, size_t size)
{
uint32_t ccr_backup;
ccr_backup = __qspi_abort_mm(devdata);
while (size > 0) {
...
qspi_cmd(QUAD_PROGRAM_CMD, QUADSPI_CCR_DMODE, ws, QUADSPI_CCR_ADMODE_0,
QUADSPI_CCR_ADSIZE_1, offset, 0);
...
}
__qspi_start_mm(devdata, true, ccr_backup);
return 0;
}