I'm playing with the L1 cache of STM32H753.
What I am trying to do is to provoke — on purpose — an inconsistency between the cache and the RAM as follows:
Here is the code:
volatile uint32_t someDummyVariable ;
int main(void)
{
MPU_Region_InitTypeDef MPU_InitStruct;
HAL_Init();
/* Configure the MPU attributes as Write-through for SRAM */
HAL_MPU_Disable();
MPU_InitStruct.Enable = MPU_REGION_ENABLE;
MPU_InitStruct.BaseAddress = 0x20000000;
MPU_InitStruct.Size = MPU_REGION_SIZE_32MB;
MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
MPU_InitStruct.IsBufferable = MPU_ACCESS_NOT_BUFFERABLE; // -> means write through ?
MPU_InitStruct.IsCacheable = MPU_ACCESS_CACHEABLE;
MPU_InitStruct.IsShareable = MPU_ACCESS_NOT_SHAREABLE;
MPU_InitStruct.Number = MPU_REGION_NUMBER0;
MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL0;
MPU_InitStruct.SubRegionDisable = 0x00;
MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_ENABLE;
HAL_MPU_ConfigRegion(&MPU_InitStruct);
/* Configure the MPU attributes as WT for the Flash */
MPU_InitStruct.Enable = MPU_REGION_ENABLE;
MPU_InitStruct.BaseAddress = 0x08000000;
MPU_InitStruct.Size = MPU_REGION_SIZE_16MB;
MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
MPU_InitStruct.IsBufferable = MPU_ACCESS_NOT_BUFFERABLE;
MPU_InitStruct.IsCacheable = MPU_ACCESS_CACHEABLE;
MPU_InitStruct.IsShareable = MPU_ACCESS_NOT_SHAREABLE;
MPU_InitStruct.Number = MPU_REGION_NUMBER1;
MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL0;
MPU_InitStruct.SubRegionDisable = 0x00;
MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_ENABLE;
HAL_MPU_ConfigRegion(&MPU_InitStruct);
HAL_MPU_Enable(MPU_HARDFAULT_NMI);
SCB_EnableDCache();
// write something in a variable in RAM -> thanks to write-through attribute
// it will be copied to real RAM, not only to the cache
someDummyVariable = 0x12345678;
// disable cache without invalidating it
SCB->CSSELR = 0U; /* select Level 1 data cache */
__DSB();
SCB->CCR &= ~(uint32_t)SCB_CCR_DC_Msk; /* disable D-Cache */
__DSB();
__ISB();
// write something else to RAM -> will NOT be written to cache
someDummyVariable = 0xAAAAAAAA;
// enable cache again (without invalidating or cleaning it)
__DSB();
SCB->CCR |= (uint32_t)SCB_CCR_DC_Msk; /* enable D-Cache */
__DSB();
__ISB();
// now we should read the old value that is still in the cache
if ( someDummyVariable != 0x12345678 )
{
__NOP();
}
Compiled with Keil 5, -O0
. The variable write is done through a STR
instruction (I mean: no weird CPU optimisation, as far as I understood).
I checked the MPU registers values, the address of the RAM variable (indeed inside the MPU region).
Is there something wrong in the algorithm and/or in the code?
You're trying to enable/disable the cache for the DTCMRAM, which starts at address 0x200000000. However, the DTCMRAM is tightly coupled memory. It is directly connected to the Cortex-M7 core and is not behind the cache. You can see this in Figure 1. (System architecture) of the (Reference Manual). Consequently, cache operations do not have effect on this memory.
If you want to do this test, you would have to use another memory region, for example the AXI SRAM (starting at address 0x24000000). This can be done by modifying your linker script (and possibly the startup code), or by directly writing to a fixed memory address using a pointer variable.
Note that another possibility to create a mismatch between RAM and cache, without disabling the cache, is by using a DMA controller to access the same memory as the MCU. The DMA controllers do not use the cache, so if a DMA controller writes to the memory that is already in cache, your application would read the old cached version instead of the newly written data. Your test would then be something like this: