Interrupt Vector Relocation: Cortex-M3


Last Update: May 7, 2020 @ 08:40 Information subject to change.

This is another Application Note relevent to using STM32CubeMX and True Studio. All the older Decaf IOC boards with the 72MHz Cortex-M3 (STM32F103 variants) used a DFU loader stored in the bottom of Flash memory (0x0800 0000). The board USB firmware was loaded at 0x0800 3000. All boards have a jumper that, if removed, causes the board to boot into DFU mode: utilities provided the means for users to load updates directly via the USB port. It also means that we don’t have a JTAG or ST-Link connector.

Loading the board firmware at 0x0800 3000 requires the Interrupt Vector Table to be moved upwards by 0x3000. In the older CMSIS firmware, this was done using …

#define InterruptVectorAddress 0x3000
#define ApplicationAddress 0x08003000
 and first in main.c
NVIC_SetVectorTable(NVIC_VectTab_FLASH, InterruptVectorAddress);

In the MX code base, the above does not exist, and the process to achieve the relocation is different. It’s important to set the new vector table address after calling HAL_Init(), because it calls the generated HAL_MspInit(), see below.

In the generated project file system_stm32f1xx.c we find around line #107 and line #220…

#define VECT_TAB_OFFSET  0x00000000U /*!< Vector Table base offset field.
                               This value must be a multiple of 0x200. */
SCB->VTOR = FLASH_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal FLASH. */

Since the file system_stm32f1xx.c is generated by the project, we can change the value of VECT_TAB_OFFSET in firmware code as needed. Note here that we DON’T change the value of VECT_TAB_OFFSET.

In main.c we have a call to HAL_Init(); Function source comments state …

 @brief  This function is used to initialize the HAL Library; it must be the first  instruction to be executed in the main program (before to call any other
 HAL function), it performs the following:
 Configure the Flash prefetch.
 Configures the SysTick to generate an interrupt each 1 millisecond,
 which is clocked by the HSI (at this stage, the clock is not yet
 configured and thus the system is running from the internal HSI at 16 MHz).
 Set NVIC Group Priority to 4.
 Calls the HAL_MspInit() callback function defined in user file
 "stm32f1xx_hal_msp.c" to do the global low level hardware initialization 

Note the call to …

void HAL_MspInit(void)
 {
   __HAL_RCC_AFIO_CLK_ENABLE();
   __HAL_RCC_PWR_CLK_ENABLE();
 __HAL_AFIO_REMAP_SWJ_DISABLE();
 }

What we wanted to do was use the earlier DFU Core code loaded at 0x0800 0000 and create the new firmware (with the Interrupt Vector relocation) that would load at 0x0800 3000. The TS project creates a .hex file that defaults to a base address 0x0800 0000: the ST-Link utility does not allow for the load address to be specified. The .hex file build has to do it via the linker script.

See the generated linker script STM32F103C8_FLASH.ld (name is our project mcu type). At approximately line #70, update

FLASH (rx)      : ORIGIN = 0x8000000, LENGTH = 64K
 to 
FLASH (rx)    : ORIGIN = 0x08003000, LENGTH = 64K-0x3000
This specifies the new load address. For the IOC-M-2, this is 0x0800 3000

So … in main.c we now need

  #define InterruptVectorAddress 0x3000 

  void NVIC_Relocate(void)
  {
      SCB->VTOR = (FLASH_BASE | InterruptVectorAddress);
  }
  int main (void
  { 
       HAL_Init();
       NVIC_Relocate();
       ...
  }
Alternatively, we could use ...
 void HAL_MspInit(void)
 {
   __HAL_RCC_AFIO_CLK_ENABLE();
   __HAL_RCC_PWR_CLK_ENABLE();
   __HAL_AFIO_REMAP_SWJ_DISABLE();

   SCB->VTOR = (FLASH_BASE | InterruptVectorAddress); 
 } 

Comments, clarifications and questions are welcome, see Contact.


Since you are here … do please have a look around our site. These products may be of interest.

Decaf IOC-M-2 Decaf IOC-2xe   Decaf IOC-235  Decaf IOC-280Z-4   Decaf IOC-350   Decaf IOC-370


[xyz-ihs snippet=”PageCopyrightStatement”]



Copyright © 2025 PiXCL Automation Tech Notes, Canada. All Rights Reserved.