I have developed in the past month or so a way to have position-independent-code (PIC) firmware image (on ARM Cortex-M0 and Cortex-M4) which can be put (almost) anywhere in flash. I’m still refining the concept and will write an in-depth-article about it. There is a part of the PIC stuff that I can discuss briefly to get us going about THIS article.
Part of the PIC firmware + bootloader has been interrupt vector table relocation. Basically the bootloader needs to read from flash the firmware vector table and copy it to RAM and then point the MCU to use the vector table from RAM. Some of tutorials, videos and comments suggest that bootloader should do the relocation. I have, however, come to the conclusion that this is actually wrong way to proceed. I will try to demonstrate now why.
Lets describe original situation. There is a system with one unchangeable bootloader and two firmware positions. Firmware has vector table in the beginning:
All fine and dandy. But then consider that we want to alter the location of the firmware vector table (if we, for example, want to include a dynamic header as described in earlier post). By convention we should still have stack pointer address as first 4 bytes and then reset handler address as next 4 bytes. But pay attention to the actual vector table relocation if we use the old bootloader:
As we can see, we have created a problem. We assumed earlier that we cannot change the bootloader. Now the poor bootloader thinks that firmware works the same way as earlier and copies the size of the data to RAM. Too bad now the firmware image is different and the bootloader copied correct stack pointer and correct reset handler address to RAM, but after that it copied random data. When firmware interrupts are run, they will most likely crash the program.
Luckily there is a solution. We make the bootloader to NOT copy vector table information to RAM. Instead the setup routines (reset handler) of the actual firmware perform the copy. See:
What we need to in bootloader is to disable interrupts, set stack pointer and then jump to firmware reset handler.
This has multiple benefits:
- Bootloader code becomes simpler and more portable.
- Firmware code modifications become more flexible because we don’t need to care about location of the vector table, we just need to ensure stack pointer and firmware reset handler address occupy the first 8 bytes (respectively) of the firmware image.
- (With the right set of options) we can directly debug the firmware image, there is no need anymore for bootloader to act as our training wheels. This helps immensely in verification and debugging.
So, for these reasons I say: Ditch the firmware vector table relocation magics in bootloader and let firmware do it. Because firmware knows always how its own code and logic is internally organized.