Determine Reset cause

I would like to save some sensor data variables if the cause of a reset was a WDT reset, or a software reset (in case I want to implement a daily reset for stability - don't think that will be required, but an option)

I am using the following code available online (small mods for dual core for portenta), which compiles OK, but the result is always UNKNOWN. I tried button reset, WDT reset, and power on reset. Has anyone tried this, and have gotten better results?

  reset_cause_t resetCause = reset_cause_get();
  char * resetCauseStr = (char *)reset_cause_get_name(resetCause);
  RCC_ClearFlag();




ty/// @brief  Possible STM32 system reset causes
typedef enum reset_cause_e
{
    RESET_CAUSE_UNKNOWN = 0,
    RESET_CAUSE_LOW_POWER_RESET,
    RESET_CAUSE_WINDOW_WATCHDOG_RESET,
    RESET_CAUSE_INDEPENDENT_WATCHDOG_RESET,
    RESET_CAUSE_SOFTWARE_RESET,
    RESET_CAUSE_POWER_ON_POWER_DOWN_RESET,
    RESET_CAUSE_EXTERNAL_RESET_PIN_RESET,
    RESET_CAUSE_BROWNOUT_RESET,
} reset_cause_t;

/// @brief      Obtain the STM32 system reset cause
/// @param      None
/// @return     The system reset cause
reset_cause_t reset_cause_get(void)
{
    reset_cause_t reset_cause;

    if (__HAL_RCC_GET_FLAG(RCC_FLAG_LPWR1RST))
    {
        reset_cause = RESET_CAUSE_LOW_POWER_RESET;
    }
    else if (__HAL_RCC_GET_FLAG(RCC_FLAG_WWDG1RST))
    {
        reset_cause = RESET_CAUSE_WINDOW_WATCHDOG_RESET;
    }
    else if (__HAL_RCC_GET_FLAG(RCC_FLAG_IWDG1RST))
    {
        reset_cause = RESET_CAUSE_INDEPENDENT_WATCHDOG_RESET;
    }
    else if (__HAL_RCC_GET_FLAG(RCC_FLAG_SFTRST))
    {
        // This reset is induced by calling the ARM CMSIS 
        // `NVIC_SystemReset()` function!
        reset_cause = RESET_CAUSE_SOFTWARE_RESET; 
    }
    else if (__HAL_RCC_GET_FLAG(RCC_FLAG_PORRST))
    {
        reset_cause = RESET_CAUSE_POWER_ON_POWER_DOWN_RESET;
    }
    else if (__HAL_RCC_GET_FLAG(RCC_FLAG_PINRST))
    {
        reset_cause = RESET_CAUSE_EXTERNAL_RESET_PIN_RESET;
    }
    // Needs to come *after* checking the `RCC_FLAG_PORRST` flag in order to
    // ensure first that the reset cause is NOT a POR/PDR reset. See note
    // below. 
    else if (__HAL_RCC_GET_FLAG(RCC_FLAG_BORRST))
    {
        reset_cause = RESET_CAUSE_BROWNOUT_RESET;
    }
    else
    {
        reset_cause = RESET_CAUSE_UNKNOWN;
    }

    // Clear all the reset flags or else they will remain set during future
    // resets until system power is fully removed.
    __HAL_RCC_CLEAR_RESET_FLAGS();

    return reset_cause; 
}

// Note: any of the STM32 Hardware Abstraction Layer (HAL) Reset and Clock
// Controller (RCC) header files, such as 
// "STM32Cube_FW_F7_V1.12.0/Drivers/STM32F7xx_HAL_Driver/Inc/stm32f7xx_hal_rcc.h",
// "STM32Cube_FW_F2_V1.7.0/Drivers/STM32F2xx_HAL_Driver/Inc/stm32f2xx_hal_rcc.h",
// etc., indicate that the brownout flag, `RCC_FLAG_BORRST`, will be set in
// the event of a "POR/PDR or BOR reset". This means that a Power-On Reset
// (POR), Power-Down Reset (PDR), OR Brownout Reset (BOR) will trip this flag.
// See the doxygen just above their definition for the 
// `__HAL_RCC_GET_FLAG()` macro to see this:
//      "@arg RCC_FLAG_BORRST: POR/PDR or BOR reset." <== indicates the Brownout
//      Reset flag will *also* be set in the event of a POR/PDR. 
// Therefore, you must check the Brownout Reset flag, `RCC_FLAG_BORRST`, *after*
// first checking the `RCC_FLAG_PORRST` flag in order to ensure first that the
// reset cause is NOT a POR/PDR reset.


/// @brief      Obtain the system reset cause as an ASCII-printable name string 
///             from a reset cause type
/// @param[in]  reset_cause     The previously-obtained system reset cause
/// @return     A null-terminated ASCII name string describing the system 
///             reset cause
const char * reset_cause_get_name(reset_cause_t reset_cause)
{
    const char * reset_cause_name = "TBD";

    switch (reset_cause)
    {
        case RESET_CAUSE_UNKNOWN:
            reset_cause_name = "UNKNOWN";
            break;
        case RESET_CAUSE_LOW_POWER_RESET:
            reset_cause_name = "LOW_POWER_RESET";
            break;
        case RESET_CAUSE_WINDOW_WATCHDOG_RESET:
            reset_cause_name = "WINDOW_WATCHDOG_RESET";
            break;
        case RESET_CAUSE_INDEPENDENT_WATCHDOG_RESET:
            reset_cause_name = "INDEPENDENT_WATCHDOG_RESET";
            break;
        case RESET_CAUSE_SOFTWARE_RESET:
            reset_cause_name = "SOFTWARE_RESET";
            break;
        case RESET_CAUSE_POWER_ON_POWER_DOWN_RESET:
            reset_cause_name = "POWER-ON_RESET (POR) / POWER-DOWN_RESET (PDR)";
            break;
        case RESET_CAUSE_EXTERNAL_RESET_PIN_RESET:
            reset_cause_name = "EXTERNAL_RESET_PIN_RESET";
            break;
        case RESET_CAUSE_BROWNOUT_RESET:
            reset_cause_name = "BROWNOUT_RESET (BOR)";
            break;
    }

    return reset_cause_name;
}

As I know, this MCU does NOT have a register to read what has caused the RESET.
A watchdog (WDT) might have its own INT vector (if not configured to let WDT do a hard reset).

Otherwise, if you have pressed a reset button on board or if you have fired reset via NVIC (there is a way to force reset via software, I use it as "fwreset") - no way to distinguish.

My suggestion:
The RTC has Backup Registers (BKPxR). These registers are NOT reset when you reset the board or MCU. It needs a power cycle (and I have seen at least 10 seconds no power) to get these RTC registers cleared (to zero). They survive a reset and a short power outage.

I use these RTC BKP registers to store values which should be still there when I reset the board. I use it for system configuration (changes still effective after next reset).

You do do this:

  • write a pattern value to an RTC BKP register, e.g. like a trace value.
  • if a reset kicks in, after FW has rebooted - you read this RTC register
  • you would see which value is there and you would know when and what has caused the reset

Example:
Watchdog enabled, or when WWDG_IRQHandler is triggered - you set RTC BKP with 1.
After FW is running normally, you set value 2 there.
Before you trigger a reset via "fwreset", you set value 3.
When FW comes back after a re-boot - you check the value and you can distinguish what was the reset reason.
BTW: the Arduino Bootloader uses some of these RTC registers, for a similar purpose. But the RTC has 16 32bit backup registers - so, enough to "forward" information from one reset to the other.

Just enable RTC (it needs just enable and clock config, not a full blown calendar setup). It makes the RTC BKP registers available.

Other then this:
you could use a memory location (RAM) to write a value there. But it must be a location which is not initialized during boot (as .data and .bss are). It is a memory location which has a random value after power up. But if not initialized and not a power cycle - it should also survive (as long it is internal SRAM, not external DDR RAM).
Just to create "fancy" pattern in order to make sure a random value after power cycles is not taken as a wrong information.

Means:
Handle the "reset reason" yourself, by using a means which will survive a reset forced (but think about also a power cycle as a reason).
Write info to "surviving" registers and set it all the time accordingly before a potential reset can happen.
Evaluate the "backup" register on the next boot (after a reset) and find the code telling where the reset has kicked in (and what the reason was).

Thanks for the detailed explanation.

I thought all the STM32 CPUs had a register to determine the cause of the reset, that was accessible via HAL functions. If it does not, that would explain why the code does not work.

I have asked ST for tech support help on this issue - they are VERY slow. They came back after 5 days answering a question that was not even remotely the one I posed. I reclarified the question, and will see what the reply is.

Unfortunately, I need to store about 500k of data, so the backup RAM would not work, except for user initiated resets (for example, if I want to perform a daily reset on the devide, I could set the Backup SRAM pre reset. This would not be possible for WDT or power cycle reset.

I have considered your final suggestion as the way to go. I would create a 50 byte array at the start of the SDRAM address space, and save a byte array into it. At boot up, IO would see if that array had the expected data, and if it does, I would not initialize it.

I did not do this yet, as it seems like an imperfect way to go - I am not crazy about it, but if all else fails, that is the way I will go.

No, as I know, this MCU does not have a register to read the reset reason (other have, mostly Cortex-A processors).

500k of data?
This is so huge!

Sure, Backup RAM is just 4KB (and would need external battery to retain the data).
What about SDCard or the QSPI flash on board?

BTW: I was wrong: RTC has 32 BKP registers: so you can store 32x 32bit values in order to survive a reset.

And: as long as you do not power cycle: you can survive data also on internal SRAM (or potentially, but not sure: on external SDRAM).
As long as you do not rip the data out via the reset handler and startup.S initialization...

External SDRAM:
I would NOT rely on if it will survive: if the refresh is stopped (e.g. during the reset phase, before SDRAM is initialized again - data is lost).

So,
a) write to SD Card before reset (but make sure the FileSystem has flashed all before reset!)
b) flash into QSPI before reset (but make sure not to get reset during a flash write!)
c) try BackupRAM (4KB) - but not sure what happens on reset and re-init (don't assume it is
still there after reset, Backup RAM can be reset as well!)
d) use RTC BKP registers (32x 32bit) - for sure it works (I use it)

