cancel
Showing results for 
Search instead for 
Did you mean: 

How do I modify an stm32f439vitx system workbench stm32 project for use with a bootloader.

DCarr.1
Senior

I have an existing stm32f439vitx custom board project which works fine when flashed at address 0x08000000 which I now need to make runnable from an existing bootloader expecting the program to have been flashed at address 0x8008000.

Is it enough to change the linker script ROM origin and length?

26 REPLIES 26

Yes, being compatible with the legacy bootloader makes sense.

If you change the stack pointer of the application to 0x2000FFFC, you'd still have to deal with the stack being in the middle of the data area. But you can fix that in the application, or it might be already taken care of in the application startup code. I don't have system workbench, so you have to verify that.

Locate the Reset_Handler function. It should be in an assembly source file (*.s), and begin with

Reset_Handler:
  ldr   sp, =_estack       /* set stack pointer */

If yours looks like mine, then there is nothing to do, the bootloader can put any junk into sp, the application overwrites it with the right value right at the start. If the ldr sp line is not there, then simply insert it.

Now arrange the Reset_Handler to start at 0x08008200. Find the .section directive a few lines above.

  .section  .text.Reset_Handler
  .weak  Reset_Handler
  .type  Reset_Handler, %function
Reset_Handler:
  ldr   sp, =_estack       /* set stack pointer */

there is the name of the section it'll be put into. Change it to something else, so it won't get mixed together with other segments named .text*

.section  .startup

and create a matching section in the linker script, right after the vector table

.isr_vector :
{
  . = ALIGN(4);
  KEEP(*(.isr_vector)) /* vector table actually */
  . = ALIGN(4);
} >FLASH
.startup
{
  . = 0x08008200;
  KEEP(*(.startup)) /* Startup code */
  . = ALIGN(4);
} >FLASH

Now compile it and verify in the .map file, if everything goes right, the Reset_Handler will show up at 0x08008200 instead of somewhere in the middle of the .text section.

The whole flash at 0x08xxxxxx is mirrored to 0x00xxxxxx if the MCU has booted from the internal flash, see Boot configuration in the reference manual. The mapping can be changed in SYSCFG->MEMRMP. I think writing the flash works only through the 0x08... address range.

So a generic bootloader would look schematically like this:

  1. uint32_t sp = *(uint32_t*)0x08008000; // get firmware's stack pointer from the firmware
  2. function_t* reset = *(function_t*)0x08008004; // get firmware's reset address from the firmware
  3.  
  4. asm {
  5. MOVW R0, #(sp & 0x0000ffff)
  6. MOVT R0, #(sp & 0xffff0000)
  7. MOV SP, R0
  8. }
  9.  
  10. reset();

Whereas I'm dealing with a bootloader which looks schematically like this:

  1. uint32_t sp = 0x2000FFFC; // assume the firmware's stack pointer
  2. function_t* reset = (function_t*)0x08008200; // assume the firmware's reset address
  3. asm {
  4. MOVW R0, #(sp & 0x0000ffff)
  5. MOVT R0, #(sp & 0xffff0000)
  6. MOV SP, R0
  7. }
  8. reset();

Wow that is some hackery..

THUMB code needs to be at an odd address, not going to be 0x08008200

Why use in-line assembler, and why just not load the values directly from the variable/register rather than deform into constants/immediates

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..

berendi,

Thanks again, that is really incredibly helpful.

I checked my Reset_Handler handler code and it looks just like yours. I think what I'm going to do is detect the version of the loaded firmware in the bootloader's JumpToApp() function and then call the firmware through either the "generic" or "legacy" method.

I'll let you know how it goes.

clive1,

I dont usually comment on other people's code, but I think in this case the original engineer was learning this stuff as he went along, mostly by copy-pasting from stack exchange questions.

Internet is full of examples with LDR instruction. Not finding it means that he didn't try at all, but copy-pasted some first thing he saw without thinking. Such attitude is despicable and such people will never make anything stable and high quality.

Hello again @berendi​ 

I finally got around to trying yout "arrange the Reset_Handler to start at 0x08008200" solution. I made the modifications you suggested, when I build though, I get the message

