2021-03-22 02:34 AM
Hi,
I try to jump from Bootloader to my main program using this function:
void jumpToMainProgram(){
uint32_t topOfStack = (*(uint32_t *)0x8008200ul);
HAL_RCC_DeInit(); // deinit clocks
// reset all periphals
__HAL_RCC_AHB_FORCE_RESET();
__HAL_RCC_AHB_RELEASE_RESET();
__HAL_RCC_APB1_FORCE_RESET();
__HAL_RCC_APB1_RELEASE_RESET();
__HAL_RCC_APB2_FORCE_RESET();
__HAL_RCC_APB2_RELEASE_RESET();
// reset Systick
SysTick->CTRL = 0;
SysTick->LOAD = 0;
SysTick->VAL = 0;
/*__set_PSP(topOfStack);*/
__set_MSP(topOfStack);
__DSB(); // complete all memory accesses
{
typedef void (*t_pFunctionNoReturn)(void) __attribute__((noreturn));
register t_pFunctionNoReturn pAppl;
register U32 resetHandler;
resetHandler = (U32)0x08008200ul + 4ul;
pAppl = (t_pFunctionNoReturn)(*(U32 *)resetHandler); /* read from Reset vector */
pAppl();
while(1) { } // never reach this point
}
}
this works fine normally, but when I call this function from an ISR, the new program does not accept any new interrupts - it seems like the Core thinks I am still in the ISR.
Is there a solution how to jump to another program from an ISR?
thanks,
Michael
2021-03-22 03:25 AM
Hello
NONBASETHRDENA bit in in Cortex Configuration and Control Register(CCR) controls the entry to thread mode from handler mode.
Eg If this bit is 1, POPing PC from stack , can do the jump in thread mode.
In Cortex M General User Guide will find details about all options to do the jump from ISR to "another program"
2021-03-22 03:28 AM
cool, thanks! could you share a link or example of how to do this? I am not too deep into the ARM core stuff, the jumping code was mainly copied from other websites as well ;)
2021-03-22 05:49 AM
IMHO, instead of wrestling the core registers to place, it's better, cleaner, nicer and even easier to simply avoid the problem by exiting the ISR in the bootloader prior to jumping to application.
In the ISR, set a flag "now_jump_into_application = 1" and in main() test this flag and perform the jump.
JW
2021-03-22 06:27 AM
this is exactly how i do it so far. problem is: if the device is frozen somewhere in the main() due to a software error, the device can not jump anywhere anymore. It would be nice if the jump would still be possible from the interrupt directly.
2021-03-22 08:21 AM
If one got the priorities right you could probably just substitute the return address on the stack.
2021-03-23 03:17 AM
I don't fully understand, how would you do this?
2021-03-23 05:49 PM
Hello
Here is a return address substitution example and it is inside a HAL's generated systick IRQ handler.
Warning, This example uses MSP only, for thread and handler modes. The new program has the responsibility to initialize again the stack pointer.
Two words poped at beginning to expose the pushed frame these words pushed by compiler I found that was only two in this case . Strongly suggest to make a naked IRQ handler function (assembly) to have the full control of the compilation.
Compare the two pictures to see the differences after execution of jump.
/**
* @brief This function handles System tick timer.
*/
void SysTick_Handler(void)
{
/* USER CODE BEGIN SysTick_IRQn 0 */
/* USER CODE END SysTick_IRQn 0 */
HAL_IncTick();
/* USER CODE BEGIN SysTick_IRQn 1 */
__asm("POP {R4}");//unwind the stack used by compiler, WARNING this is compiler dependable. Make a naked function to control it
__asm("POP {R4}");//unwind the stack used by compiler, WARNING this is compiler dependable. Make a naked function to control it
//_____________________________________________________________________________
// this example uses MSP for both thread and handler modes.
//SET this bit, so the processor can enter Thread mode under the control of an EXC_RETURN value (given to PC reg.)
SCB->CCR |= SCB_CCR_NONBASETHRDENA_Msk;// set bit 0
// modify, the stacked frame when ISR made active. "main" is the new return address
//unwind automatic saved stacked frame
__asm("POP {R0}");// POP R0
__asm("POP {R1}");// POP R1
__asm("POP {R2}");// POP R2
__asm("POP {R3}");// POP R3
__asm("POP {R12}");// POP R12
__asm("POP {LR}");// POP LR
__asm("POP {R4}");//old address .. to change
//push again with modifications, the frame to stack to be used when jump
__asm("LDR R4, =main");
__asm("PUSH {R4}");//load the new return address
__asm("PUSH {LR}");//
__asm("PUSH {R12}");//
__asm("PUSH {R3}");//
__asm("PUSH {R2}");//
__asm("PUSH {R1}");//
__asm("PUSH {R0}");//
// this is the "jump" of ISR , dont forget to disable the NONBASETHRDENA bit when start the new program(thread mode )
//load the PC with one of EXC_RETURN values.. Exception return values,described in cortex Generic User Guide
__asm("LDR R4, =0xFFFFFFF9");//Return to Thread mode, exception return uses non-floating-point state from MSP and execution uses MSP after return.
__asm("BX R4");// load PC with EXC_RETURN value.. will go to thread mode, after xPSR(8th word) poped from stack
// normaly the execution will not reach this line
while(1);//trap
//_____________________________________________________________________________
/* USER CODE END SysTick_IRQn 1 */
}
2021-03-23 06:06 PM
2021-03-24 01:31 AM
thanks very much, looks good but much more complicated than I had hoped :D
I have to admit I never used ASM so far, so I don't know the meanings of POP and PUSH nor how the core Registers work.
where in your example code can I enter the adress where I want to jump to? Does this function also work if I call it from thread mode in another part of the application?