Skip to content

cpu/stm32: make backup SRAM available#16870

Merged
benpicco merged 2 commits intoRIOT-OS:masterfrom
fabian18:cpu_stm32_add_periph_backup_ram
Jan 21, 2022
Merged

cpu/stm32: make backup SRAM available#16870
benpicco merged 2 commits intoRIOT-OS:masterfrom
fabian18:cpu_stm32_add_periph_backup_ram

Conversation

@fabian18
Copy link
Copy Markdown
Contributor

@fabian18 fabian18 commented Sep 19, 2021

Contribution description

This PR aims to make the backup SRAM available for stm32 MCUs.
It also includes renaming of backup_ram to periph_backup_ram to 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_ram with nucleo-f767zi

2021-09-19 10:35:10,955 # Try to reconnect to /dev/ttyACM0 again...
2021-09-19 10:35:10,957 # Reconnected to serial port /dev/ttyACM0
2021-09-19 10:35:14,657 # counter: 2
2021-09-19 10:35:18,610 # counter: 3
2021-09-19 10:35:22,563 # counter: 4
2021-09-19 10:35:26,517 # counter: 5
2021-09-19 10:35:30,470 # counter: 6
2021-09-19 10:35:34,423 # counter: 7
2021-09-19 10:35:38,375 # counter: 8
2021-09-19 10:35:42,328 # counter: 9
2021-09-19 10:35:46,280 # counter: 10
2021-09-19 10:35:50,232 # counter: 11
2021-09-19 10:35:54,184 # counter: 12
2021-09-19 10:35:58,136 # counter: 13
2021-09-19 10:36:02,086 # counter: 14
2021-09-19 10:36:06,038 # counter: 15
2021-09-19 10:36:09,988 # counter: 16

⚠️ 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.

Welcome to pyterm!
Type '/exit' to exit.
2021-09-19 10:36:51,323 # WARNING: non-backup memory retained - did we really enter deep sleep?
2021-09-19 10:36:51,324 # counter: 27
2021-09-19 10:36:55,279 # WARNING: non-backup memory retained - did we really enter deep sleep?
2021-09-19 10:36:55,281 # counter: 28
2021-09-19 10:36:59,234 # WARNING: non-backup memory retained - did we really enter deep sleep?
2021-09-19 10:36:59,235 # counter: 29
2021-09-19 10:37:03,186 # WARNING: non-backup memory retained - did we really enter deep sleep?
2021-09-19 10:37:03,188 # counter: 30
2021-09-19 10:37:07,142 # WARNING: non-backup memory retained - did we really enter deep sleep?
2021-09-19 10:37:07,142 # counter: 31
2021-09-19 10:37:11,096 # WARNING: non-backup memory retained - did we really enter deep sleep?
2021-09-19 10:37:11,097 # counter: 32

Issues/PRs references

@github-actions github-actions bot added Area: cpu Area: CPU/MCU ports Area: Kconfig Area: Kconfig integration Area: tests Area: tests and testing framework Platform: ARM Platform: This PR/issue effects ARM-based platforms labels Sep 19, 2021
Copy link
Copy Markdown
Contributor

@benpicco benpicco left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

@github-actions github-actions bot added the Area: build system Area: Build system label Sep 23, 2021

#ifdef CPU_HAS_BACKUP_RAM
#if BACKUP_RAM_HAS_INIT
backup_ram_init();
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need for the indent

Suggested change
backup_ram_init();
backup_ram_init();

}

