Hi everyone,
I’m working on a custom bootloader for an STM32H750 and need some help. My goal is to be able to update the unit from USB and for doing so I use an external eeprom to store the MCU FW and load it into RAM at startup. The main reason to use the RAM to execute the code is that the h750 flash is 128KB and it can only be erased by 128KB sector. I can succesfully write the code into the eeprom and load into ram at startup (address 0x20000000). However, when I try to jump to it, the debugger shows a 0x2xxxxxxx address but it get stuck.
For the application code, I took the original flash based code, modified the scatter file for the linker as follows:
; ************************************************************* ; *** Scatter-Loading Description File generated by uVision *** ; ************************************************************* LR_IROM1 0x20000000 0x00020000 { ; load region size_region ER_IROM1 0x20000000 0x00020000 { ; load address = execution address *.o (RESET, +First) *(InRoot$$Sections) .ANY (+RO) .ANY (+XO) } RW_IRAM2 0x24020000 0x00080000 { ; RW data .ANY (+RW +ZI) } }
I also tried setting the VTOR to 0x20000000 in the application, but it didn’t help. Looking at the .bin file, the stack pointer (SP) and program counter (PC) appear to point to valid addresses within the 0x20000000 range.Here is the part of the code which is supposed to jump to the app.
#define APP_BASE_RAM (0x20000000) uint32_t *p; void (*jmp)(); ---------- ..... __disable_irq(); p = (uint32_t *)(APP_BASE_RAM); SCB->VTOR = (uint32_t)p; // Set Vector Table Offset Register (VTOR) to RAM base __set_MSP(*p); __DSB(); // Data Synchronization Barrier __ISB(); // Instruction Synchronization Barrier jmp = (void (*)())p[1]; __enable_irq(); while (true) ;
I’m not sure if there’s something I’m missing in the application code or bootloader setup. Has anyone encountered a similar issue or have suggestions on what I could check next?
Thanks in advance!
Did you read this knowledgebase article?
https://developer.arm.com/documentation/ka002218/latest/
It should explain in detail what you need to do before starting the application and how to start it.
Hello Hans Schneebauer thanks for your reply. I went through the document and my code seems to implement correctly the procedure. To test the jump I tried to set the address to the bootloader address (0x08000000) and it correctly jumps and re-execute it. When I set the RAM address from where I want to execute the application code, 0x20000000, it doesnt. I wonder if the issue is to be found in the memory allocation.My bootloaded linker script is:
; ************************************************************* ; *** Scatter-Loading Description File generated by uVision *** ; *************************************************************
LR_IROM1 0x08000000 0x00020000 { ; load region size_region ER_IROM1 0x08000000 0x00020000 { ; load address = execution address *.o (RESET, +First) *(InRoot$$Sections) .ANY (+RO) .ANY (+XO) } RW_IRAM2 0x24020000 0x00080000 { ; RW data .ANY (+RW +ZI) } }
My application linker:
LR_IROM1 0x20000000 0x00020000 { ; load region size_region ER_IROM1 0x20000000 0x00020000 { ; load address = execution address *.o (RESET, +First) *(InRoot$$Sections) .ANY (+RO) .ANY (+XO) } RW_IRAM2 0x24020000 0x00080000 { ; RW data .ANY (+RW +ZI) } }
Hello, any idea? I changed the memory allocation to make sure there is no overlap in the RAM between bootloader and application. But result is the same.
Bootloader:
LR_IROM1 0x08000000 0x00020000 { ; load region size_region ER_IROM1 0x08000000 0x00020000 { ; load address = execution address *.o (RESET, +First) *(InRoot$$Sections) .ANY (+RO) .ANY (+XO) } RW_IRAM2 0x24040000 0x00040000 { ; RW data .ANY (+RW +ZI) } }
Application:
LR_IROM1 0x20000000 0x00020000 { ; load region size_region ER_IROM1 0x20000000 0x00020000 { ; load address = execution address *.o (RESET, +First) *(InRoot$$Sections) .ANY (+RO) .ANY (+XO) } RW_IRAM2 0x24020000 0x00040000 { ; RW data .ANY (+RW +ZI) } }
Hi Matteo,
what is the optimization level of your compiler?
Hi Milorad Cvjetkovic compiler settings are Level 3, Optmize for time. I unchecked Optimize for time and set Level to Default. No change.
I have seen a problem when the optimization was O0, the code used stack for information where to jump and then after__set_MSP that information was wrong as stack pointer was changed.Currently, my suggestion is to debug the code and see what happens.
thanks Milorad Cvjetkovic . When I use the keil debugger and I step through the code after reaching the jmp() instruction, the debugger gets stuck at an address in the 0x20000xxxx range but I cannot debug what a happens before. I expect the debugger to jump to the reset handler after jmp() but somehow it doesnt. no idea why
I see now.
Well in your code you are actually not jumping to anywhere as you have not called jmp(); function.You just assigned it an address with jmp = (void (*)())p[1];but you haven't actually jumped.
jmp = (void (*)())p[1];
my bad, that is a copy paste mistake. after that instruciton I have jmp(); I wish that was the problem :(
Milorad Cvjetkovichere is the correct code: SysTick->CTRL = 0; NVIC->ICER[0] = 0xffffffff; NVIC->ICER[1] = 0xffffffff; NVIC->ICER[2] = 0xffffffff; NVIC->ICER[3] = 0xffffffff; NVIC->ICER[4] = 0xffffffff; NVIC->ICER[5] = 0xffffffff; NVIC->ICER[6] = 0xffffffff; NVIC->ICER[7] = 0xffffffff;
p = (U32 *)(APP_BASE_RAM);
SCB->VTOR = (uint32_t)p; // Set Vector Table Offset Register (VTOR) to RAM base // this doesnt change anything if removed__set_MSP(*p);
jmp = (void (*)())p[1]; jmp();while (true) ;
There are 2 possibilities, either content at 0x20000000 and 0x20000004 is not what you expect, or jmp value after jmp = (void (*)())p[1]; is not what you expect.You should be able to single step through that code and see exactly what goes wrong.
jmp
That’s a good point! If the content at those memory addresses isn’t what’s expected, it could definitely cause issues. Single-stepping through the code should help pinpoint whether the problem is with the stored values or how jmp is being assigned. Have you tried printing out p[1] before the assignment to confirm it holds the right address?
p[1]