Skip to main content
Schmid.M.
Associate III
March 22, 2021
Question

Jumping to main program from ISR

  • March 22, 2021
  • 9 replies
  • 4071 views

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

    This topic has been closed for replies.

    9 replies

    Vangelis Fortounas
    Associate II
    March 22, 2021

    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"

    Schmid.M.
    Schmid.M.Author
    Associate III
    March 22, 2021

    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 ;)

    waclawek.jan
    Super User
    March 22, 2021

    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

    Schmid.M.
    Schmid.M.Author
    Associate III
    March 22, 2021

    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.

    Tesla DeLorean
    Guru
    March 22, 2021

    If one got the priorities right you could probably just substitute the return address on the stack.

    Tips, Buy me a coffee, or three.. PayPal VenmoUp vote any posts that you find helpful, it shows what's working..
    Schmid.M.
    Schmid.M.Author
    Associate III
    March 23, 2021

    I don't fully understand, how would you do this?

    Vangelis Fortounas
    Associate II
    March 24, 2021

    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 */
    }

    0693W000008xjbCQAQ.jpg0693W000008xjbHQAQ.jpg 

    0693W000008xjcjQAA.jpg0693W000008xjctQAA.jpg 

    Vangelis Fortounas
    Associate II
    March 24, 2021

    0693W000008xjefQAA.jpg

    Vangelis Fortounas
    Associate II
    March 24, 2021

    Hello

    This is another example fit to your needs

    It used only from inside an ISR, to jump to an address in thread mode.

    Use it like this:    jumpfromISR(topOfStack,(uint32_t)main);// first parameter is the new stackpointer in MSP, main is the function to jump.

    The bit 0 of the second parameter must be always 1

    clear also all pending interrupts before call this function.

    It just a working (tested in M4) example and needs improvements , dont rely on it until you understand how it works.

    In general , install the new stack, make the stack frame with new address to jump, load PC with proper exec return value, the stack pointer returns to its set position after jump.

    __attribute__((naked)) __attribute__((noreturn)) void jumpfromISR(uint32_t new_stack_pointer,uint32_t jump_to_address);// remember jumptoaddress bit0 must be 1 
     
    __attribute__((naked)) __attribute__((noreturn)) void jumpfromISR(uint32_t new_stack_pointer,uint32_t jump_to_address) 
    {
     //_____________________________________________________________________________
    	// this example uses MSP for both thread and handler modes. 
    	__asm("LDR.W R2, =0xE000ED14");// Read SCB->CCR that located at address 0xE000ED14
    	__asm("LDR R3, [R2]");//Take the value of register
    	__asm("ORR R3, R3, #1");//Set bit 0 to enable NONBASETHRDENA
    	__asm("STR R3, [R2]");// Write back the value to the SCB->CCR
    		
    	// take only the xPSR from stacked frame and discard the rest
    	__asm("POP {r2-r8}");// POP R0,ri,r2,r3,r12,lr,pc
    	__asm("POP {r2}");// POP xPSR	to use it	
     
    	__asm("MSR MSP, R0");//set the new stack pointer
    	__asm("DSB");// wait for store to complete
    	__asm("ISB");// reset pipeline
    	
    	//Make a new stack frame
    	__asm("PUSH {R2}");//xPSR
    	__asm("PUSH {R1}");// new address to go
    	__asm("PUSH {R0-R5}");// just complete the next of stack frame with "dont care" values. But if you care to return something , just modify it
     
    	// 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 R0, =0xFFFFFFF9");//Return to Thread mode, exception return uses non-floating-point state from MSP and execution uses MSP after return.
    	__asm("BX R0");// 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
    	__asm("B .");//trap
    //_____________________________________________________________________________ 
    }

    Schmid.M.
    Schmid.M.Author
    Associate III
    March 26, 2021

    Hi,

    I really appreciate your effort. Still as you say... before using this I must understand it fully. I think I am not able to understand this code without learning much more about the internal core structure of the ARM. My problem is, that I don't have time for this at the moment.

    Therefore I must use the workaround described below.

    Piranha
    Principal III
    March 24, 2021

    > if the device is frozen somewhere in the main() due to a software error

    If the bootloader is freezing, the device is doomed anyway. Bootloader is the piece of code that must always be capable of running and recovering the main firmware. Instead of investing your time into making dirty hacks, you should invest it into learning all the details of hardware and software development and making the bootloader almost perfect. And then to come out of that freezing, that should not happen at all, microcontrollers have a hardware watchdog peripherals.

    Uwe Bonnes
    Chief
    March 24, 2021

    Remove the flag for bootloader entry and reset the CPU. Voila!

    Piranha
    Principal III
    March 24, 2021
    Schmid.M.
    Schmid.M.Author
    Associate III
    March 26, 2021

    okay. thanks guys. I thought there would be a simple solution to this. Now I set up a region in RAM for communication between bootloader and main program. for jumping into any part of the program I now just set the command where to jump to and then reset the device. It is not as simple and fast, but it is more clean.