void backup_ram_regulator_on(void)
{
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

@github-actions github-actions bot added the Area: boards Area: Board ports label Sep 28, 2021
@fabian18
Copy link
Copy Markdown
Contributor Author

I have tried the VBAT mode.
If there is a battery connected to VBAT, the backup registers can be retained even when power supply is off.
When you remove the battery and replug the USB cable, backup ram content is reset.

I have removed a solder bridge (156) from my nucleo-f767zi, that connected VBAT with VDD and I connected 3.3V from another board to the VBAT pin to simulate a backup battery.

20210928_154710

20210928_160910

2021-09-29 00:22:49,557 # Backup RAM test
2021-09-29 00:22:49,557 # 
2021-09-29 00:22:49,563 # This test will increment the counter by 1, then enter deep sleep for 1s.
2021-09-29 00:22:49,564 # 
2021-09-29 00:22:49,570 #   Because some tools have trouble re-flashing/debugging in deep sleep,
2021-09-29 00:22:49,575 #   the test will wait for 3s before entering deep sleep.
2021-09-29 00:22:49,575 # 
2021-09-29 00:22:49,575 # counter: 1
2021-09-29 00:22:53,579 # counter: 2
2021-09-29 00:22:57,581 # counter: 3
2021-09-29 00:23:01,584 # counter: 4
2021-09-29 00:23:02,352 # Serial port disconnected, waiting to get reconnected...
2021-09-29 00:23:03,354 # Serial port disconnected, waiting to get reconnected...
2021-09-29 00:23:04,356 # Serial port disconnected, waiting to get reconnected...
2021-09-29 00:23:05,357 # Try to reconnect to /dev/ttyACM0 again...
2021-09-29 00:23:05,359 # Reconnected to serial port /dev/ttyACM0
2021-09-29 00:23:08,945 # counter: 6
2021-09-29 00:23:12,947 # counter: 7

#if IS_USED(MODULE_PERIPH_BACKUP_RAM)
bool cpu_woke_from_backup(void);
#else
static inline bool cpu_woke_from_backup(void) {return false;}
Copy link
Copy Markdown
Contributor

@benpicco benpicco Oct 4, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

or backup_ram_was_retained() because it is called after reset

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;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

  1. backup_ram_init() possibly (re)enables the regulator, thus checking whether the regulator was on is meaningless. 😯
  2. 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 an stm32::post_startup(), or a new reset_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));
}

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

those code snippets above don´t work 😔.
But the current code works as before.

@fabian18
Copy link
Copy Markdown
Contributor Author

fabian18 commented Oct 7, 2021

Those fixups are getting messy. I would like to squash

@fabian18
Copy link
Copy Markdown
Contributor Author

fabian18 commented Oct 7, 2021

As a reference of how to calculate the voltage value from the ADC sample I found this

Comment on lines +85 to +88
/* 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 */
Copy link
Copy Markdown
Contributor

@benpicco benpicco Oct 7, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

works good

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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().

@fabian18 fabian18 force-pushed the cpu_stm32_add_periph_backup_ram branch from 1fed5d0 to a7cff8c Compare October 7, 2021 15:53
@github-actions github-actions bot added the Area: drivers Area: Device drivers label Dec 28, 2021
@aabadie
Copy link
Copy Markdown
Contributor

aabadie commented Dec 28, 2021

It also includes renaming of backup_ram to periph_backup_ram to better fit in the line with other peripheral features

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 FEATURES_REQUIRED or HAS_xx.
And that would make this PR more focused on STM32 without introducing changes in other CPUs.

@fabian18
Copy link
Copy Markdown
Contributor Author

To me it seems a bit inconsistent to not have it as a periph - module.
What I think speaks for it are:

  • there is tests/periph_backup_ram
  • there is periph_rtc_mem which kind of serves the same purpose
  • having it as a periph- module gives the chance to distinguish whether it is used or not, i.e whether content must be preserved or not, using the backup regulator

What I think speaks against it are:

  • backup_ram.c only contains cpu_woke_from_backup() which is hardly a driver
  • the meaning of "peripheral" does not fully apply, but nor does it for periph_pm, periph_flashpage or periph_eeprom, I think

I admit that this PR has drifted away from the stm32 topic a bit.

@benpicco
Copy link
Copy Markdown
Contributor

benpicco commented Dec 29, 2021

It's of course bike shedding, but I think there are two things that speak for the backup_ram name (not periph_)

  • It's not connected to a peripheral (like RTC mem), but accessed directly by the CPU
  • It avoids needlessly breaking external apps that use the backup_ram name

Those are minor points, but the 'improvement' of a more consistent name only barely justifies breaking compatibility.
If it turns out to not be any more consistent at all, there is only busy work left.

