//------------------------------------------------------------------------------ // @file hal/micro/cortexm3/context-switch.s79 // @brief Context save/restore for deep sleep using the PendSV exception. // // This file also contains a simple halInternalIdleSleep() function that // executes just the WFI instruction for idle sleeping. // // When the STM32W108XX enters deep sleep, the hardware will actually remove power // from the Cortex-M3 core (in Deep Sleep 0, power is not removed but the core // is held in reset). Since this will clear the internal state of the core, it // must be properly restored such that execution can resume from the sleep code. // The simplest and most secure mechanism to do this is to perform a context save // and restore. Context save/restore is almost identical to a context switch // used in multi-threaded systems with the main difference being only one stack // pointer is used and the save/restore operations are disjoint. // // When an interrupt is triggered in the STM32W108XX, the core automatically saves 8 // of the 16 CPU registers on the stack. The ISR then only needs to save the // other 8 registers and store the resulting stack pointer. Restoring is the // reverse operation where 8 registers are manually copied back with the other 8 // being restored on the return from interrupt. // // As its last act, the deep sleep code will trigger the PendSV exception to // perform a context save. When the core is booted upon deep sleep exit, the // RESET_EVENT register informs cstartup if the chip just exited deep sleep. // Cstartup will then trigger halTriggerContextRestore which sets up the stack // pointer and trigger the PendSV exception to perform a restore. When PendSV // returns from interrupt context the system will be back at the same point it // was before deep sleep. // // // //------------------------------------------------------------------------------ #include "compiler/asm.h" //------------------------------------------------------------------------------ // boolean halPendSvSaveContext // // A simple boolean flag used to indicate if a context save or a context restore // should be performed. Since context switching is handled with the PendSV // interrupt, parameters cannot be passed into the ISR and as such this boolean // flag is used. If this flag is zero, PendSV should perform a context restore. // If this flag is non-zero, PendSV should perform a context save. // Note: The smallest unit of storage is a single byte. // // NOTE: This flag must be set before PendSV is triggered! //------------------------------------------------------------------------------ __BSS__ __EXPORT__ halPendSvSaveContext halPendSvSaveContext: __SPACE__ 1 //------------------------------------------------------------------------------ // uint32_t savedMSP // // Private storage to hold the saved stack pointer. This variable is only used // in this file and should not be extern'ed. In our current design we // do not use real context switching, but only context saving and restoring. // As such, we only need to keep track of the Main Stack Pointer (MSP). This // variable is used to hold the MSP between a save and a restore. //------------------------------------------------------------------------------ __BSS__ __EXPORT__ savedMSP savedMSP: __SPACE__ 4 //------------------------------------------------------------------------------ // void halPendSvIsr(void) // // This ISR is installed by cstartup in the vector table for the PendSV // exception. The purpose of this ISR is to either save the current context // and trigger sleeping through the 'WFI' instruction, or restore a // previous context. The variable halPendSvSaveContext is used to // decide if a save or a restore is performed. Since entering/exiting interrupt // context automatically saves/restores 8 of the 16 CPU registers on the stack // we need to manually save the other 8 onto the stack as well. // // When a context save is complete, the stack will have been expanded by 16 // words with the current Stack Pointer stored in savedMSP. // // When a context restore is complete, the stack will have been shrunk by 16 // words with the old context restored after the return instruction. // // NOTE: The IAR default handler name for PendSV, PendSV_Handler, is also // instantiated here so it routes to the same code as the St // name halPendSvIsr. //------------------------------------------------------------------------------ __CODE__ __THUMB__ __EXPORT__ PendSV_Handler __EXPORT__ halPendSvIsr PendSV_Handler: halPendSvIsr: LDR R0, =halPendSvSaveContext //load the variable's address LDRB R0, [R0] //get the value in the variable CBZ R0, contextRestore //if variable is zero, branch to contextRestore contextSave: MRS R0, MSP //load the main stack pointer into R0 SUB R0, R0, #0x20 //make room on the stack for 8 words (32 bytes) MSR MSP, R0 //load new MSP from adjusted stack pointer STM R0, {R4-R11} //store R4-R11 (8 words) onto the stack LDR R1, =savedMSP //load address of savedMSP into R1 STR R0, [R1] //store the MSP into savedMSP WFI //all saved, trigger deep sleep // Even if we fall through the WFI instruction, we will immediately // execute a context restore and end up where we left off with no // ill effects. Normally at this point the core will either be // powered off or reset (depending on the deep sleep level). contextRestore: LDR R0, =savedMSP //load address of savedMSP into R0 LDR R0, [R0] //load the MSP from savedMSP LDM R0, {R4-R11} //load R4-R11 (8 words) from the stack ADD R0, R0, #0x20 //eliminate the 8 words (32 bytes) from the stack MSR MSP, R0 //restore the MSP from R0 BX LR //return to the old context //------------------------------------------------------------------------------ // void halTriggerContextRestore(void) // // Cstartup is responsible for triggering a context restore based upon the // RESET_EVENT register. Since the stack pointer sits at the top of memory // after the core boots, cstartup cannot simply trigger a PendSV to restore // context as this will cause existing stack data to be over written. Cstartup // disables interrupts, pends PendSV, and then calls this function. This // function simply configures the Stack Pointer to be past the previous data // such that when interrupts are enabled and PendSV fires it wont corrupt // previous data. //------------------------------------------------------------------------------ __CODE__ __THUMB__ __EXPORT__ halTriggerContextRestore halTriggerContextRestore: LDR R0, =savedMSP //load address of savedMSP into R0 LDR R0, [R0] //load the MSP from savedMSP MSR MSP, R0 //restore the MSP from R0 CPSIE i //enable interrupts and let PendSV fire BX LR //this return should never be triggered //------------------------------------------------------------------------------ // void halInternalIdleSleep(void) // // A simple internal function call (to be called from halSleep) for executing // the WFI instruction and entering the simple, idle sleep state. //------------------------------------------------------------------------------ __CODE__ __THUMB__ __EXPORT__ halInternalIdleSleep halInternalIdleSleep: WFI //trigger idle sleep BX LR //return __END__