RTC is really easy to use.

I was wrong:
There is a register RCC reset status register (RCC_RSR) which gives you the reason of a RESET.
It can distinguish between CPU1 and CPU2, Watchdog as well as external pin reset or (SW) system reset (by which core).
This would do the job to differentiate why the system was reset.

I gotv this from st today - which confirms above

Week try it out this weekend

Robert,

Thanks for explaining the entirety of your situation. In order to determine exactly where your reset is coming from, you need to look at the RCC clock control & status register (RCC_CSR). For more information on this register please refer to RM0399 section 7.7.10 and section 9.7.26. Section 9 in general explains all the functionality behind the Reset and Clock Control.

All reset flags can be found in the Reset and Clock Controller (RCC) header file for your microcontroller. You can use the __HAL_RCC_GET_FLAG() macro within the "stm32f2xx_hal_rcc.h" header file to obtain a list of all the reset flags used in the function.

Here is a list of all RCC flags:

`
/** @brief Check RCC flag is set or not.

  • @param FLAG: specifies the flag to check.

  • This parameter can be one of the following values:
    
  •  @arg RCC_FLAG_HSIRDY: HSI oscillator clock ready
    
  •  @arg RCC_FLAG_HSIDIV: HSI divider flag
    
  •  @arg RCC_FLAG_CSIRDY: CSI oscillator clock ready
    
  •  @arg RCC_FLAG_HSI48RDY: HSI48 oscillator clock ready
    
  •  @arg RCC_FLAG_HSERDY: HSE oscillator clock ready
    
  •  @arg RCC_FLAG_D1CKRDY: Domain1 clock ready (*)
    
  •  @arg RCC_FLAG_D2CKRDY: Domain2 clock ready (*)
    
  •  @arg RCC_FLAG_CPUCKRDY: CPU Domain clock ready (CPU, APB3, bus matrix1 and related memories) (*)
    
  •  @arg RCC_FLAG_CDCKRDY: CPU Domain clock ready (*)
    
  •  @arg RCC_FLAG_PLLRDY: PLL1 clock ready
    
  •  @arg RCC_FLAG_PLL2RDY: PLL2 clock ready
    
  •  @arg RCC_FLAG_PLL3RDY: PLL3 clock ready
    
  •  @arg RCC_FLAG_LSERDY: LSE oscillator clock ready
    
  •  @arg RCC_FLAG_LSIRDY: LSI oscillator clock ready
    
  •  @arg RCC_FLAG_CPURST: CPU reset flag
    
  •  @arg RCC_FLAG_D1RST: D1 domain power switch reset flag (*)
    
  •  @arg RCC_FLAG_D2RST: D2 domain power switch reset flag (*)
    
  •  @arg RCC_FLAG_CDRST: CD domain power switch reset flag (*)
    
  •  @arg RCC_FLAG_BORRST: BOR reset flag
    
  •  @arg RCC_FLAG_PINRST: Pin reset
    
  •  @arg RCC_FLAG_PORRST: POR/PDR reset
    
  •  @arg RCC_FLAG_SFTRST: System reset from CPU reset flag
    
  •  @arg RCC_FLAG_BORRST:  D2 domain power switch reset flag
    
  •  @arg RCC_FLAG_IWDG1RST: CPU Independent Watchdog reset
    
  •  @arg RCC_FLAG_WWDG1RST: Window Watchdog1 reset
    
  •  @arg RCC_FLAG_LPWR1RST: Reset due to illegal D1 DSTANDBY or CPU CSTOP flag
    
  •  @arg RCC_FLAG_LPWR2RST: Reset due to illegal D2 DSTANDBY flag
    
  • @retval The new state of FLAG (TRUE or FALSE).

  • (*) Available on some STM32H7 lines only.

*/

`