@fabian18
Copy link
Copy Markdown
Contributor Author

I looked again into the datasheets and they consider backup RAM as peripheral too 😕, so I guess it makes sense.

stm32:
Screenshot_20211229_092209

samd51:
Screenshot_20211229_092548

lpc23xx:
Screenshot_20211229_093524

@benpicco benpicco added CI: ready for build If set, CI server will compile all applications for all available boards for the labeled PR and removed CI: ready for build If set, CI server will compile all applications for all available boards for the labeled PR labels Jan 2, 2022
stm32f405% stm32f407% stm32f415% stm32f417% \
stm32f427% stm32f429% stm32f437% stm32f439% \
stm32f446% stm32f469% stm32f479% \
stm32f7%
Copy link
Copy Markdown
Contributor

@benpicco benpicco Jan 2, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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
 }

@benpicco
Copy link
Copy Markdown
Contributor

benpicco commented Jan 2, 2022

Let's not stall this because of name bikeshedding. If you think periph_backup_ram really is a better name, that should be a separate PR (that then also marks the API change properly).

This PR just adds the implementation for STM32, it should not the name of what is being implemented as well.

{
/* see reference manual "Battery backup domain" */
#ifdef RCC_AHB1ENR_BKPSRAMEN
periph_clk_en(APB1, BIT_APB_PWREN);
Copy link
Copy Markdown
Contributor

@benpicco benpicco Jan 2, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we need APB1 here?

Looks like this is on another bus on STM32U5

image

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.
Screenshot_20220103_142720

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.

@fabian18 fabian18 force-pushed the cpu_stm32_add_periph_backup_ram branch from 87749a4 to ccb957e Compare January 3, 2022 21:51
@github-actions github-actions bot removed the Area: drivers Area: Device drivers label Jan 3, 2022
ifneq (, $(filter $(STM32_MODEL2), 7 8))
RAM_LEN = 768K
SRAM4_LEN = 16K
BACKUP_RAM_ADDR = 0x40036400
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it correct to use the "Non-secure boundary address" here?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@aabadie could give it a try

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

@benpicco
Copy link
Copy Markdown
Contributor

benpicco commented Jan 4, 2022

Looks like there are some issues with riotboot still

@fabian18
Copy link
Copy Markdown
Contributor Author

fabian18 commented Jan 4, 2022

I have no clue what Murdock is trying to tell me, but maybe the last fixup does fix it.

@fabian18 fabian18 force-pushed the cpu_stm32_add_periph_backup_ram branch from acd9834 to c3218d7 Compare January 20, 2022 14:11
Comment on lines +403 to +405
#ifndef RIOTBOOT
pm_backup_regulator_off();
#endif
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this supposed to be unavailable in riotboot.elf? However, BOARD=nucleo-f767zi make -C tests/riotboot flash test prints "TEST PASSED".

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

@benpicco
Copy link
Copy Markdown
Contributor

Feel free to squash if you think the comment is resolved.

@fabian18 fabian18 force-pushed the cpu_stm32_add_periph_backup_ram branch from c3218d7 to 70d3d64 Compare January 21, 2022 15:24
@benpicco benpicco enabled auto-merge January 21, 2022 15:26
@fabian18
Copy link
Copy Markdown
Contributor Author

@aabadie are you also fine with the implementation or do you have any doubts?

@benpicco benpicco merged commit 2520b5c into RIOT-OS:master Jan 21, 2022
@fabian18
Copy link
Copy Markdown
Contributor Author

Thank you for reviewing and merging.

@OlegHahm OlegHahm added this to the Release 2022.04 milestone Apr 25, 2022
@fabian18 fabian18 deleted the cpu_stm32_add_periph_backup_ram branch January 11, 2025 15:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Area: build system Area: Build system Area: cpu Area: CPU/MCU ports Area: Kconfig Area: Kconfig integration Area: tests Area: tests and testing framework CI: ready for build If set, CI server will compile all applications for all available boards for the labeled PR Platform: ARM Platform: This PR/issue effects ARM-based platforms

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants