Search code examples
stm32

Flash bank swapping with BFB2 on STM32G4


I am implementing an OTA update system for the STM32G4, and want to use the dual flash banks to make it safe. That is, write the new firmware in the inactive flash bank while the main program is still running from the active bank, and reboot only after checking the integrity of the newly installed firmware.

To do this, I'm setting the BFB2 bit from the option bytes to 1, which, according to AN2606 (section 45.2) and this presentation (page 8), tells the MCU to first attempt booting from flash bank 2 if the stack address located at offset 0x0 of the flash bank 2 is a valid SRAM address. Otherwise the MCU boots from flash bank 1. This fits my use case, as I can write the new firmware to the flash bank 2, except for the stack address which is written only after an integrity check to commit the change.

However, it seems the flash banks addresses aren't swapped when booting from the flash bank 2. According to the programming manual (section 10.2.1), swapping the flash banks addresses is done by setting the FB_MODE bit, which is cleared on reset and does not seem to be set when booting from flash bank 2. Not having the flash banks addresses swapped automatically is problematic because it means the firmware cannot be linked assuming a flash address of 0x08000000.

The workaround I can think of is to integrate a bootloader that checks which bank is in use, set the FB_MODE bit accordingly, then jump to the actual firmware entry point. This bootloader would be flashed in both banks, and not updated during an OTA update.

Is there really no way to swap flash banks addresses automatically when booting from flash bank 2? Or is there another solution which wouldn't involve a bootloader?

Edit: after reading this thread on the ST forums, I am not sure anymore my understanding is correct. If this user is right, then setting BFB2 swaps the banks and their addresses, and setting FB_MODE on top of that would "cancel" the swap.


Solution

  • I finally got this to work after a lot of trial and error. Some of my assumptions were wrong. Here's what the update process looks like:

    1. Check the FB_MODE bit to know which bank the firmware is currently running from.
    2. Perform a mass erase on the other bank (MER2 bit if FB_MODE is 0, MER1 otherwise).
    3. Write the new firmware version at 0x08040000. The inactive bank is always mapped at that address, regardless of FB_MODE.
    4. Set the BFB2 bit in the option bytes if FB_MODE is 0, or clear it otherwise. Do not blindly toggle BFB2, because it does not necessarily reflect which bank is active.
    5. Reload option bytes with the LAUNCH bit, which also resets the MCU.
    6. If the stack top address written at 0x08040000 is valid, the ST bootloader should boot to the new firmware. See step 1.

    Some notes:

    • The firmware is always linked with a virtual address of 0x08000000, regardless of which bank it will be flashed to.
    • In dual bank mode, the main flash is NOT aliased to 0x0, so the firmware can't be linked to that address.
    • This does not update the flash option bytes, only the firmware code. I solved this by updating the flash option bytes if necessary when the new firmware boots for the first time.