I've been following bootloader guide here and trying to write a bootloader that simply turns an LED on when entering a pushbutton-induced performFirmwareUpdate(). I'm using the ARM GNU toolchain in Ubuntu to compile the code, using srec_cat to merge the .hex files of my bootloader and application, and flashing them on the board using stlink and the arm gdb. When I flash the hex file I believe they are going to the correct places in memory because of the output I get from gdb, showing the 2 sections being loaded in the memory locations specified in their linker files. Unfortunately, the main application never gets accessed, even when the pushbutton isn't pressed. I'll attach the relevant portions of the linker files and main code here.

Bootloader linker:

    /* Entry Point */

    /* Highest address of the user mode stack */
    _estack = 0x20017FFF;    /* end of RAM */
    /* Generate a link error if heap and stack don't fit into RAM */
    _Min_Heap_Size = 0x200;;      /* required amount of heap  */
    _Min_Stack_Size = 0x400;; /* required amount of stack */

    /* Specify the memory areas */
    FLASH (rx)     : ORIGIN = 0x08000000, LENGTH = 48K
    RAM (xrw)      : ORIGIN = 0x20000000, LENGTH = 96K

Application linker:

/* Entry Point */

/* Highest address of the user mode stack */
_estack = 0x20017FFF;    /* end of RAM */
/* Generate a link error if heap and stack don't fit into RAM */
_Min_Heap_Size = 0x200;;      /* required amount of heap  */
_Min_Stack_Size = 0x400;; /* required amount of stack */

/* Specify the memory areas */
FLASH (rx)     : ORIGIN = 0x0800C000, LENGTH = 976K
RAM (xrw)      : ORIGIN = 0x20000000, LENGTH = 96K

Bootloader main.c:

#include "stm32l4xx_hal.h"

#define APPLICATION_ADDRESS 0x0800C000

typedef void (*pFunction)(void);

uint32_t checkFirmwareUpdate();
uint32_t performFirmwareUpdate();
void System_Clock_Init(void);
void GPIO_Init(void);
void GPIO_DeInit(void);

int boot_main(void)
    // initialize hardware

    /* Check if a firmware update is required */
    if (checkFirmwareUpdate()) {

        /* Perform the update */

    // de-init gpio

    pFunction appEntry;
    uint32_t appStack;

    /* Get the application stack pointer (First entry in the application vector table) */
    appStack = (uint32_t) *((__IO uint32_t*)APPLICATION_ADDRESS);

    /* Get the application entry point (Second entry in the application vector table) */
    appEntry = (pFunction) *(__IO uint32_t*) (APPLICATION_ADDRESS + 4);

    /* Reconfigure vector table offset register to watch the application bootloader */

    /* Set the application stack pointer */


    /* Infinite loop */
    while (1);

uint32_t checkFirmwareUpdate(){
    // hold down middle joystick button
    //return (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0));
    if (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0)){ return 1; }
    else { return 0; }

uint32_t performFirmwareUpdate(){
    // turn on green LED
    GPIOE->ODR |= (1 << 8);
    return 0;

Application main.c:

#include "stm32l4xx.h"
#include "core_cm4.h"

#define LED_PIN 2
#define LED_TOGGLE() GPIOB->ODR ^= (1 << 2)
#define LED_ON() GPIOB->ODR |= (1 << 2);
#define LED_OFF() GPIOB->ODR &= ~(1 << 2);

volatile int32_t TimeDelay;
void SysTick_Handler (void);
void Delay (uint32_t nTime);

int main() {
    // Enable the clock to GPIO Port B  

    // MODE: 00: Input mode, 01: General purpose output mode
    //       10: Alternate function mode, 11: Analog mode (reset state)
    GPIOB->MODER &= ~(0x03<<(2*2)) ;   // Clear bit 13 and bit 12
    GPIOB->MODER |= (1<<4);

    // select push-pull output
    GPIOB->OTYPER &= ~(1<<2);
    /* Turn on the LED */

    SysTick_Config(10000);  //10 ms reload period

    // toggle LED
    while(1) {
        Delay(100); // wait 1000 ms


void SysTick_Handler (void) {
    if (TimeDelay > 0)

void Delay (uint32_t nTime) {
    TimeDelay = nTime;  // nTime specifies the delay time length
    while(TimeDelay != 0);  // busy wait

void assert_failed(uint8_t* file, uint32_t line)
    /* infinite loop */
    /* use gdb to find out why we're here */
    while (1);

Interestingly when I run a regular srec_cat merge (srec_cat boot.hex -Intel app.hex -Intel -O merge.hex -Intel) I get some warnings for "redundant 0x08000000 value (0xFF)", with additional warnings at addresses 0x08000001-0x08000003. Then the merge fails as I get a "contradictory 0x08000004 value (previous = 0x39, this one = 0x7D)".

To overcome this I add an offset to the application hex of 0x0000C000 (srec_cat boot.hex -Intel app.hex -Intel -offset 0x0000C00 -Output_Block_Size=16 -O merge.hex -Intel)

So at the end of the day, my bootloader code works as it enters performFirmwareUpdate() when I hold the pushbutton down on boot up, but never enters the application as there is no LED blinking. The application works without the bootloader so I've verified the code works.

If I can clear anything up or include anything else, please let me know.

  • \$\begingroup\$ Create a map file to see if all application sections are really at the correct addresses. \$\endgroup\$ – filo Mar 9 '18 at 19:05
  • \$\begingroup\$ @filo how do i do that? \$\endgroup\$ – akivjh Mar 9 '18 at 19:45
  • \$\begingroup\$ Google "gcc map file" \$\endgroup\$ – filo Mar 9 '18 at 20:16
  • \$\begingroup\$ @filo yep, figured it out. The map file for the bootloader shows flash memory going from 0x08000000-0x08001040. The map file for the app shows the flash memory going from 0x0800C000-0x0800C5a8. Anything else I should be looking out for? \$\endgroup\$ – akivjh Mar 9 '18 at 20:22
  • \$\begingroup\$ oh my god I can't believe this, the application linker file in the makefile I was using wasn't pointing to the edited linker file. Didn't need to add an offset after using the correct linker and everything works properly now \$\endgroup\$ – akivjh Mar 9 '18 at 20:33

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.