cancel
Showing results for 
Search instead for 
Did you mean: 

Jumping to main program from ISR

Schmid.M.
Senior

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

16 REPLIES 16

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.
Senior

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 😉

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

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.

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 Venmo
Up vote any posts that you find helpful, it shows what's working..

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

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 

0693W000008xjefQAA.jpg

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?