159 lines
7.8 KiB
Plaintext
159 lines
7.8 KiB
Plaintext
//------------------------------------------------------------------------------
|
|
// @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.
|
|
//
|
|
//
|
|
// <!--(C) COPYRIGHT 2010 STMicroelectronics. All rights reserved. -->
|
|
//------------------------------------------------------------------------------
|
|
|
|
#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
|
|
|
|
|
|
//------------------------------------------------------------------------------
|
|
// int32u 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__
|
|
|