Hi,
In my S12X code I have implemented an interrupt service routine that I call STANDBY. The point of STANDBY is to "softly" stop any ongoing operation and return to a state resembling the state after running the startup code.
Currently, STANDBY disables bunch of PIT timers and initializes most important variables and then returns to the execution of the code. Usually, my program will successfully return to the for-loop inside the main function and the variables are correctly initialized. But sometimes, probably due to bad luck with timing, the ISR returns inside a function that will modify the variables that were just initialized with STANDBY.
So I am looking for a way to return from my STANDBY ISR to the for-loop inside the main function, without running any of the code that was executing while STANDBY was issued.
Does anyone have any suggestions or examples how to achieve this? I guess one option would be to somehow clear the call stack and then use the goto command to jump into the main function. But I do not have any idea how to safely modify the call stack...
PS. I already have implemented a "REBOOT" command, that causes the MCU to reset by writing an invalid value to the ARMCOP register. But I would like to have also this faster and softer STANDBY command.
Thanks!
Timo
EDIT: After discussing about this problem with a colleague, we have an idea how this could be achieved by a couple of assembly instructions:
The plan is to somehow capture the Program Counter (PC) and Stack Pointer (SP) values in the beginning of the for-loop where I want to force the CPU. Ideally this is somehow done in the compiler, or then this will be stored in memory during run-time.
In the end of the STANDBY ISR the SP will be modified to point to the same value as in the beginning of the main function's for-loop. I am considering using the GLDS instruction for this. Then, the PC will be updated to the desired value with JMP instruction.
I guess I will have to also call CLI just before JMP, or the future interrupts will stay disabled? Is there anything else that I should do before the JMP instruction? E.g. something with the IPL?
I will try the above idea in practice and let you know what happens.
After some trial and error, I was able to achieve what I wanted with Assembly.
I made a function called standbySP_PCstore();, that stores the SP to a fixed RAM address, when it is executed. The PC in the beginning of standbySP_PCstore(); is stored already earlier like this:
//Store PC value for CMD_STANDBY to jump to
*(unsigned int *__far)(STANDBY_PC_ADDR) = ((unsigned long) ((unsigned long *__far) standbySP_PCstore)) >> 8;
standbySP_PCstore() is defined as:
static void standbySP_PCstore(void){
asm {
MOVB STANDBY_SP_ADDR_GPAGE,GPAGE ; //Select GPAGE for global address
GSTS STANDBY_SP_ADDR_BYTE ; //Store Stack Pointer
}
}
standbySP_PCstore() is placed just before the for-loop in the main function and when the STANDBY command is called, it will clear the I bit in CCR, set Interrupt Priority Level 0, set SP to what it was inside standbySP_PCstore() and finally jump inside standbySP_PCstore() with the following Assembly subroutine:
//Force CPU back to just befor main function's for-loop and forget any other subroutines which were executing
asm {
MOVB #0x0F, GPAGE ; //Select GPAGE for global address
GLDS STANDBY_SP_ADDR_BYTE ; //Load Stack Pointer from RAM
LDAA #0x00 ; //Load bitmask for clearing IPL
TFR A,CCRH ; //Clear IPL in CCRH
CLI ; //Clear interrupt flag from CCR
MOVB #0x0F, GPAGE ; //Select GPAGE for global address
GLDX STANDBY_PC_ADDR_BYTE ; //Load address for JMP instruction from RAM to index register X
JMP 0,X ; //Jump to address in index register X
}
}
I hope this might be of help to someone wrestling with a similar problem in the future.