cancel
Showing results for 
Search instead for 
Did you mean: 

Questions about an A-B memory schema on an stm32 microcontroller

ADrag.2
Associate II

I'm trying to figure out if an A-B memory schema on a microcontroller is feasible. Right now I have a bootloader and an application. I basically want to duplicate the application onto two sections of memory: A and B. This way I can receive a firmware update and write it to whatever section of memory isn't active, try and boot it, and if it doesn't work I can simply switch back to section A and write over the corrupt section B with the valid and working section A.

My first problem is with the vector table offset. It seems like the vector table address must be hard coded at compile time and cannot be switched at runtime. Is this true? If not, is there a way for me to change the vector table offset at runtime?

My second problem is with the linker script. Right now I have everything [FLASH] being created for section A. Can I simply just duplicate these lines of code for section B? I think .isr_vector won't like being there twice:

  /* The startup code into "FLASH" Rom type memory */
  .isr_vector :
  {
    . = ALIGN(4);
    KEEP(*(.isr_vector)) /* Startup code */
    . = ALIGN(4);
  } >FLASH_A
 
  /* The startup code into "FLASH" Rom type memory */
  .isr_vector :
  {
    . = ALIGN(4);
    KEEP(*(.isr_vector)) /* Startup code */
    . = ALIGN(4);
  } >FLASH_B
 
  /* The program code and other data into "FLASH" Rom type memory */
  .text :
  {
    . = ALIGN(4);
    *(.text)           /* .text sections (code) */
    *(.text*)          /* .text* sections (code) */
    *(.glue_7)         /* glue arm to thumb code */
    *(.glue_7t)        /* glue thumb to arm code */
    *(.eh_frame)
 
    KEEP (*(.init))
    KEEP (*(.fini))
 
    . = ALIGN(4);
    _etext = .;        /* define a global symbols at end of code */
  } >FLASH_A
 
  /* The program code and other data into "FLASH" Rom type memory */
  .text :
  {
    . = ALIGN(4);
    *(.text)           /* .text sections (code) */
    *(.text*)          /* .text* sections (code) */
    *(.glue_7)         /* glue arm to thumb code */
    *(.glue_7t)        /* glue thumb to arm code */
    *(.eh_frame)
 
    KEEP (*(.init))
    KEEP (*(.fini))
 
    . = ALIGN(4);
    _etext = .;        /* define a global symbols at end of code */
  } >FLASH_B

5 REPLIES 5

In every Cortex-M outside the M0, you can point the SCB->VTOR to any suitably aligned memory (notionally 512-byte aligned for most given table size, the NVIC/SCB does pin level muxing of the low order bits to index into the table)

This can be RAM, the M0 parts do this by physically remapping the zero address space, which can be ROM, FLASH or RAM, so the code can copy an offset vector table into the bottom of RAM (0x20000000), and then map/shadow that at 0x00000000 where the M0 expects it to live.

The addresses IN the Vector Table are absolute. If the code is in some location other than the one the Linker bound it for, then you'll need to relocate/fix-up the pointers in the vector table.

Most compilers can generate position-independent code, although you should check this by linking at different addresses and diffing the images.

Keil allows for R9 to carry the data section basis, allowing for multiple instances using the same code, in a thread-safe fashion. I mention it because some of the External Loader examples build this way.

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..

Thanks. The solution I went with was having two linker files and compiling twice.

Piranha
Chief II

Typically it's a better idea to make an initial bootloader as a separate minimal project, which is never updated. The fundamental problem with updating the bootloader is that you can ship a new firmware with a broken bootloader, the user will update the device and that's it - the device is either stuck with the current firmware version forever or completely bricked.

Here I'm not trying to update the bootloader. I'm trying to update A and B. The bootloader stays untouched. The idea is to update A if B is being used and B if A is being used, boot into the currently inactive section and if it fails, roll back to A or B. The problems I'm facing now are: A and B are separately compiled solutions, so copying A to B and vice versa won't work. I found an example here: https://www.youtube.com/watch?v=S0s69xNE1dE but this realistically doesn't work because if I can't use A or B to downgrade one or the other, then they don't have much purpose. I guess I've sorta answered my own question now.

People tend to exaggerate the necessity of automatic roll-back after unsuccessful firmware update. Most projects don't need that and it is enough that the bootloader can recover the device by just re-flashing a working firmware again. And, doing it that way, you will also not waste almost half of the FLASH memory. ;)