From here I would recommend testing one command at a time to see what your result is. For example you can comment out all other __HAL_RCC_GET_FLAG() commands and just try __HAL_RCC_GET_FLAG(RCC_FLAG_WWDG1RST) to see if your reset is from Window Watchdog1 and so forth.

Best,

MCU Support Team

Well, it appears that the ST Micro suggestion is the exact same thing I was doing on the initial post above.

I am still getting 0 in the register check - always

I simplified the code to

  int restmp1 = __HAL_RCC_GET_FLAG(RCC_FLAG_WWDG1RST);
  int restmp2 = __HAL_RCC_GET_FLAG(RCC_FLAG_PORRST);

I then created a WDT event and subsequently a power off/on event, and I still get 0 for each of those variables.

I am wondering if the startup code for the portenta potentially resets the RCC flag somehow.

I have asked that question to Arduino Support, we'll see what the reply is.

It appears that the boot loader clears the reset reason as part of the process of checking the reset reason.

From mcuboot-arduino-stm32h7/main.cpp at main · arduino/mcuboot-arduino-stm32h7 · GitHub we can see where it asks the mbed library for the reset reason:

  // in case we have been reset let's wait 500 ms to see if user is trying to stay in bootloader
  if (ResetReason::get() == RESET_REASON_PIN_RESET) {
    // now that we've been reset let's set magic. resetting with this set will
    // flag we need to stay in bootloader.
    RTCSetBKPRegister(RTC_BKP_DR0, 0xDF59);
    HAL_Delay(500);
  }

