Hello, I am trying to build a custom bootloader which is jumping in the application. In order to do this in a first approach, i wrote a Bootloder which jumps as follows:
For the application I changed the VET_TAB_OFFSET to 0x000A0000UL.
In Keil Settings Target I changed the Read/only Memory Areas:
IROM1 : Start 0x80A000 Size 0x60000
In the Target Options I unchecked Use Memory Layout from Target Dialog
For R/O Base I put 0x080A0000
My scatter file for the application:
LR_IROM1 0x080A0000 0x00060000 { ; load region size_region ER_IROM1 0x080A0000 0x00060000 { ; load address = execution address *.o (RESET, +First) *(InRoot$$Sections) .ANY (+RO) .ANY (+XO) } RW_IRAM1 0x20000000 0x00020000 { ; RW data .ANY (+RW +ZI) } RW_IRAM2 0x24000000 0x00080000 { .ANY (+RW +ZI) } } LR_IROM2 0x08100000 0x00100000 { ER_IROM2 0x08100000 0x00100000 { ; load address = execution address .ANY (+RO) } }
For the Bootloader
; ************************************************************* ; *** Scatter-Loading Description File generated by uVision *** ; ************************************************************* LR_IROM1 0x08000000 0x000A0000 { ; load region size_region ER_IROM1 0x08000000 0x00A0000 { ; load address = execution address *.o (RESET, +First) *(InRoot$$Sections) .ANY (+RO) .ANY (+XO) } RW_IRAM1 0x20000000 0x00020000 { ; RW data .ANY (+RW +ZI) } RW_IRAM2 0x24000000 0x00080000 { .ANY (+RW +ZI) } } LR_IROM2 0x08100000 0x00100000 { ER_IROM2 0x08100000 0x00100000 { ; load address = execution address .ANY (+RO) } }
I use Keil with the MDK Plus Toolchain on the STM32H753.
I do the jump on the bootloader as follows and nothing happens:
void start_app() { uint32_t JumpAddress = *(volatile uint32_t*) (JUMP_ADDR+4); void (*jump_to_application)(void)= (void*) JumpAddress; HAL_RCC_DeInit(); __disable_irq(); __set_MSP(*(__IO uint32_t*) JUMP_ADDR); // Disable systick timer and reset it to default values SysTick->CTRL = 0; SysTick->LOAD = 0; SysTick->VAL = 0; //__disable_irq(); //__DSB(); SCB->VTOR = JUMP_ADDR; jump_to_application(); }
What could be the reason?
Hi Jan.
One situation I have seen was that, if Compiler used no optimization, it actually kept local variables on stack, and when you change MSP then all those variables actually are not there anymore so jump to address which was on stack would be actually jump to unknown (whatever is on the new stack at that location, probably 0).Anyways, you should be able to debug your application and will then be able to find out the root cause.
Best regards, Milorad
There are more things to consider before jumping from the bootloader to the application. Did you already see this knowlegebase article:
https://developer.arm.com/documentation/ka002218/latest/
Thanks for this valuable information. I try to deinit everything involved in interrupt generation. I am not shure if I cleared all pending interrupts. However, after the jump, my bootloaders just resets and starts again.
__attribute__( ( naked, noreturn ) ) void BootJumpASM( uint32_t SP, uint32_t RH ) { __asm("MSR MSP,r0"); __asm("BX r1"); } void EnablePrivilegedMode( ){ //AP_CSW } static void BootJump( uint32_t *Address ) { if( CONTROL_nPRIV_Msk & __get_CONTROL( ) )//Make sure, the CPU is in privileged mode. SuperVisor Call (SVC) instruction is triggered { /* not in privileged mode */ EnablePrivilegedMode( ); } /*Disable all enabled interrupts in NVIC.*/ 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; /*Disable all enabled peripherals which might generate interrupt requests, and clear all pending interrupt flags in those peripherals. Because this is device-specific, refer to the device datasheet for the proper way to clear these peripheral interrupts.*/ dmaDisable(); extiInterruptDisable(); fdcanDisable();//interrupts deactivated in cubemx adcDisable();//interrupts deactivated in cubemx HAL_PWREx_DisableUSBVoltageDetector(); HAL_UART_DeInit(&huart8); HAL_UART_DeInit(&huart1); HAL_UART_DeInit(&huart3); HAL_UART_DeInit(&huart6); HAL_UART_DeInit(&huart2); /*Clear all pending interrupt requests in NVIC.*/ NVIC->ICPR[ 0 ] = 0xFFFFFFFF; NVIC->ICPR[ 1 ] = 0xFFFFFFFF; NVIC->ICPR[ 2 ] = 0xFFFFFFFF; NVIC->ICPR[ 3 ] = 0xFFFFFFFF; NVIC->ICPR[ 4 ] = 0xFFFFFFFF; NVIC->ICPR[ 5 ] = 0xFFFFFFFF; NVIC->ICPR[ 6 ] = 0xFFFFFFFF; NVIC->ICPR[ 7 ] = 0xFFFFFFFF; /*Disable SysTick and clear its exception pending bit, if it is used in the bootloader, e. g. by the RTX.*/ SysTick->CTRL = 0 ; SCB->ICSR |= SCB_ICSR_PENDSTCLR_Msk; /*Disable individual fault handlers if the bootloader used them.*/ SCB->SHCSR &= ~( SCB_SHCSR_USGFAULTENA_Msk | \ SCB_SHCSR_BUSFAULTENA_Msk | \ SCB_SHCSR_MEMFAULTENA_Msk ) ; /*Activate the MSP, if the core is found to currently run with the PSP. As the compiler might still use the stack, the PSP needs to be copied to the MSP before this.*/ if( CONTROL_SPSEL_Msk & __get_CONTROL( ) ) { /* MSP is not active */ __set_MSP( __get_PSP( ) ) ; __set_CONTROL( __get_CONTROL( ) & ~CONTROL_SPSEL_Msk ) ; } /*Load the vector table address of the user application into SCB->VTOR register. Make sure the address meets the alignment requirements.*/ //A few device families, like the NXP 4300 series, will also have a shadow pointer to the VTOR, which also needs to be updated with the new address. Review the device datasheet to see if one exists. SCB->VTOR = JUMP_ADDR; /*The final part is to set the MSP to the value found in the user application vector table and then load the PC with the reset vector value of the user application. This can't be done in C, as it is always possible, that the compiler uses the current SP. But that would be gone after setting the new MSP. So, a call to a small assembler function is done.*/ BootJumpASM( Address[ 0 ], Address[ 1 ] ) ; }
As far as I know, our knowledgebase article covers all aspects of jumping from a bootloader to an application. As my colleague already mentioned, you need to use a debugger to see what happens. If you have an ST board, there should be an ST-Link on it. Single step through your code to see what happens. Does your bootloader jump to an unknow address, or does the jump cause an exception or.…
Does your hardware have an enabled on-chip or external watchdog timer that needs to be fed?
It jumps to the reset Handler and then just resets to the bootloader again.
It also doen't jump in here:
if( CONTROL_SPSEL_Msk & __get_CONTROL( ) ) { /* MSP is not active */ __set_MSP( __get_PSP( ) ) ; __set_CONTROL( __get_CONTROL( ) & ~CONTROL_SPSEL_Msk ) ; }
I have set also the projects up as a multiproject workspace according to this : https://developer.arm.com/documentation/ka005060/latest/
Still the debugger lands in the reset handler.
If your CPU is already using the MSP, it also does not require to execute this piece of code.
Then, bootloader and application must not have overlapping flash regions. So in the bootloader project, remove the LR_IROM2.
Also with what parameter do you call BootJump() function? And, is at the address you give there already some valid application flashed?
You should see in the debug session when stepping into the BootJumpASM() in the CPU registers the if the values are plausible or not. What values do you see there?