Starting an STM32 Application from a Custom Flash Address in Keil

Hi everyone,

I’m trying to run and debug my STM32 application from a custom Flash address in Keil, and I’m currently stuck.

I need to reserve around 136 KB of Flash for the bootloader, so my application should start at:

Application start address: 0x08022000

The same bootloader and application setup works correctly in STM32CubeIDE. I can flash and debug the application at both the default address and the custom offset. However, due to project requirements, I need to migrate the project to Keil MDK.

To isolate the issue, I tested with a simple Blinky application:

  • 0x08000000 → Works correctly in Keil
  • 0x08022000 → Flashing/debugging is still not working correctly

So far, I have tried the following:

  1. Modified the scatter file with the required Flash start address and size.
  2. Kept the initial stack pointer at the top of RAM.
  3. Updated the vector table offset:
    #define VECT_TAB_OFFSET 0x22000U
  4. Updated the Flash programming range/settings under:
    Options for Target → Debug → Settings → Flash Download → Programming Algorithm
  5. Verified the generated .map file to check the application placement.

Even after going through many forum posts and trying the suggested solutions, I haven't been able to get this working. While debugging, the PC eventually jumps outside the valid Flash region.

Current behavior

STM32CubeIDE

  • Default Flash address → Flashing and debugging work
  • Custom address 0x08022000 → Flashing and debugging work

Keil MDK

  • Default Flash address 0x08000000 → Works
  • Custom address 0x08022000 → Debugging fails / PC goes outside the expected Flash area

The same bootloader is used in both cases.

I’ve attached screensh

;Scatter file 
LR_IROM1 0x08022000 0x001DE000  {    ; load region size_region
  ER_IROM1 0x08022000 0x001DE000  {  ; load address = execution address
   *.o (RESET, +First)
   *(InRoot$$Sections)
   .ANY (+RO)
   .ANY (+XO)
  }

  RW_IRAM1 0x20000000 0x0009FA00 {  ; RW data
   .ANY (+RW +ZI)
  }    
  ARM_LIB_HEAP 0x2009FA00 EMPTY 0x00000200
  {
  }
  ARM_LIB_STACK 0x2009FC00 EMPTY 0x00000400
  {
  }
}

    Execution Region RW_IRAM1 (Exec base: 0x20000000, Load base: 0x080241e8, Size: 0x00000418, Max: 0x0009fa00, ABSOLUTE)

    Exec Addr    Load Addr    Size         Type   Attr      Idx    E Section Name        Object

    0x20000000   0x080241e8   0x00000004   Data   RW          898    .data.SystemCoreClock  system_stm32h5xx.o
    0x20000004   0x080241ec   0x00000001   Data   RW          856    .data.uwTickFreq    stm32h5xx_hal.o
    0x20000005   0x080241ed   0x00000003   PAD
    0x20000008   0x080241f0   0x00000004   Data   RW          855    .data.uwTickPrio    stm32h5xx_hal.o
    0x2000000c   0x080241f4   0x00000004   PAD
    0x20000010        -       0x00000004   Zero   RW          857    .bss.uwTick         stm32h5xx_hal.o
    0x20000014   0x080241f4   0x00000004   PAD
    0x20000018        -       0x00000400   Zero   RW            3    STACK               startup_stm32h563xx.o


    Execution Region ARM_LIB_HEAP (Exec base: 0x2009fa00, Load base: 0x080241f8, Size: 0x00000200, Max: 0x00000200, ABSOLUTE)

    Exec Addr    Load Addr    Size         Type   Attr      Idx    E Section Name        Object

    0x2009fa00        -       0x00000200   Zero   RW            1    ARM_LIB_HEAP.bss    anon$$obj.o


    Execution Region ARM_LIB_STACK (Exec base: 0x2009fc00, Load base: 0x080241f8, Size: 0x00000400, Max: 0x00000400, ABSOLUTE)

    Exec Addr    Load Addr    Size         Type   Attr      Idx    E Section Name        Object

    0x2009fc00        -       0x00000400   Zero   RW            2    ARM_LIB_STACK.bss   anon$$obj.o
    
    
    
    
        SystemCoreClock                          0x20000000   Data           4  system_stm32h5xx.o(.data.SystemCoreClock)
    uwTickFreq                               0x20000004   Data           1  stm32h5xx_hal.o(.data.uwTickFreq)
    uwTickPrio                               0x20000008   Data           4  stm32h5xx_hal.o(.data.uwTickPrio)
    uwTick                                   0x20000010   Data           4  stm32h5xx_hal.o(.bss.uwTick)
    __initial_sp                             0x20000418   Data           0  startup_stm32h563xx.o(STACK)
    Image$$ARM_LIB_HEAP$$ZI$$Base            0x2009fa00   Number         0  anon$$obj.o ABSOLUTE
    Image$$ARM_LIB_HEAP$$ZI$$Limit           0x2009fc00   Number         0  anon$$obj.o ABSOLUTE
    Image$$ARM_LIB_STACK$$ZI$$Limit          0x200a0000   Number         0  anon$$obj.o ABSOLUTE



==============================================================================

Memory Map of the image

  Image Entry point : 0x0802224d

  Load Region LR_IROM1 (Base: 0x08022000, Size: 0x000021f8, Max: 0x001de000, ABSOLUTE)

    Execution Region ER_IROM1 (Exec base: 0x08022000, Load base: 0x08022000, Size: 0x000021e4, Max: 0x001de000, ABSOLUTE)

    Exec Addr    Load Addr    Size         Type   Attr      Idx    E Section Name        Object

    0x08022000   0x08022000   0x0000024c   Data   RO            5    RESET               startup_stm32h563xx.o
    0x0802224c   0x0802224c   0x00000008   Code   RO          913  * !!!main             c_w.l(__main.o)
    0x08022254   0x08022254   0x00000034   Code   RO         1098    !!!scatter          c_w.l(__scatter.o)
    
    
        __Vectors_Size                           0x0000024c   Number         0  startup_stm32h563xx.o ABSOLUTE
    __Vectors                                0x08022000   Data           4  startup_stm32h563xx.o(RESET)
    __Vectors_End                            0x0802224c   Data           0  startup_stm32h563xx.o(RESET)
    __main                                   0x0802224d   Thumb Code     8  __main.o(!!!main)
ots of my Keil target settings, scatter file, and relevant parts of the .map file.

Could anyone share a complete checklist for running an STM32 application from a custom Flash address in Keil?

Also, is there any important difference in how STM32CubeIDE and Keil handle the application entry point, vector table, scatter file, or debugger startup that could explain why the CubeIDE setup works while the equivalent Keil setup does not?

Any pointers on what I might be missing would be really helpful. Thanks!