In the mbed implementation of that ResetReason::get() method, we see that it clears the reset reason as well which would explain why you don't see anything when you check it. Code snippet from mbed-os/ResetReason.cpp at master · ARMmbed/mbed-os · GitHub

reset_reason_t ResetReason::get()
{
    // Store the reason statically so it can be accessed after the first call to
    // this function resets it.
    const static reset_reason_t reason = hal_reset_reason_get();

    // Call get raw to cache the reset reason before clearing the registers.
    ResetReason::get_raw();

    hal_reset_reason_clear();

    return reason;
}

Thanks, I was suspicious of that, but did not know where to look.

I forwarded the info to Arduino support, I am asking if they can create a global variable where the read occurs, so that that info can be available in the setup routine.

We'll see what they say.

"Uppps" - very possible that the Arduino bootloader interferes here!
I have checked the source code for this bootloader a bit and I saw: it sets several different "magic numbers" in RTC backup registers.

I can very heavily imagine that the bootloader checks also the "reset reason" (and clears this register).
A boot loader "needs to know" if a new reboot is caused due to "flashing new firmware done" or if it is an "activation of bootloader" (via double press on reset button) etc.

If the bootloader "kicks in" here (and obviously it does) - the reset reason register does not have any meaning anymore when the/your final FW is running.

But it remains possible to use RTC backup registers to "trace" your own FW. Just: some registers (I guess the first ones) are used also by the Arduino bootloader (with bootloader involved - some RTC registers are not usable).

Anyway: I would suggest still to write to a memory location which can survive a reset (even with the bootloader interfering). A non-pre-initialized memory location, higher RTC backup registers potentially not used ... in order trace yourself where (when) a reset has happened ("what was the last trace-log-pattern written"?).

