;******************************************************************************************************** ; uC/OS-II ; The Real-Time Kernel ; ; Copyright 1992-2021 Silicon Laboratories Inc. www.silabs.com ; ; SPDX-License-Identifier: APACHE-2.0 ; ; This software is subject to an open source license and is distributed by ; Silicon Laboratories Inc. pursuant to the terms of the Apache License, ; Version 2.0 available at www.apache.org/licenses/LICENSE-2.0. ; ;******************************************************************************************************** ;******************************************************************************************************** ; ; ARMv7-M Port ; ; Filename : os_cpu_a.asm ; Version : V2.93.01 ;******************************************************************************************************** ; For : ARMv7-M Cortex-M ; Mode : Thumb-2 ISA ; Toolchain : IAR EWARM ;******************************************************************************************************** ; Note(s) : (1) This port supports the ARM Cortex-M3, Cortex-M4 and Cortex-M7 architectures. ; (2) It has been tested with the following Hardware Floating Point Unit. ; (a) Single-precision: FPv4-SP-D16-M and FPv5-SP-D16-M ; (b) Double-precision: FPv5-D16-M ;******************************************************************************************************** ;******************************************************************************************************** ; PUBLIC FUNCTIONS ;******************************************************************************************************** EXTERN OSRunning ; External references EXTERN OSPrioCur EXTERN OSPrioHighRdy EXTERN OSTCBCur EXTERN OSTCBHighRdy EXTERN OSIntExit EXTERN OSTaskSwHook EXTERN OS_CPU_ExceptStkBase EXTERN OS_KA_BASEPRI_Boundary PUBLIC OSStartHighRdy ; Functions declared in this file PUBLIC OS_CPU_SR_Save PUBLIC OS_CPU_SR_Restore PUBLIC OSCtxSw PUBLIC OSIntCtxSw PUBLIC PendSV_Handler #ifdef __ARMVFP__ PUBLIC OS_CPU_FP_Reg_Push PUBLIC OS_CPU_FP_Reg_Pop #endif ;******************************************************************************************************** ; EQUATES ;******************************************************************************************************** NVIC_INT_CTRL EQU 0xE000ED04 ; Interrupt control state register. NVIC_SYSPRI14 EQU 0xE000ED22 ; System priority register (priority 14). NVIC_PENDSV_PRI EQU 0xFF ; PendSV priority value (lowest). NVIC_PENDSVSET EQU 0x10000000 ; Value to trigger PendSV exception. ;******************************************************************************************************** ; CODE GENERATION DIRECTIVES ;******************************************************************************************************** RSEG CODE:CODE:NOROOT(2) THUMB ;******************************************************************************************************** ; FLOATING POINT REGISTERS PUSH ; void OS_CPU_FP_Reg_Push (OS_STK *stkPtr) ; ; Note(s) : 1) This function saves S16-S31 registers of the Floating Point Unit. ; ; 2) Pseudo-code is: ; a) Push remaining FPU regs S16-S31 on process stack; ; b) Update OSTCBCur->OSTCBStkPtr; ;******************************************************************************************************** #ifdef __ARMVFP__ OS_CPU_FP_Reg_Push MRS R1, PSP ; PSP is process stack pointer CBZ R1, OS_CPU_FP_nosave ; Skip FP register save the first time VSTMDB R0!, {S16-S31} LDR R1, =OSTCBCur LDR R2, [R1] STR R0, [R2] OS_CPU_FP_nosave BX LR #endif ;******************************************************************************************************** ; FLOATING POINT REGISTERS POP ; void OS_CPU_FP_Reg_Pop (OS_STK *stkPtr) ; ; Note(s) : 1) This function restores S16-S31 of the Floating Point Unit. ; ; 2) Pseudo-code is: ; a) Restore regs S16-S31 of new process stack; ; b) Update OSTCBHighRdy->OSTCBStkPtr pointer of new proces stack; ;******************************************************************************************************** #ifdef __ARMVFP__ OS_CPU_FP_Reg_Pop VLDMIA R0!, {S16-S31} LDR R1, =OSTCBHighRdy LDR R2, [R1] STR R0, [R2] BX LR #endif ;******************************************************************************************************** ; CRITICAL SECTION METHOD 3 FUNCTIONS ; ; Description : Disable/Enable Kernel aware interrupts by preserving the state of BASEPRI. Generally speaking, ; the state of the BASEPRI interrupt exception processing is stored in the local variable ; 'cpu_sr' & Kernel Aware interrupts are then disabled ('cpu_sr' is allocated in all functions ; that need to disable Kernel aware interrupts). The previous BASEPRI interrupt state is restored ; by copying 'cpu_sr' into the BASEPRI register. ; ; Prototypes : OS_CPU_SR OS_CPU_SR_Save (OS_CPU_SR new_basepri); ; void OS_CPU_SR_Restore(OS_CPU_SR cpu_sr); ; ; ; Note(s) : 1) These functions are used in general like this: ; ; void Task (void *p_arg) ; { ; #if OS_CRITICAL_METHOD == 3 /* Allocate storage for CPU status register */ ; OS_CPU_SR cpu_sr; ; #endif ; ; : ; : ; OS_ENTER_CRITICAL(); /* cpu_sr = OS_CPU_SR_Save(new_basepri); */ ; : ; : ; OS_EXIT_CRITICAL(); /* OS_CPU_RestoreSR(cpu_sr); */ ; : ; : ; } ; ; 2) Increasing priority using a write to BASEPRI does not take effect immediately. ; (a) IMPLICATION This erratum means that the instruction after an MSR to boost BASEPRI ; might incorrectly be preempted by an insufficient high priority exception. ; ; (b) WORKAROUND The MSR to boost BASEPRI can be replaced by the following code sequence: ; ; CPSID i ; MSR to BASEPRI ; DSB ; ISB ; CPSIE i ;******************************************************************************************************** OS_CPU_SR_Save CPSID I ; Cortex-M7 errata notice. See Note #2 PUSH {R1} MRS R1, BASEPRI MSR BASEPRI, R0 DSB ISB MOV R0, R1 POP {R1} CPSIE I BX LR OS_CPU_SR_Restore CPSID I ; Cortex-M7 errata notice. See Note #2 MSR BASEPRI, R0 DSB ISB CPSIE I BX LR ;******************************************************************************************************** ; START MULTITASKING ; void OSStartHighRdy(void) ; ; Note(s) : 1) This function triggers a PendSV exception (essentially, causes a context switch) to cause ; the first task to start. ; ; 2) During task execution, PSP is used as the stack pointer. ; When an exception occurs, the core will switch to MSP until the exception return. ; ; 3) OSStartHighRdy() MUST: ; a) Setup PendSV exception priority to lowest; ; b) Set initial PSP to 0, to tell context switcher this is first run; ; c) Set the main stack to OS_CPU_ExceptStkBase ; d) Set OSRunning to TRUE; ; e) Get current high priority, OSPrioCur = OSPrioHighRdy; ; f) Get current ready thread TCB, OSTCBCur = OSTCBHighRdy; ; g) Get new process SP from TCB, SP = OSTCBHighRdy->OSTCBStkPtr; ; h) Restore R0-R11 and R14 from new process stack; ; i) Enable interrupts (tasks will run with interrupts enabled). ;******************************************************************************************************** OSStartHighRdy CPSID I ; Prevent interruption during context switch LDR R0, =NVIC_SYSPRI14 ; Set the PendSV exception priority LDR R1, =NVIC_PENDSV_PRI STRB R1, [R0] MOVS R0, #0 ; Set the PSP to 0 for initial context switch call MSR PSP, R0 LDR R0, =OS_CPU_ExceptStkBase ; Initialize the MSP to the OS_CPU_ExceptStkBase LDR R1, [R0] MSR MSP, R1 BL OSTaskSwHook ; Call OSTaskSwHook() for FPU Push & Pop LDR R0, =OSRunning ; OSRunning = TRUE MOVS R1, #1 STRB R1, [R0] LDR R0, =OSPrioCur ; OSPrioCur = OSPrioHighRdy; LDR R1, =OSPrioHighRdy LDRB R2, [R1] STRB R2, [R0] LDR R0, =OSTCBCur ; OSTCBCur = OSTCBHighRdy; LDR R1, =OSTCBHighRdy LDR R2, [R1] STR R2, [R0] LDR R0, [R2] ; R0 is new process SP; SP = OSTCBHighRdy->OSTCBStkPtr; MSR PSP, R0 ; Load PSP with new process SP MRS R0, CONTROL ORR R0, R0, #2 MSR CONTROL, R0 ISB ; Sync instruction stream LDMFD SP!, {R4-R11, LR} ; Restore r4-11, lr from new process stack LDMFD SP!, {R0-R3} ; Restore r0, r3 LDMFD SP!, {R12, LR} ; Load R12 and LR LDMFD SP!, {R1, R2} ; Load PC and discard xPSR CPSIE I BX R1 ;******************************************************************************************************** ; PERFORM A CONTEXT SWITCH (From task level) - OSCtxSw() ; PERFORM A CONTEXT SWITCH (From interrupt level) - OSIntCtxSw() ; ; Note(s) : 1) OSCtxSw() is called when OS wants to perform a task context switch. This function ; triggers the PendSV exception which is where the real work is done. ; ; 2) OSIntCtxSw() is called by OSIntExit() when it determines a context switch is needed as ; the result of an interrupt. This function simply triggers a PendSV exception which will ; be handled when there are no more interrupts active and interrupts are enabled. ;******************************************************************************************************** OSCtxSw OSIntCtxSw LDR R0, =NVIC_INT_CTRL ; Trigger the PendSV exception (causes context switch) LDR R1, =NVIC_PENDSVSET STR R1, [R0] BX LR ;******************************************************************************************************** ; HANDLE PendSV EXCEPTION ; void OS_CPU_PendSVHandler(void) ; ; Note(s) : 1) PendSV is used to cause a context switch. This is a recommended method for performing ; context switches with Cortex-M. This is because the Cortex-M auto-saves half of the ; processor context on any exception, and restores same on return from exception. So only ; saving of R4-R11 & R14 is required and fixing up the stack pointers. Using the PendSV exception ; this way means that context saving and restoring is identical whether it is initiated from ; a thread or occurs due to an interrupt or exception. ; ; 2) Pseudo-code is: ; a) Get the process SP ; b) Save remaining regs r4-r11 & r14 on process stack; ; c) Save the process SP in its TCB, OSTCBCur->OSTCBStkPtr = SP; ; d) Call OSTaskSwHook(); ; e) Get current high priority, OSPrioCur = OSPrioHighRdy; ; f) Get current ready thread TCB, OSTCBCur = OSTCBHighRdy; ; g) Get new process SP from TCB, SP = OSTCBHighRdy->OSTCBStkPtr; ; h) Restore R4-R11 and R14 from new process stack; ; i) Perform exception return which will restore remaining context. ; ; 3) On entry into PendSV handler: ; a) The following have been saved on the process stack (by processor): ; xPSR, PC, LR, R12, R0-R3 ; b) Processor mode is switched to Handler mode (from Thread mode) ; c) Stack is Main stack (switched from Process stack) ; d) OSTCBCur points to the OS_TCB of the task to suspend ; OSTCBHighRdy points to the OS_TCB of the task to resume ; ; 4) Since PendSV is set to lowest priority in the system (by OSStartHighRdy() above), we ; know that it will only be run when no other exception or interrupt is active, and ; therefore safe to assume that context being switched out was using the process stack (PSP). ; ; 5) Increasing priority using a write to BASEPRI does not take effect immediately. ; (a) IMPLICATION This erratum means that the instruction after an MSR to boost BASEPRI ; might incorrectly be preempted by an insufficient high priority exception. ; ; (b) WORKAROUND The MSR to boost BASEPRI can be replaced by the following code sequence: ; ; CPSID i ; MSR to BASEPRI ; DSB ; ISB ; CPSIE i ;******************************************************************************************************** PendSV_Handler CPSID I ; Cortex-M7 errata notice. See Note #5 MOV32 R2, OS_KA_BASEPRI_Boundary ; Set BASEPRI priority level required for exception preemption LDR R1, [R2] MSR BASEPRI, R1 DSB ISB CPSIE I MRS R0, PSP ; PSP is process stack pointer STMFD R0!, {R4-R11, R14} ; Save remaining regs r4-11, R14 on process stack LDR R5, =OSTCBCur ; OSTCBCur->OSTCBStkPtr = SP; LDR R1, [R5] STR R0, [R1] ; R0 is SP of process being switched out ; At this point, entire context of process has been saved MOV R4, LR ; Save LR exc_return value BL OSTaskSwHook ; Call OSTaskSwHook() for FPU Push & Pop LDR R0, =OSPrioCur ; OSPrioCur = OSPrioHighRdy; LDR R1, =OSPrioHighRdy LDRB R2, [R1] STRB R2, [R0] LDR R1, =OSTCBHighRdy ; OSTCBCur = OSTCBHighRdy; LDR R2, [R1] STR R2, [R5] ORR LR, R4, #0x04 ; Ensure exception return uses process stack LDR R0, [R2] ; R0 is new process SP; SP = OSTCBHighRdy->OSTCBStkPtr; LDMFD R0!, {R4-R11, R14} ; Restore r4-11, R14 from new process stack MSR PSP, R0 ; Load PSP with new process SP MOV32 R2, #0 ; Restore BASEPRI priority level to 0 CPSID I MSR BASEPRI, R2 DSB ISB CPSIE I BX LR ; Exception return will restore remaining context END