Crash after powerdown if ram is not powered

sdk: 2.5.1

cpu: nrf5340

I have a bootloader which have a big ram block for upgrade purpose (around 250kb) in bss section (I will move it in noinit, but it is not the point here).

I  have an app, that poweroff unused ram with VMC register.

Sometimes, the app will enter in poweroff (using REGULATORS register), and will be awakened by vbus or nfc.

When the cpu is awakened, the bss section and idle stacks are in a part of the ram that is powered off, and this is a unrecoverable crash.

I found a solution with powering ram at early startup, but I would like to check with you if it is correct.

The solution may also be about powering up ram before entering poweroff, but I would like may bootloader to be robust.

I wrapped the method z_arm_reset (with linker argument --wrap), and wrote this method:

extern void __real_z_arm_reset();

__attribute__((naked))
void __wrap_z_arm_reset(void)
{
  NRF_VMC->RAM[0].POWERSET = 0xFFFF;
  NRF_VMC->RAM[1].POWERSET = 0xFFFF;
  NRF_VMC->RAM[2].POWERSET = 0xFFFF;
  NRF_VMC->RAM[3].POWERSET = 0xFFFF;
  NRF_VMC->RAM[4].POWERSET = 0xFFFF;
  NRF_VMC->RAM[5].POWERSET = 0xFFFF;
  NRF_VMC->RAM[6].POWERSET = 0xFFFF;
  NRF_VMC->RAM[7].POWERSET = 0xFFFF;
   __asm__ volatile ("b __real_z_arm_reset");
}

I checked the generated code, and no stack seem to be used:

00002a28 <__wrap_z_arm_reset>:
    2a28:       f64f 72ff       movw    r2, #65535      ; 0xffff
    2a2c:       4b09            ldr     r3, [pc, #36]   ; (2a54 <__wrap_z_arm_reset+0x2c>)
    2a2e:       f8c3 2604       str.w   r2, [r3, #1540] ; 0x604
    2a32:       f8c3 2614       str.w   r2, [r3, #1556] ; 0x614
    2a36:       f8c3 2624       str.w   r2, [r3, #1572] ; 0x624
    2a3a:       f8c3 2634       str.w   r2, [r3, #1588] ; 0x634
    2a3e:       f8c3 2644       str.w   r2, [r3, #1604] ; 0x644
    2a42:       f8c3 2654       str.w   r2, [r3, #1620] ; 0x654
    2a46:       f8c3 2664       str.w   r2, [r3, #1636] ; 0x664
    2a4a:       f8c3 2674       str.w   r2, [r3, #1652] ; 0x674
    2a4e:       f00a b9e7       b.w     ce20 <z_arm_reset>
    2a52:       bf00            nop
    2a54:       50081000        .word   0x50081000

Waiting for your answer.

Regards,

  • Could you expand on what you mean by "main stack of the bootloader" and "reset function of the bootloader"? Maybe a diagram of your memory layout could help as well, where is your bootloader and app placed in ROM, and which RAM areas do they use?

  • The reset function is the function called when the device start.

    Its implementation is in zephyr/arch/arm/core/aarch32/cortex_m\reset.S

    Its address is store in the vector table at addresse 0.

    By main stack I mean the main stack pointer registry (MSP).

  • OK, either your bootloader or application is must configure the behavior of the VMC before entering poweroff.

    The VMC automatically powers on/off sections of the RAM blocks depending on the system power state. You can configure two options for each section of each RAM block:

    1. Should this section be powered when the system is ON.

    2. Should this section be retained (powered) when the system is OFF.

    By default, after a "real" reset (using the reset pin or power reset), all RAM blocks will be powered when the system is ON, and not powered when the system is OFF.

    The behavior you are describing I have only been able to recreate by configuring the VMC to disable RAM sections in system ON which are in use by your bootloader/app, by doing the following:

    NRF_VMC->RAM[0].POWERCLR = 0xFFFF;
    NRF_VMC->RAM[1].POWERCLR = 0xFFFF;
    sys_poweroff();
    This will cause a crash when you reboot unless you manage to manually power the RAM sections again before accessing them, which seems to be what you are doing with
    void __wrap_z_arm_reset(void)
    How are you disabling power to the RAM blocks in power off would be my next question if the above is not accurate to your case :)
  • I power down ram inside app, at startup using power_up_unused_ram in nrf\include\ram_pwrdn.h.

    The ram power status is then not modified anymore.

    The app is a sample app with very low ram usage, so a lot of ram is powered down.

    The bootloader as a big ram buffer in bss section (that I will move in uninit, but it is not the point here). This buffer is before stacks that are in noinit sections. So the stacks are in positions that are powered off by app.

  • Ah, now I think I understand your setup. This is how I would solve your problem:

    The bootloader and application will share a "small" section of RAM, which will be configured to always follow the system state, powered when the system is ON, not powered when the system is OFF. I would make this explicit in the devicetree like this:

    / {
    	chosen {
    		zephyr,sram = &common_sram;
    	};
    
    	reserved-memory {
    		common_sram: image@20000000 {
    			reg = <0x20000000 0x4000>;
    		};
    
    		bank_sram: image@20004000 {
    			reg = <0x20004000 0x3c000>;
    		};
    	};
    };
    

    The common_sram section is the RAM containing .bss, .no_init etc. shared by the application and bootloader. This section will never be powered down while the system is ON. The section bank_sram will be used exclusively for the RAM bank.

    Your bootloader will need to power this bank manually before using the memory, you can get the address and size of your memory section from the devicetree

    uintptr_t bank_start = DT_REG_ADDR(DT_NODELABEL(bank_sram));
    size_t bank_size = DT_REG_SIZE(DT_NODELABEL(bank_sram));
    uintptr_t bank_end = DT_REG_ADDR(DT_NODELABEL(bank_sram)) + DT_REG_SIZE(DT_NODELABEL(bank_sram));
    

     

    Which you can then pass to power_up_ram() and power_down_ram() before and after using the RAM bank. From you application, simply call power_down_unused_ram() if you don't intend to power down the bank_sram RAM from the bootloader before entering the application.

Related