2025-12-04 4:47 AM - edited 2026-05-06 1:02 AM
Backup SRAM or normal SRAM content is not preserved after reset or wake up from standby. After a reset, the last few bytes written are not kept.
This issue appears on STM32 devices that have ECC (Error Code Correction) on internal SRAM. This article focuses on STM32H7, but a similar approach can be applied to other STM32 series such as STM32H5 and STM32N6.
On STM32H7, the ECC is computed for each word of SRAM (64-bit for AXI SRAM, 32-bit for other SRAMs, including backup SRAM).
All "word-aligned" write accesses are directly written to the SRAM. Word-aligned access is when the whole SRAM word is written to an aligned address:
All other access ("word-unaligned") ends in one word cache, which is part of SRAM. This cache is used to optimize ECC computation by waiting for the next data, which could be part of the same SRAM word. Otherwise, the SRAM must perform a read-modify-write operation to have the correct ECC for the whole SRAM word.
To flush this cache after "word-unaligned" access, there are two options:
Performing aligned write access to different part of SRAM does not flush the cache.
The code for flushing the ECC can look like this:
void FlushECC(void *ptr, int bytes){
uint32_t addr = (uint32_t)ptr;
/* Check if accessing AXI SRAM => 64-bit words*/
if(addr >= 0x24000000 && addr < 0x24080000){
volatile uint64_t temp;
volatile uint64_t* flush_ptr = (uint64_t*) (addr & 0xFFFFFFF8);
uint64_t *end_ptr = (uint64_t*) ((addr+bytes+7) & 0xFFFFFFF8);
do{
temp = *flush_ptr;
*flush_ptr = temp;
flush_ptr++;
}while(flush_ptr != end_ptr);
}
/* Otherwise 32-bit words */
else {
volatile uint32_t temp;
volatile uint32_t* flush_ptr = (uint32_t*) (addr & 0xFFFFFFFC);
uint32_t *end_ptr = (uint32_t*) ((addr+bytes+3) & 0xFFFFFFFC);
do{
temp = *flush_ptr;
*flush_ptr = temp;
flush_ptr++;
}while(flush_ptr != end_ptr);
}
}
This can be useful, for example, flushing the whole C structure, when we are not sure which variable was written last.
Note that this issue only affects data retention after reset or exit from standby mode (which acts as reset). It does not affect coherency between core and DMA, since this one word cache is a part of SRAM.
First published on Oct 10, 2020
I would suggest to optimize do-while loop.
If the pointed variable is larger as 64-bit (in case of AXI) then part of data is already 64-bit chunk and writen thru. Only less than 64-bit long chunk of start and tail of data block must be additionaly processed by mentioned FlushECC funtion, like that fictional structure of sizeof()==26 bytes :
| __ | __ | __ | FF | FA | FA | EE | F0 | ==> flush it
| 91 | A3 | 44 | FF | FA | FA | EE | 00 |
| 96 | A3 | 44 | FF | FA | FA | EE | 78 |
| FF | FA | FA | EE | F0 | __ | __ | __ | ==> flush it
The same would apply to 32-bit ECC area. Am I wrong?
Edited to apply 'Preformatted' format so that the table layout works. See here for details.
Hello @DrDro ,
the example code I provided should work in all cases. In some applications the RAM cell that needs the flush (last unaligned access) can be in the middle of the memory range, because the data are written at "random". The application might not know where exactly this is, since it doesn't track the memory accesses.
Of course, in specific use cases, this can be optimized.
There is still an issue in the code. Here the end_ptr is not written in the do-while code!
do{
temp = *flush_ptr;
*flush_ptr = temp;
flush_ptr++;
}while(flush_ptr != end_ptr);should looks like:
do{
temp = *flush_ptr;
*flush_ptr = temp;
flush_ptr++;
}while(flush_ptr > end_ptr);to write also the chunk including end_ptr.
Hello @DrDro ,
you are right that there is a flaw in the code. Thank you for point to that.
The end_ptr should point to first address after the flush block. So, the end_ptr itself shouldn't be written. However, the alignment will shrink it, if the ptr+bytes address isn't aligned. So, the proper calculation of end_ptr should be:
/* 64-bit align */
uint64_t *end_ptr = (uint64_t*) ((addr+bytes+7) & 0xFFFFFFF8);
/* 32-bit align */
uint32_t *end_ptr = (uint32_t*) ((addr+bytes+3) & 0xFFFFFFFC);
I will modify the article accordingly.