INDIA +91 964 309 2571 | USA +1 650 405 4891 info@navyuginfo.com

Need for Custom Bootloader

Like every software, embedded firmware written can have bugs, especially in cases where we are doing rapid development. The extra challenge in the field of IoT is that the devices on which firmware is installed can be anywhere in the world, or maybe in space?

So we need the ability to update the firmware on these devices remotely, for this we write a piece of code which is robust (and never changes) and is programmed only once on the devices and this program is first to run after system reset, also this program is responsible for updating the firmware of the device. This program is called bootloader.

Architecture Of Bootloader

In this post, we will be using STM32F072, an ARM cortex m0 microcontroller with 128K flash. Below is the memory map of this microcontroller.
After a reset, this microcontroller fetches instructions from address 0x0000 0000. This MCU has the capability to remap address 0x0000 0000 to the flash, RAM or system memory which is the embedded bootloader. This means that if the RAM is remapped to 0x0000 0000, accessing address 0x0000 1000 is the same as accessing address 0x2000 1000. After a reset, by default, internal flash is remapped to 0x0000 0000. If no data is programmed in the first address, the microcontroller will remap system memory and then execute internal bootloader automatically. Internal flash memory starts at address 0x08000000, so for our bootloader to be able to execute first after reset it needs to be located at address 0x08000000 which is the beginning of flash. Hence we will be configuring our IDE to link bootloader code at this address.

Below is the image describing how we will be distributing the flash among bootloader and application firmware.
First 32KB starting from 0x08000000 we will be reserving for the bootloader. Next 16KB from 0x08008000 to 0x0800c000 we will be using to store some configuration flags like application firmware start address, version of current firmware etc. Now here is something different, we will be using two different address spaces for application firmware (Firmware 1 and Firmware 2). We will be doing so to avoid the need of external flash/storage. And our flow will be something like this:

  • If currently, the application firmware is in Firmware 1 area then we will download new firmware in Firmware 2 area, and vice versa.
  • After successfully writing new firmware at the respective location, we will set the appropriate values of configuration flags so that bootloader executes the new firmware.

One drawback of using two different address space for application firmware is that we will have to manage application firmware for two different address space and the device should download the appropriate address space firmware.

Restricting Address Space of Bootloader In Keil

We will be using first 32 KB of address space starting from 0x08000000 for the bootloader. To configure this right click on root folder in the project and click Options for Target .
In the Target tab set Start value of IROM1 to 0x08000000 and Size to 0x8000 and click OK.
Now linker will use first 32KB of address space for bootloader.

Invoking Application Firmware from Bootloader

To invoke application firmware from bootloader following step are needed:

  • Disable all the interrupts.
  • Set main stack pointer to start of application firmware (0x0800c000 or 0x08018000 in our case)
  • Invoke the function at address pointed by start of application firmware + 4

Below is a code snippet which invokes the application firmware starting at address application.

if (checkApplicationAddress((*(__IO uint32_t*)APPLICATION_START_ADDR_PTR)))
{
   __disable_irq(); 
   // Jump to user application
   JumpAddress = *(__IO uint32_t*) (ApplicationAddress + 4);
   Jump_To_Application = (pFunction) JumpAddress;
   // Initialize user application's Stack Pointer
   __set_MSP(*(__IO uint32_t*) ApplicationAddress);
   Jump_To_Application();
}

Things to take care in application firmware

Interrupt Vector Table

The first thing which we need to take care of in application firmware is to update the Interrupt Vector Table which is first 192 bytes of memory starting from 0x00000000 with first 192 bytes of application firmware. Vector table starts from address 0x00000000, to change it on Cortex M0 devices we copy the Vector Table from Flash to RAM starting from address 0x20000000. Then we use SYSCFG_MemoryRemapConfig to copy the values at address 0x00000000 from embedded SRAM starting at 0x20000000. Below is a code snippet to reconfigure vector table with that of firmware starting at FIRMWARE_START_ADDR.

void Remap_Table(void)
{
 // Copy interrupt vector table to the RAM.
 volatile uint32_t *VectorTable = (volatile uint32_t *)0x20000000;
 uint32_t ui32_VectorIndex = 0;
 for(ui32_VectorIndex = 0; ui32_VectorIndex < 48; ui32_VectorIndex++)
 {
   VectorTable[ui32_VectorIndex] = *(__IO uint32_t*)((uint32_t)FIRMWARE_START_ADDR + (ui32_VectorIndex << 2));
 }
 //  Enable SYSCFG peripheral clock
 RCC_APB2PeriphResetCmd(RCC_APB2Periph_SYSCFG, ENABLE);
 // Remap RAM into 0x0000 0000
 SYSCFG_MemoryRemapConfig(SYSCFG_MemoryRemap_SRAM);
}

Address Space (Flash and RAM)

We will have to configure the address space of application firmware just like a bootloader. For firmware 1 address space set IROM1 Start address to 0x0800C000 and Size to 0x8000. Also since first 192 bytes of RAM, we will be using to map the vector table, therefore, we need to make sure that it is not used by application firmware so set the IRAM1 Start value to 0x200000C4 and Size to 0x3F3C.
For firmware 2 address space set Start of IROM1 as 0x08018000 and Size to 0x8000.
References
http://marcelojo.org/marcelojoeng/2015/09/bootloader-on-stm32f0.html1
http://www.st.com/en/microcontrollers/stm32f072rb.html

[avatar user=”Ajay Yadav” size=”thumbnail” align=”left” /]