I'm trying to write a bootloader for an STM32F4xx (currently for the STM32F429 discovery board). My application should start at address 0x08020000 while the bootloader starts at 0x08000000 (start of flash).

I created the application with CubeMX, changed the FLASH ORIGIN in the linker file to 0x08020000 and the VECT_TAB_OFFSET to 0x20000 in the system_stm32f4xx.c file (this changes the VTOR to 0x08020000). My application just blinks a LED and works as expected at this new address when started from the IDE.

while(true) {
    HAL_GPIO_TogglePin(GPIOG, GPIO_PIN_13);

Instead of writing the bootloader immediately (actually I did that and it didn't work) I used the ST-Link utility and did a few things: I verified that everything, beside the application, is erased (0xff). I reset the CPU and manually set the PC to 0x08021924 (this is the Reset_Handler according to the map file from the application). When I slowly step/run through the application, I can see the following: The SP is corretly written (as this is part of startup assembler code from CubeMX). The VTOR is corretly set to 0x08020000 (I read out the SCB manually at address 0xE000ED00, the 3rd value is the VTOR) and after some time the application loops inside the HAL_GetTick & HAL_Delay function (I read out the PC an compared it to the map file). I even see that the application reads the uwTick (at address 0x20000020 in my case), which is never increased, and therefore the HAL_Delay loops forever. so my conclusion is:

The interrupts seem to not work correctly

in this case the SysTick ... Despite a correct VTOR and the application seems to run fine. When I replace HAL_Delay with a Busy_Delay (which just consists of a loop of millions of NOP), the LED blinks. When I start it manually through the mentioned method (or a manually written bootloader with jumps to the correct address) then the application runs normally (because quite likely nothing depends on interrupts in this short example).

What I'm missing to write a working bootloader (with working interrupts)?

  • \$\begingroup\$ Have you tried searching the STM32 Forum? I'm not sure but I think similar questions have been asked and answered there a number of times. \$\endgroup\$
    – Tut
    Commented Oct 24, 2017 at 18:15
  • 1
    \$\begingroup\$ You're right the forum is full of this kind of questions, but many of them unanswered and was unable to find a solution to my particular problem so far, or some people have similar problems and circumvent it. \$\endgroup\$
    – Freya Thor
    Commented Oct 24, 2017 at 21:53

1 Answer 1


I think I found a missing link myself.

Instead of jumping to the Reset_Handler itself, jumping to the Reset_Handler + 1 helps. This sets the LSB to 1 an gives an indication that it is thumb code. For the IRQ handlers it is documented in the ARM Information Centre.

Most (if not all) jumps in the generated code from C have an LSB 0 of zero (and interrupts still work), I don't understand fully why it is necessary to set the LSB in my case. Perhaps: Normally the jumps are relative (but inside the debugger translated into addresses with an LSB of 0) but the jump to the bootloader is an address which is loaded with a register first and than jumped too.


Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.