HowTo: STM32 Custom Bootloader/Application

H

It is a common practice in embedded system these days to use a bootloader-application architecture. The following source code snippets will help you to build you own bootloader/application project providing by the minimum code that is required to jump from one to the other. The example also works with FreeRTOS.

First we need to understand 2 simple things. Each binary will be placed in different sectors inside the Flash memory of our microcontroller (STM32 Family). For that reason we need to make sure about the sectors of the memory and the addressing.

For example in a STM32-H743 we can alter the linker script:

//BOOTLOADER
MEMORY
{
DTCMRAM (xrw)      	: ORIGIN = 0x20000000, 	LENGTH = 128K
RAM_D1 (xrw)      	: ORIGIN = 0x24000000, 	LENGTH = 512K
RAM_D2 (xrw)      	: ORIGIN = 0x30000000, 	LENGTH = 288K
RAM_D3 (xrw)      	: ORIGIN = 0x38000000, 	LENGTH = 64K
ITCMRAM (xrw)      	: ORIGIN = 0x00000000, 	LENGTH = 64K
FLASH (rx)      	: ORIGIN = 0x8000000, 	LENGTH = 128K
FLASH1(rx)		: ORIGIN = 0x8020000, 	LENGTH = 2048K - 128K
}

//APPLICATION
MEMORY
{
DTCMRAM (xrw)      	: ORIGIN = 0x20000000, 	LENGTH = 128K
RAM_D1 (xrw)      	: ORIGIN = 0x24000000, 	LENGTH = 512K
RAM_D2 (xrw)      	: ORIGIN = 0x30000000, 	LENGTH = 288K
RAM_D3 (xrw)      	: ORIGIN = 0x38000000, 	LENGTH = 64K
ITCMRAM (xrw)      	: ORIGIN = 0x00000000, 	LENGTH = 64K
FLASH(rx)		: ORIGIN = 0x8020000, 	LENGTH = 2048K - 128K
}

Basically, our bootloader will be placed at 0x8000000 and our application at 0x8020000.

Afterwards, by using the following header/source files we will be able to perform our actual memory addresses jumping :). Those files include the basic functions that we will need to make our examples work. (make sure you change the DVC_APPJMP_CURRENT_ADDRESS according the linker scripts for each case)

#ifndef INC_DVC_APPJMP_H_
#define INC_DVC_APPJMP_H_

#define DVC_APPJMP_CURRENT_ADDRESS 0x8000000

void dvc_appjmp_init_vectors()  __attribute__((optimize("-O0")));
void dvc_appjmp_goto(uint32_t)	__attribute__((optimize("-O0")));
void dvc_appjmp_enable() 	__attribute__((optimize("-O0")));

#endif
#include <string.h>
#include "dvc_appjmp.h"
#include "stm32h7xx_hal.h" // Include the proper header here :)

extern uint32_t g_pfnVectors[];

static uint32_t vectorTable_RAM[256] __attribute__(( aligned(0x200ul) ));
static uint8_t enabled=0;


void dvc_appjmp_init_vectors()
{
	__disable_irq();
	memmove(vectorTable_RAM,g_pfnVectors,256*sizeof(uint32_t));
	SCB->VTOR = 0x8020000;
	__DSB();
	__ISB();
}

void dvc_appjmp_goto(uint32_t app_addr)
{
	if(enabled != 1)
	   return;
	__disable_irq();
	for(int i = 0;i < 8;i++) NVIC->ICER[i] = 0xFFFFFFFF;
	for(int i = 0;i < 8;i++) NVIC->ICPR[i] = 0xFFFFFFFF;
	__set_CONTROL(0);
	__set_MSP(*(__IO uint32_t*)USER_CODE);
	uint32_t JumpAddress = *((volatile uint32_t*) (USER_CODE + 4));
	__ISB();
	__DSB();
	SysTick->CTRL &= ~(SysTick_CTRL_CLKSOURCE_Msk | SysTick_CTRL_ENABLE_Msk);
	void (*reset_handler)(void) = (void*)JumpAddress;
	while(1)
	   reset_handler();
}

void dvc_appjmp_enable()
{
	enabled = 1;
}

Below are a quick example of the main functions (bootloader, application) providing the sequence and the order and the position of the library calls.

// Bootloader
int main(void)
{
    // Initialize the vectors table before the HAL initialization 
    dvc_appjmp_init_vectors();
    HAL_Init();
    SystemClock_Config();
    __enable_irq();
    ...
    ...
    // Enable the jump
    dvc_appjmp_enable();
    ...
    ...
    // According to your program logic, jump to a specific address 
    // (in our case the application address)
    dvc_appjmp_goto(0x8020000);
}
// Application
int main(void)
{
    // Initialize the vectors table before the HAL initialization 
    dvc_appjmp_init_vectors();
    HAL_Init();
    SystemClock_Config();
    __enable_irq();
    ...
    ...
    // Enable the jump
    dvc_appjmp_enable();
    ...
    ...
    // According to your program logic, jump to a specific address 
    // (in our case the bootloader address)
    dvc_appjmp_goto(0x8000000);

As you can see, in our example both bootloader and application are able to jump to each other, normally you could also make more than one application …(ex. Bootloader – Main Application – Fail-Safe Application)

The only limitation is that the dvc_appjmp_goto function must be called outside of any interrupt, in your main program.

Disclaimer: The present content may not be used for training artificial intelligence or machine learning algorithms. All other uses, including search, entertainment, and commercial use, are permitted.

Categories

Tags