cpu/stm32: make backup SRAM available#16870
Conversation
benpicco
left a comment
There was a problem hiding this comment.
If I just reflash the board, the following warning occures and the counter is not reset. I am not sure if it should be. Maybe someone has an idea.
I see the same behavior on SAM D5x/E5x. The CPU will not enter deep sleep right after it has been flashed. (That's why I added the warning)
I have no idea what causes this and since the issue is not relevant in production, I never did dig any deeper.
cpu/cortexm_common/vectors_cortexm.c
Outdated
|
|
||
| #ifdef CPU_HAS_BACKUP_RAM | ||
| #if BACKUP_RAM_HAS_INIT | ||
| backup_ram_init(); |
There was a problem hiding this comment.
No need for the indent
| backup_ram_init(); | |
| backup_ram_init(); |
cpu/stm32/periph/backup_ram.c
Outdated
| } | ||
|
|
||
| void backup_ram_regulator_on(void) | ||
| { |
There was a problem hiding this comment.
We probably want a
if (!IS_USED(MODULE_PERIPH_BACKUP_RAM)) {
return;
}here so we don't increase standby current if backup ram is not used
|
I have tried the VBAT mode. I have removed a solder bridge (156) from my |
cpu/stm32/include/periph_cpu.h
Outdated
| #if IS_USED(MODULE_PERIPH_BACKUP_RAM) | ||
| bool cpu_woke_from_backup(void); | ||
| #else | ||
| static inline bool cpu_woke_from_backup(void) {return false;} |
There was a problem hiding this comment.
Couldn't that function still be useful when backup ram is not used?
We could move the implementation to cpu_common.c instead of backup_ram.c
There was a problem hiding this comment.
I skimmed over the cmsis-header-stm32 repository.
I guess a function for all stm32 would be more complicated. Some don´t have RCC_CSR_PORRSTF. Some don´t have RCC_CSR_BORRSTF. And stm32h7 doesn´t have reset flags at all.
I would rename it to backup_ram_is_retained() because the use case of this function is to check exactly that.
There was a problem hiding this comment.
or backup_ram_was_retained() because it is called after reset
cpu/stm32/periph/backup_ram.c
Outdated
| if (_backup_battery_connected() && backup_ram_regulator_is_on()) { | ||
| /* if regulator is on and a battery is connected after reset, | ||
| backup ram should have been retained */ | ||
| return true; |
There was a problem hiding this comment.
Maybe that function name was a bad idea since originally it should just return if we did a cold boot or woke from Deep Sleep, but you are right that for this use case we also should know if the backup battery was connected.
But shouldn't this then return false if the backup battery is not connected()?
Then again if no backup battery exists we can't use the backup RAM and there is little the software can do about it, so we can just ignore it. Or are there cases where backup RAM retention works without backup battery?
There was a problem hiding this comment.
The battery is used to retain backup ram when there is no power supply, E.g. someone removed the USB cable. If there is no battery and the board entered standby mode while the power supply was not interrupted, the backup ram is retained even without battery.
There was a problem hiding this comment.
Or maybe rewrite like this?:
if (backup_ram_regulator_is_on()) {
if (_backup_battery_connected()) {
/* if regulator is on and a battery is connected after reset,
backup ram should have been retained */
return true;
}
uint32_t coldboot = (RCC->CSR & (RCC_CSR_PORRSTF | RCC_CSR_BORRSTF));
RCC->CSR |= RCC_CSR_RMVF; /* clear reset flags */
return !(coldboot == (RCC_CSR_PORRSTF | RCC_CSR_BORRSTF));
}
return false;There was a problem hiding this comment.
So backup_ram_regulator_is_on() is only true if we did not cold boot? Than that version looks more correct to me!
But then we never clear reset flags if the battery is connected? Why is this early return needed?
There was a problem hiding this comment.
So backup_ram_regulator_is_on() is only true if we did not cold boot?
If we enter the first if and skip the second if, coldboot can still be true or false, so no.
What I did not mention so far is a note in the manual regarding the BRE and BRR bits (the regulator).
Note: This bit is not reset when the device wakes up from Standby mode, by a system reset,
or by a power reset. [RM0410 p. 146]
I try to formulate my thoughts more verbose.
This function answers the question: Does the backup ram contain valid data (true) or can it be overridden.
We are at the point where the function is called, the reset handler, so right after reset.
A necessary condition for the backup ram to be valid after reset is that the regulator must have been on.
So if that is not true, it returns false.
If the regulator was on and we have a battery, then backup ram survived even cold boots, so that is not tested further and it returns true.
Else backup ram survived only soft resets where the power supply was not interrupted.
By the time of writing I notice two things.
backup_ram_init()possibly (re)enables the regulator, thus checking whether the regulator was on is meaningless. 😯- No other function after that one will be able to check the reset flags because they are cleared immediately. A good place to clear reset flags would be at the and of
reset_handler_default(), I guess.
But this is architecture dependent. Could this be done in anstm32::post_startup(), or a newreset_done()at the very end?
void backup_ram_init(void)
{
/* see reference manual 4.1.5 "Battery backup domain" */
periph_clk_en(APB1, RCC_APB1ENR_PWREN);
stmclk_dbp_unlock();
periph_clk_en(AHB1, RCC_AHB1ENR_BKPSRAMEN);
#if defined(VBAT_ADC) && IS_USED(MODULE_PERIPH_ADC)
adc_init(VBAT_ADC);
#endif
}
bool cpu_woke_from_backup(void) {
bool regon = backup_ram_regulator_is_on();
if (_backup_battery_connected()) {
if (regon) {
/* if regulator is on and a battery is connected after reset,
backup ram should have been retained */
return true;
}
/* in case the board has a backup battery the regulator must be on
to mitigate (unexpected) outage of VDD, so RTC register and
backup domain register contents are not lost */
backup_ram_regulator_on();
return false;
}
uint32_t coldboot = (RCC->CSR & (RCC_CSR_PORRSTF | RCC_CSR_BORRSTF));
RCC->CSR |= RCC_CSR_RMVF; /* clear reset flags somewhere else */
return regon && !(coldboot == (RCC_CSR_PORRSTF | RCC_CSR_BORRSTF));
}There was a problem hiding this comment.
I am sorry for the confusion. I think now the battery + regulator thing is clear. Of course I am open for change requests as long as it works out.
There was a problem hiding this comment.
those code snippets above don´t work 😔.
But the current code works as before.
|
Those fixups are getting messy. I would like to squash |
|
As a reference of how to calculate the voltage value from the ADC sample I found this |
cpu/stm32/periph/backup_ram.c
Outdated
| /* if the board does not expose a VBAT pin or ADC is not implemented, | ||
| you can define BOARD_HAS_BACKUP_BATTERY and promise that | ||
| you have correctly soldered a working battery, | ||
| else backup ram content is undefined */ |
There was a problem hiding this comment.
How about we put a magic string in backup RAM to check if the memory was retained? To me that appears like a simpler solution than pulling in the ADC peripheral for that. (measuring Vbat is still valuable, but I'd prefer to have that as a separate PR and independent of backup RAM)
lpc23xx had a similar issue.
There was a problem hiding this comment.
It was working too well. I wondered why the backup ram was retained when battery was on but the regulator was off. Then I figured out that backup_ram_regulator_off() was never called here .
So a better place to switch off the regulator would be in cpu_woke_from_backup().
1fed5d0 to
a7cff8c
Compare
Sorry to jump in late but doesn't it makes sense to consider backup RAM as a peripheral ? IMHO, it doesn't and the initial name was fine. From the build system point of view, it's fine to have it in |
|
To me it seems a bit inconsistent to not have it as a
What I think speaks against it are:
I admit that this PR has drifted away from the stm32 topic a bit. |
|
It's of course bike shedding, but I think there are two things that speak for the
Those are minor points, but the 'improvement' of a more consistent name only barely justifies breaking compatibility. |
cpu/stm32/Makefile.features
Outdated
| stm32f405% stm32f407% stm32f415% stm32f417% \ | ||
| stm32f427% stm32f429% stm32f437% stm32f439% \ | ||
| stm32f446% stm32f469% stm32f479% \ | ||
| stm32f7% |
There was a problem hiding this comment.
You can rebase and add stm32u5% to the list, then apply this patch:
diff --git a/cpu/stm32/include/cpu_conf.h b/cpu/stm32/include/cpu_conf.h
index 8930b13cea..e89fd40a45 100644
--- a/cpu/stm32/include/cpu_conf.h
+++ b/cpu/stm32/include/cpu_conf.h
@@ -67,7 +67,7 @@
#elif CPU_FAM_STM32U5
#include "stm32u5xx.h"
#include "irqs/u5/irqs.h"
-#define NUM_HEAPS 2
+#define NUM_HEAPS 3
#elif CPU_FAM_STM32WB
#include "stm32wbxx.h"
#include "irqs/wb/irqs.h"
diff --git a/cpu/stm32/ldscripts/stm32.ld b/cpu/stm32/ldscripts/stm32.ld
index 1ed6fa43d5..f216ac3ff6 100644
--- a/cpu/stm32/ldscripts/stm32.ld
+++ b/cpu/stm32/ldscripts/stm32.ld
@@ -31,8 +31,8 @@ SECTIONS
{
.heap2 ALIGN(4) (NOLOAD) :
{
- _sheap1 = . ;
- _eheap1 = ORIGIN(sram4) + LENGTH(sram4);
+ _sheap2 = . ;
+ _eheap2 = ORIGIN(sram4) + LENGTH(sram4);
} > sram4
}|
Let's not stall this because of name bikeshedding. If you think This PR just adds the implementation for STM32, it should not the name of what is being implemented as well. |
cpu/stm32/cpu_init.c
Outdated
| { | ||
| /* see reference manual "Battery backup domain" */ | ||
| #ifdef RCC_AHB1ENR_BKPSRAMEN | ||
| periph_clk_en(APB1, BIT_APB_PWREN); |
There was a problem hiding this comment.
The st32u5 seems to be a little different then. I must take a closer look on that one. For the others APB1 is also required.

My source of information which MCUs support support backup RAM was cmsis-header-stm32. I just pulled and noted that stm32h745xg, stm32h747xg, stm32u575xx, and stm32u585xx have been added.
All right, I am not going to make it a periph_ - module in this PR.
87749a4 to
ccb957e
Compare
| ifneq (, $(filter $(STM32_MODEL2), 7 8)) | ||
| RAM_LEN = 768K | ||
| SRAM4_LEN = 16K | ||
| BACKUP_RAM_ADDR = 0x40036400 |
There was a problem hiding this comment.
Is it correct to use the "Non-secure boundary address" here?
There was a problem hiding this comment.
I think the stm32u5 lacks periph_rtc support yet. I got compiling errors when I tried to compile the backup RAM test for the board with that CPU. That was the reason why I added FEATURES_REQUIRED += periph_rtc to the test.
At least the hello world example with a variable in backup RAM did compile.
|
Looks like there are some issues with riotboot still |
|
I have no clue what Murdock is trying to tell me, but maybe the last fixup does fix it. |
acd9834 to
c3218d7
Compare
| #ifndef RIOTBOOT | ||
| pm_backup_regulator_off(); | ||
| #endif |
There was a problem hiding this comment.
Is this supposed to be unavailable in riotboot.elf? However, BOARD=nucleo-f767zi make -C tests/riotboot flash test prints "TEST PASSED".
There was a problem hiding this comment.
This is only about saving a bit of energy right?
Given that riotboot only runs for a very brief moment and we want to keep it as small as possible, I'd say it's the right thing to do to leave it on here.
Of course we should not end up in a situation where using riotboot causes us to lose the contents of the backup RAM.
There was a problem hiding this comment.
Yes turning it off is just to save some power.
When I do FEATURES_REQUIRED+=riotboot BOARD=nucleo-f446re make -c tests/periph_backup_ram flash term, the counter in backup RAM is not reset, so I´d say it´s fine.
|
Feel free to squash if you think the comment is resolved. |
c3218d7 to
70d3d64
Compare
|
@aabadie are you also fine with the implementation or do you have any doubts? |
|
Thank you for reviewing and merging. |






Contribution description
This PR aims to make the backup SRAM available for
stm32MCUs.It also includes renaming ofbackup_ramtoperiph_backup_ramto better fit in the line with other peripheral features. I can split out the latter, if you agree to the renaming.Testing procedure
tests/periph_backup_ramwithnucleo-f767ziIssues/PRs references