nes-proj/cpu/stm32w108/hal/micro/cortexm3/context-switch.s79

159 lines
7.8 KiB
Plaintext
Raw Normal View History

2010-10-25 09:03:38 +00:00
//------------------------------------------------------------------------------
// @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__