Invoking: MCU G++ Linker
 
arm-none-eabi-g++ -mcpu=cortex-m4 -mthumb -mfloat-abi=hard -mfpu=fpv4-sp-d16 -L"C:\Users\david\workspace\divecomputer.eu_hal_lib\Debug" -T"C:/Users/david/workspace/flash/LinkerScript.ld" -Wl,-Map=output.map -Wl,--gc-sections -fno-exceptions -fno-rtti -o "flash.elf" @"objects.list" -ldivecomputer.eu_hal_lib -lm
 
c:/ac6/systemworkbench/plugins/fr.ac6.mcu.externaltools.arm-none.win32_1.17.0.201812190825/tools/compiler/bin/../lib/gcc/arm-none-eabi/7.3.1/../../../../arm-none-eabi/bin/ld.exe: flash.elf section `.mystartup' will not fit in region `ROM'
 
makefile:52: recipe for target 'flash.elf' failed
 
c:/ac6/systemworkbench/plugins/fr.ac6.mcu.externaltools.arm-none.win32_1.17.0.201812190825/tools/compiler/bin/../lib/gcc/arm-none-eabi/7.3.1/../../../../arm-none-eabi/bin/ld.exe: region `ROM' overflowed by 132174580 bytes
 
collect2.exe: error: ld returned 1 exit status

Do you have any idea what might make the linker think that the tiny "mystartup" section is so huge?

My reset handler file, which I'm assuming is very standard stuff, looks like this:

.syntax unified
.cpu cortex-m4
.fpu softvfp
.thumb
 
.global g_pfnVectors
.global Default_Handler
 
/* start address for the initialization values of the .data section. defined in linker script */
.word _sidata
/* start address for the .data section. defined in linker script */
.word _sdata
/* end address for the .data section. defined in linker script */
.word _edata
/* start address for the .bss section. defined in linker script */
.word _sbss
/* end address for the .bss section. defined in linker script */
.word _ebss
 
  .section .mystartup
  .weak Reset_Handler
  .type Reset_Handler, %function
Reset_Handler:
  ldr   r0, =_estack
  mov   sp, r0          /* set stack pointer */
 
/* Copy the data segment initializers from flash to SRAM */
  ldr r0, =_sdata
  ldr r1, =_edata
  ldr r2, =_sidata
  movs r3, #0
  b LoopCopyDataInit
 
CopyDataInit:
  ldr r4, [r2, r3]
  str r4, [r0, r3]
  adds r3, r3, #4
 
LoopCopyDataInit:
  adds r4, r0, r3
  cmp r4, r1
  bcc CopyDataInit
  
/* Zero fill the bss segment. */
  ldr r2, =_sbss
  ldr r4, =_ebss
  movs r3, #0
  b LoopFillZerobss
 
FillZerobss:
  str  r3, [r2]
  adds r2, r2, #4
 
LoopFillZerobss:
  cmp r2, r4
  bcc FillZerobss
 
/* Call the clock system intitialization function.*/
  bl  SystemInit
/* Call static constructors */
  bl __libc_init_array
/* Call the application's entry point.*/
  bl main
 
LoopForever:
    b LoopForever
 
 
.size Reset_Handler, .-Reset_Handler
 
    .section .text.Default_Handler,"ax",%progbits
Default_Handler:
Infinite_Loop:
  b Infinite_Loop
  .size Default_Handler, .-Default_Handler
 
   .section .isr_vector,"a",%progbits
  .type g_pfnVectors, %object
  .size g_pfnVectors, .-g_pfnVectors
 
 
g_pfnVectors:
  .word _estack
  .word Reset_Handler
  .word NMI_Handler
  .word HardFault_Handler
  .word	MemManage_Handler
  .word	BusFault_Handler
  .word	UsageFault_Handler
  .word	0
  .word	0
  .word	0
 
[numerous similar lines snipped]
 
  .word	SAI1_IRQHandler /* SAI1 global interrupt */
  .word	LCD_TFT_IRQHandler /* LTDC global interrupt */
  .word	LCD_TFT_1_IRQHandler /* LTDC global error interrupt */
  .word	DMA2D_IRQHandler  /* DMA2D global interrupt */
 
 
  	.weak	NMI_Handler
	.thumb_set NMI_Handler,Default_Handler
 
  	.weak	HardFault_Handler
	.thumb_set HardFault_Handler,Default_Handler
 
  	.weak	MemManage_Handler
	.thumb_set MemManage_Handler,Default_Handler
 
  	.weak	BusFault_Handler
	.thumb_set BusFault_Handler,Default_Handler
 
[numerous similar lines snipped]
 
	.weak	LCD_TFT_IRQHandler
	.thumb_set LCD_TFT_IRQHandler,Default_Handler
	
	.weak	LCD_TFT_1_IRQHandler
	.thumb_set LCD_TFT_1_IRQHandler,Default_Handler
	
	.weak	DMA2D_IRQHandler
	.thumb_set DMA2D_IRQHandler,Default_Handler
	
	.weak	SystemInit

And my linker script like this:

/* Entry Point */
ENTRY(Reset_Handler)
 
/* Highest address of the user mode stack */
_estack = 0x20030000;    /* end of RAM */
 
_Min_Heap_Size = 0;      /* required amount of heap  */
_Min_Stack_Size = 0x400; /* required amount of stack */
 
/* Memories definition */
MEMORY
{
  RAM (xrw)		: ORIGIN = 0x20000000, LENGTH = 192K
  ROM (rx)		: ORIGIN = 0x08008000, LENGTH = 2040K
}
 
/* Sections */
SECTIONS
{
  /* The startup code into ROM memory */
  .isr_vector :
  {
    . = ALIGN(4);
    KEEP(*(.isr_vector)) /* Startup code */
    . = ALIGN(4);
  } >ROM
  /* The reset handler code where the bootloader is expecting it */
  .mystartup :
  {
    . = 0x08008200;
    KEEP(*(.mystartup)) /* Startup code */
    . = ALIGN(4);
  } >ROM
  /* The program code and other data into ROM memory */
  .text :
  {
    . = ALIGN(4);
    *(.text)           /* .text sections (code) */
    *(.text*)          /* .text* sections (code) */
    *(.glue_7)         /* glue arm to thumb code */
    *(.glue_7t)        /* glue thumb to arm code */
    *(.eh_frame)
 
    KEEP (*(.init))
    KEEP (*(.fini))
 
    . = ALIGN(4);
    _etext = .;        /* define a global symbols at end of code */
  } >ROM
 
  /* Constant data into ROM memory*/
  .rodata :
  {
    . = ALIGN(4);
    *(.rodata)         /* .rodata sections (constants, strings, etc.) */
    *(.rodata*)        /* .rodata* sections (constants, strings, etc.) */
    . = ALIGN(4);
  } >ROM
 
  .ARM.extab   : { 
  	. = ALIGN(4);
  	*(.ARM.extab* .gnu.linkonce.armextab.*)
  	. = ALIGN(4);
  } >ROM
  
  .ARM : {
    . = ALIGN(4);
    __exidx_start = .;
    *(.ARM.exidx*)
    __exidx_end = .;
    . = ALIGN(4);
  } >ROM
 
  .preinit_array     :
  {
    . = ALIGN(4);
    PROVIDE_HIDDEN (__preinit_array_start = .);
    KEEP (*(.preinit_array*))
    PROVIDE_HIDDEN (__preinit_array_end = .);
    . = ALIGN(4);
  } >ROM
  
  .init_array :
  {
    . = ALIGN(4);
    PROVIDE_HIDDEN (__init_array_start = .);
    KEEP (*(SORT(.init_array.*)))
    KEEP (*(.init_array*))
    PROVIDE_HIDDEN (__init_array_end = .);
    . = ALIGN(4);
  } >ROM
  
  .fini_array :
  {
    . = ALIGN(4);
    PROVIDE_HIDDEN (__fini_array_start = .);
    KEEP (*(SORT(.fini_array.*)))
    KEEP (*(.fini_array*))
    PROVIDE_HIDDEN (__fini_array_end = .);
    . = ALIGN(4);
  } >ROM
 
  /* Used by the startup to initialize data */
  _sidata = LOADADDR(.data);
 
  /* Initialized data sections into RAM memory */
  .data : 
  {
    . = ALIGN(4);
    _sdata = .;        /* create a global symbol at data start */
    *(.data)           /* .data sections */
    *(.data*)          /* .data* sections */
 
    . = ALIGN(4);
    _edata = .;        /* define a global symbol at data end */
  } >RAM AT> ROM
 
  
  /* Uninitialized data section into RAM memory */
  . = ALIGN(4);
  .bss :
  {
    /* This is used by the startup in order to initialize the .bss secion */
    _sbss = .;         /* define a global symbol at bss start */
    __bss_start__ = _sbss;
    *(.bss)
    *(.bss*)
    *(COMMON)
 
    . = ALIGN(4);
    _ebss = .;         /* define a global symbol at bss end */
    __bss_end__ = _ebss;
  } >RAM
 
  /* User_heap_stack section, used to check that there is enough RAM left */
  ._user_heap_stack :
  {
    . = ALIGN(8);
    PROVIDE ( end = . );
    PROVIDE ( _end = . );
    . = . + _Min_Heap_Size;
    . = . + _Min_Stack_Size;
    . = ALIGN(8);
  } >RAM
 
  
 
  /* Remove information from the compiler libraries */
  /DISCARD/ :
  {
    libc.a ( * )
    libm.a ( * )
    libgcc.a ( * )
  }
 
  .ARM.attributes 0 : { *(.ARM.attributes) }
}

Try with this in the linker file

/* Sections */
SECTIONS
{
  /* The startup code into ROM memory */
  .isr_vector :
  {
    . = ALIGN(4);
    KEEP(*(.isr_vector)) /* Startup code */
    . = 0x200; /* apparently this should be relative to the section start */
    KEEP(*(.mystartup))   /* The reset handler code where the bootloader is expecting it */
    . = ALIGN(4);
  } >ROM
  /* The program code and other data into ROM memory */
  .text :
  {
    . = ALIGN(4);
    *(.text)           /* .text sections (code) */
    *(.text*)          /* .text* sections (code) */

@berendi​ 

Fantastic stuff! The linker .map is showing the mystartup section with Reset_Handler at 0x08008200

It still runs fine in the debugger, although not from the bootloader, but I'm guessing that that's pretty much my fault at this point.

0x20030000                _estack = 0x20030000
                0x00000000                _Min_Heap_Size = 0x0
                0x00000400                _Min_Stack_Size = 0x400
 
.isr_vector     0x08008000      0x250
                0x08008000                . = ALIGN (0x4)
 *(.isr_vector)
 .isr_vector    0x08008000      0x1ac startup/startup_stm32.o
                0x08008000                g_pfnVectors
                0x00000200                . = 0x200
 *fill*         0x080081ac       0x54 
 *(.mystartup)
 .mystartup     0x08008200       0x50 startup/startup_stm32.o
                0x08008200                Reset_Handler
                0x08008250                . = ALIGN (0x4)
 
.text           0x08008280    0x12dec
                0x08008280                . = ALIGN (0x4)

Start the bootloader from the debugger.

Find the debugger reset vector, which is stored at 0x08000004, and use the value found there as the start address in the debug configuration dialog (hope it's there in system workbench too).

0690X00000DBnpgQAD.png

Then put a breakpoint on the application reset handler, main, etc to see how far it gets.

@berendi​ Thanks a huge amount for your help - I'm starting to feel a bit guilty about using your time.

So. Before the bootloader even thinks about jumping to the application it does a few checks:

As part of the flash programming process a special fingerprint sequence is written to the end of the flash.

0x081FFE00 1C D4 00 00 C1 CF AA 55 

The bootloader then executes

unsigned char lobyte = *(unsigned char *)(0x081ffe00 + 6);
unsigned char hibyte = *(unsigned char *)(0x081ffe00 + 7);

To recover and check the "AA" at 0x081FFE06 and the "55" at 0x081FFE07. This test fails. However, I can manually confirm that these actually are present by examining the memory myself using the stm link utility.

What possible reason could there be for failing to read the contents of the flash?