(Using STM32CubeIDE with external debugger and getting rid of Arduino bootloader (and all the LIBs) - helped me a lot to "have control over the entire system" and to understand what is going on)

Arduino Support replied that the engineers are looking into the issue, and should have a solution for us with the next Board Manager version.

I think I will implement the memory pattern routine, and will use it for now, and in parallel with the reset cause when it is available (for a little added safety).

What is the issue (engineers are looking into what? changing the MCU?)?

Sure, patterns in memory will work, but to be honest: RTC BKPxx registers are much easier to use.

Here, my code for using RTC BKP registers (as "syscfg" to survive a reset):
debug_sys.h (2.4 KB)
debug_sys.c (2.9 KB)

You need just a clock config for RTC (in my "system_clock.c") as:

//enable also RTC: we use the BKPxx registers
    PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_RTC | RCC_PERIPHCLK_USB | RCC_PERIPHCLK_SPI2 | RCC_PERIPHCLK_QSPI;

Please, see this RCC_PERIPHCLK_RTC.
Just enable the RTC - nothing else to configure there, just to make the RTC BKP registers available.

BTW:
when you use a memory location:

  • it must be a region which is NOT initialized on startup (not the code pre-initializing memories)
  • it can have any random value on power-up (you need a nice "key pattern" to realize if 'valid'
  • RTC BKP registers have the beauty that they are reset to 0 with a power cycle (just long enough without power), so: not random on next startup
  • and RTC BKP registers do not need any special "tricks" in linker script (reserving a space never initialized)

I asked them to create a global variable that has the reset cause when they first read it - this way, even if the RCC register is cleared, the cause will still be known.

In my case, I am storing my own setup variable in the external SD card as a JSON file. It allows me to view them by reading the SD card, as well as making changes if required manually (for testing etc).

I write to that file infrequently, so SD wear is not an issue. For the WDT, I will look at your suggestion of using a BKP register, but the SDRAM is very straightforward - I will reserve space for 100 sensors at the start of the SDRAM (1440 x4 x 100 bytes), and the SDRAM.begin(0x6008CA00) will start after the last memory location that I reserve above. Position 0 will be used as the pattern position, and the other 99 will be used to store sensor data.

For the reboot, my main requirement is power up versus WDT.

I am getting an annoying crash that happens when a web page (updating every 1 second via ajax) is connected to the device
if the computer is on standby, and the monitor is in power save mode, I need to wake up the monitor manually first before trying to log in, otherwise the LCD screen will take about 15 seconds to reformat itself, and position all the windows. It is an unusual setup, with 4 monitors, and the main screen is an LG OLED 4k HDMI TV - this is the monitor that what causes the issue .
During that rearranging of monitors 15 second time there is a lot of lag, the web page loses communication with the portenta, and the crash occurs - triggering the WDT.

Hard to reproduce every time. I have added code to timeout the web page after 750 mS, and after 3 timeouts, it pauses any further ajax requests. This helped, but did not eliminate the issue.

For now, when it happens, the WDT triggers, and the device restarts - hence me wanting at least for now to keep the log data in SDRAM across this event.

Cool, well done.

Just comments:
RTC BKP registers are not cleared on any reset. They just reset to 0 with a power cycle.
So, they should have more deterministic content.
SDRAM will have random content. And I am not sure how long you can press the reset button or how long a reset startup can take.
SDRAM clock is dead during reset. If it takes too long to reenable SDRAM again - I would assume the SDRAM content fades away (no refresh for SDRAM).
Do a test: press reset button for 1 minute and check what happens with SDRAM content.

Your crash - no idea.
I could make just a guess (what I have experienced):
A web browser (on PC client) opens all the time a new socket when a new or the same page is requested. So, an "autorefresh" will do the same: all the time the client creates a new socket, browser closes the socket at the end.

This has two "effects":
after a while all the TCP port numbers (64K numbers possible) where used. The client creates a response port all the time with a different port number. Previously used ports remain in a "zombie" state. TCP uses all the time a new port just in case there is still "late" response on an old, previous socket.

So, after a while, the client runs out of port numbers! Client cannot create a new socket. And the "life time" of previously used ports kicks on: it takes a while until an old used port is really released and can be used again.
I have read, this can take up to 30 minutes.

Maybe this is the case here for you: the client runs out of port numbers and cannot create a socket. It will wait for this "zombie release duration" until it uses a port number again.

ATTENTION:
It fails for me due to this issue: if I write a Python script and every request creates a new socket, releases the socket after the response - I run out of ports.
I cannot loop this "create-do-close" command: after 64K times - I hit this "out of port" issue.

You can see it with Windows "netstat" command (the "zombie sockets" in list).
So, I use Python scripts and let the socket open until the scripts finishes (or MCU realizes that the socket has "stalled", e.g. script has been killed/crashed).
Looping with a Web Browser ("autorefresh") can cause the same issue with "out of port numbers" after a while.

Another issue could be:
if you have "autorefresh" (every 1 second) and PC goes to sleep mode: maybe you are in a state (on MCU) where the socket is created and remains in use but is not closed. The MCU (HTTP server) might have still this socket in use. But PC wakes up again and creates a new socket. So, the MCU will not accept the new socket because it is listening still on the previous, still open one.
No idea how do you handle this case, maybe your watchdog kicks in order to resolve this "dead lock".

I am using the Async Web Server library, does the out of port issue occur with that library, or just with the standard synchronous library?

I ran CurPorts - provides a lot of details - it lists about 120 connections to my portenta IP address, they seem to close at the rate of about 1 per second, while new ones open up. In the event of excessive lag, unless the javascript routine goes crazy - I should not get more than 15 attempts to make a connections that period.

As far as the question of in sleep getting a deadlock - I would not know how to check for this, but I can say with some certainty, that the PC is unable to multitask effectively when the screen reformat happens - likely low level driver stuff going on in the NVIDIA driver, that results in very sluggish performance (cannot open close windows, cannot move them, can't even type - but after 15 seconds when all the dust settles, the PC is OK again - but a chance the portenta that was being queried every 1 second by the same PC, may have crashed)

One question for the RTC BKP - I am using the actual RTC in the application, and therefore have a 3V battery connected to the unit (on the breakout board, but will make my own carrier board in the near future). I believe the backup registers will not clear to zero if the RTC battery is present - correct?

true: if backup battery (on RTC) - the RTC registers are never cleared (there is not any power cycle).
But it sounds even better to me: you keep any settings (I use RTC BKP for "syscfg") or trace values (reset reason) there.

I think, for regular use of RTC, e.g. calendar, data, time - the RTC BKP registers are not needed and not used (just Arduino Bootloader uses some). As I understand: they are like "spare registers", not needed for any function, like a backup memory (32x 32bit registers).

With backup battery you could also Backup SRAM (4KB).
I could enable and use, just the MCU core clock speed seems to be limited (not 480 MHz) when accessing Backup SRAM (otherwise errors).

Regarding your "CurPorts" (I do not know it):
when you see so many connections and they seem to close very second - it sounds to me exactly the issue I meant:
sockets are created all the time again. The list gets longer and longer and after a while some entries disappear. These are the "zombie" connections.
I guess you have some attributes to see in list: they should tell you if it is a "zombie".
But I am sure: you hit the issue with "creating-do-release a socket" all the time. And they remain for a while in "zombie" state (before they disappear from list).

Just finished coding the static SDRAM arrays.

I verified that on a WDT trigger, the arrays are OK, they do not lose any of their content. They are also ok with a hardware reset push. They do lose data on a double green reset (to program device), or of course on power up/down.

I used the following code to create and check a pattern array

void createPatternArray() {
  int i;
  uint8_t *ptr = (uint8_t *)0x60000000;
  
  for(i=0; i<1440 * (int)sizeof(float); i++) {
    *ptr++ = i%256;
  }
}

bool checkPatternArray() {
  int i;
  uint8_t *ptr = (uint8_t *)0x60000000;
  static bool prevCheck = false;
  static bool didCheck = false;
  
  if (didCheck == true) {
    return(prevCheck);
  }

  didCheck = true;
  
  for(i=0; i<1440 * (int)sizeof(float); i++) {
    if (*ptr++ != i%256) {
      return(false);
    }
  }
  prevCheck = true;
  return(true);
}

Make sense: double RESET press enters the Bootloader.
Bootloader will not enable SDRAM clock and you stay in this mode until FW was flashed.
This is long enough for SDRAM not having a clock to lose the data.

BTW:
I use the RTC BKP registers, even with Bootloader. It seems to use just the very first BKP0R.
I use all following and it seems to work.
It survives also a RESET double press, a new FW load etc.