2020-01-16 03:26 AM
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?
Solved! Go to Solution.
2020-01-22 09:54 AM
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.
2020-01-22 10:58 AM
So a generic bootloader would look schematically like this:
Whereas I'm dealing with a bootloader which looks schematically like this:
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
2020-01-23 01:04 AM
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.
2020-01-23 01:06 AM
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.
2020-01-25 12:29 PM
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.
2020-02-18 01:16 AM
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) }
}
2020-02-18 01:51 AM
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) */
2020-02-18 03:14 AM
@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)
2020-02-18 05:13 AM
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).
Then put a breakpoint on the application reset handler, main, etc to see how far it gets.
2020-02-18 09:43 AM
@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?