
Ever wonder, how easy it is, these days to update your phone to the latest software. Another version of “Android” or “iOS” might be on its way while you are reading this post. Any seconds you can get the notification to update your device. You don’t have to change your phone to use the latest software available in the market (unless it is too old 😊).
Let’s back to bootloader….
The bootloader is an inevitable part of any embedded application (also called firmware). Especially, when you want to design a system which can be updated while it is in the operation or in the hand of customers. With this blog post, I will explain a bootloader which I designed to add support for the software update in an embedded device recently. But this bootloader can be used with any device and it is independent of the user application. If you want to skip to source code directly you can access it on my GitHub (Link).
Let start with the architectural block diagram and some hardware detail of the test device.

The above sequence flow diagram illustrates the decision tree inside the bootloader, management of software update and recovery of the target device from any bad update (fail-safe). If the software is not tested carefully before pushing the update, it can cause catastrophic failure in the field. Which may lead the devices to go anything from unstable to completely unfunctional. In such a scenario, a fail-safe feature inside the bootloader can prevent the device from going unstable and help in bringing it back to anything from the basic functional state to fully functional state.
The below picture shows the block diagram of the hardware setup required for the bootloader.

It is pretty straight forward hardware setup. The microcontroller (STM32) is connected to an external serial flash and some debug pins over UART coming out of controller which is required during development only.
External flash memory is used to keep the candidate firmware (firmware to be updated). The bootloader doesn’t care about the mechanism by which candidate firmware got inside that external flash. All bootloader sees during update checkup, whether external flash contains a candidate firmware for update or not. It is the job of the user application to download the candidate firmware from the internet (FOTA) or over USB (DFU) and store it into external flash so that on next boot (a hard or soft reset) bootloader sees that candidate firmware inside the external flash and makes the update.
Let’s dive deeper …
In figure 1, The code flow sequence of the bootloader is very simple unless you know what’s happening inside each rectangle and rhombus box. so let’s look at them one by one.
PowerOnReset denotes to a state of the device (or microcontroller). It is the very first state for the device to be in, Whenever the device gets the power, or its get reset by the watchdog, by pressing the external reset button, by BOR, etc.
CheckUpdate is a procedure ( a C function) which checks if any candidate firmware is available in the external flash.
CopyUpdate is another procedure (again a C function) which copy the candidate firmware from the external flash into the internal flash of the microcontroller. Once the firmware is copied successfully, it can start.
CopyFailSafe copies the fail-safe firmware to the internal flash of the microcontroller. fail-safe firmware is loaded in the factory at the same time as of loading bootloader itself. So, fail-safe firmware+ bootloader are the two-piece of software which should be tested rigorously and must be bug-free before shipping from the factory. fail-safe firmware is the user application with just acceptable functionality which guarantees to run in case of total blackout. Its resides inside the internal flash of microcontroller and when required, it is copied to the user application area of the flash, from there it can boot.
BOOT procedure is called when the bootloader is ready to hand over its controller of the device to user application. It is the last function which is executed by the bootloader till system reset.
Retry is the collection of some variables which reside in a special section of SRAM, which can be accessed by bootloader only (can be accessed by user application also, but not required). Data inside this section survive soft resets. So the bootloader keep a retry counter (a Uin32 sized variable) inside this section of ram, which helps the bootloader to track the no. of times a user application crashed. Having that stats bootloader can spot the bad firmware.
Okay…. Now lets checkout address mapping scheme inside the internal flash of the microcontroller.

We are only interested in the main flash memory part highlighted in yellow with starting address 0x0800 0000. Though, the address scheme is totally dependent on the manufacturer and you will have to follow reference manual provided by microcontroller’s manufacturer. besides that we will also understand the role of SRAM in the process of bootloader design (not highlighted but, you can spot with address 0x20000000).
Let start with some basic understanding about the boot sequence in a typical microcontroller.
As soon as you power on the device, cpu inside the microcontroller look for the vector table and depending on you microcontroller’s configuration (usually a physical boot pin in STM32), the search for vector can endup in one of the may available memory option like, Flash, ROM, SRAM, sometimes secondary SRAM, etc. But almost every microcontroller end-up with Flash memory by default unless configuration is altered by the user. The vector table is a list of 32 bit wide addresses with many entries depending on the architecture. Each position (or index) in the table refer to special address. so let’s have a look at one such table below.

In above picture, Vector table starts bottom to top. First entry in the table is the address of the beginning of the stack which happens to be in SRAM and the second entry is the address of reset function (usually in Flash). You should really understand this table if you want to have good understanding of boot procedure in almost any cpu in the world.
Once cpu locates the vector table and both address, it load the first address into MSP (master stack pointer) and the second address into PC (program counter). Now cpu jump to PC and start executing instructions from there (which is running the reset function, usually written in assembly). I would rather not go in great detail (may be in another post) about what happens in reset function but I would still like to summarise thing in bullet below.
- Copy initialised variables from Flash to SRAM.
- Copy zeros to SRAM for non initialised variables.
- Jump to main();
Now that we understand how a microcontroller boot. We can continue with bootloader memory organization in Flash.
In STM32F070, we have the flash memory of 128KB and 128KB is broken into 64 pages with the size of each page being 2KB. (if you don’t know about flash, then please read Wikipedia)
We are going to reserve first 10 pages (20KB) for bootloader itself and remaining flash (128KB-20KB =108KB) is available for fail-safe as well as main firmware. For example if the size of fail-safe firmware (firmware to run in case of failure of main firmware) is 40KB then last 20 pages can be used to store fail-safe firmware and remaining 68 KB can be used for main firmware.

Figure 5, Represent things visually in more detail. Main Firmware partition has INF block at it start. This block hold some details (meta information) like Size of the Firmware, CRC of the Firmware and a Signature which is just a fixed number like 0x565A, and it helps bootloader recognize the validity of meta INF block. Because, it is not necessary the size of the firmware will be equal to its partition size (smaller or equal to the size of partition). It is important to store the size of the firmware. It will help the firmware calculate CRC and copy firmware from one partition to another.
Now let’s understand the bootloader’s Flow Diagram (Figure 1)
Now, That you have understood things like, boot procedure, vector table and flash partition. Let’s join everything together and understand the bootloader working with the help of Figure 1.

As soon as, The device turns on, it jump to reset function of bootloader where it jumps again to main function after coping data into SRAM. In main function, it initialises the External Flash memory (see figure 2) and Runs the state machine. In state machine, it check for any update available in external flash. If it finds the update it copies the candidate firmware from external flash into “Main Firmware Partition” of internal flash and mark the update in external flash as copied so that the next time it doesn’t copy the same update again. Once the candidate firmware has been copied. It initializes the retry context (retry counter = 0) and continues the state machine from start.
Entering the state machine loop again it rechecks for any update available and as it has already copied it earlier so, this time it doesn’t find any update in external flash and bootloader continue for next check.
Bootloader next checks if retry context is valid. For a retry context to be valid it has a variable called “signature”, which should always be equal to a fixed magic number. If that signature is not valid then, it means that the bootloader has never initialized the retry variable and retry variable contains a garbage value. If the value of retry variable is not valid then it initializes the retry variable to zero and Boot the application.
If retry value is valid and less than the maxmium no of allowed retry, then it increment the retry variable by 1 and Boots the application.
Now here comes the interesting part, if the retry variable count exceeds the maximum no of allowed retry, then bootloader will assume that the main firmware is faulty and it copies the fail-safe firmware from fail-safe partition to main-firmware partition after earsing the main-firmware partition and as well as initializes the retry variable to zero and state machine starts from begining.
Boot the application …
Booting the application involves couple of actions, and they are listed below in bullets.
- Initialize the watchdog.
- Remap the vector table — because cpu currently points to the vector table of the bootloader. We have to remap it to point the cpu to new vector table of application. (see the code for implementation).
- set the MSP (master stack pointer) from new vector table.
- and Jump to new PC (program counter).
How bootloader spot the bad firmware ?
As soon as, application boots, the application firmware must feed the bootloader on the regular time interval (max 6s). If it fails to do so, then watchdog will reset the controller and bootloader will kick back in, once the bootloader state machine starts again it will check for retry variable count before booting the main firmware again. If this phenomena of reseting microcontroller by watchdog happen more than the maximum allowed value (max retry count), then the firmware is spotted BAD.
You can checkout the source code — (Here)
