mirror of
https://github.com/FreeRTOS/FreeRTOS-Kernel.git
synced 2025-04-19 21:11:57 -04:00
Fix FPU stack order issue and Improve FPU checking flow Fix Interrupt depth comparison logic Fix parameter mismatch in portmacro.h file Add comment to explain assembly code
332 lines
10 KiB
ArmAsm
332 lines
10 KiB
ArmAsm
;/*
|
|
; * FreeRTOS Kernel <DEVELOPMENT BRANCH>
|
|
; * Copyright (C) 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
|
; *
|
|
; * SPDX-License-Identifier: MIT
|
|
; *
|
|
; * Permission is hereby granted, free of charge, to any person obtaining a copy of
|
|
; * this software and associated documentation files (the "Software"), to deal in
|
|
; * the Software without restriction, including without limitation the rights to
|
|
; * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
|
; * the Software, and to permit persons to whom the Software is furnished to do so,
|
|
; * subject to the following conditions:
|
|
; *
|
|
; * The above copyright notice and this permission notice shall be included in all
|
|
; * copies or substantial portions of the Software.
|
|
; *
|
|
; * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
; * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
|
; * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
|
; * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
|
; * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
|
; * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
; *
|
|
; * https://www.FreeRTOS.org
|
|
; * https://github.com/FreeRTOS
|
|
; *
|
|
; */
|
|
|
|
;------------------------------------------------------------------------------
|
|
; Extern symbols
|
|
;------------------------------------------------------------------------------
|
|
.extern _uxInterruptNesting
|
|
.extern _uxPortMaxInterruptDepth
|
|
.extern _xPortScheduleStatus
|
|
.extern _vTaskSwitchContext
|
|
.extern _pvPortGetCurrentTCB
|
|
.extern _vCommonISRHandler
|
|
.extern _xPortGET_CORE_ID
|
|
|
|
.public _vIrq_Handler
|
|
.public _vPortStartFirstTask
|
|
.public _vPortYield
|
|
.public _vTRAP0_Handler
|
|
;------------------------------------------------------------------------------
|
|
; Macro definitions
|
|
;------------------------------------------------------------------------------
|
|
EIPC .set 0
|
|
EIPSW .set 1
|
|
PSW .set 5
|
|
FPSR .set 6
|
|
FPEPC .set 7
|
|
EIIC .set 13
|
|
CTPC .set 16
|
|
CTPSW .set 17
|
|
EIIC_MSK .set 0x00000FFF
|
|
FPU_MSK .set 0x00010000
|
|
;------------------------------------------------------------------------------
|
|
; portSAVE_CONTEXT
|
|
; Context saving
|
|
;------------------------------------------------------------------------------
|
|
portSAVE_CONTEXT .macro
|
|
prepare lp, 0
|
|
|
|
; Save general-purpose registers and EIPSW, EIPC, EIIC, CTPSW, CTPC into stack.
|
|
pushsp r5, r30
|
|
$nowarning
|
|
pushsp r1, r2
|
|
$warning
|
|
|
|
stsr EIPSW, r15
|
|
stsr EIPC, r16
|
|
stsr EIIC, r17
|
|
stsr CTPSW, r18
|
|
stsr CTPC, r19
|
|
pushsp r15, r19
|
|
|
|
; Save FPU registers to stack if FPU is enabled
|
|
mov FPU_MSK, r19
|
|
tst r15, r19
|
|
|
|
; Jump over next 3 instructions: stsr (4 bytes)*2 + pushsp (4 bytes)
|
|
bz 12
|
|
stsr FPSR, r18
|
|
stsr FPEPC, r19
|
|
pushsp r18, r19
|
|
|
|
; Save EIPSW register to stack
|
|
; Due to the syntax of the pushsp instruction, using r14 as dummy value
|
|
pushsp r14, r15
|
|
|
|
; Get current TCB, the return value is stored in r10 (CCRH compiler)
|
|
jarl _pvPortGetCurrentTCB, lp
|
|
st.w sp, 0[r10]
|
|
|
|
.endm
|
|
|
|
;------------------------------------------------------------------------------
|
|
; portRESTORE_CONTEXT
|
|
; Context restoring
|
|
;------------------------------------------------------------------------------
|
|
portRESTORE_CONTEXT .macro
|
|
; Current TCB is returned by r10 (CCRH compiler)
|
|
jarl _pvPortGetCurrentTCB, lp
|
|
ld.w 0[r10], sp ; Restore the stack pointer from the TCB
|
|
|
|
; Restore FPU registers if FPU is enabled
|
|
mov FPU_MSK, r19
|
|
; Restore EIPSW register to check FPU
|
|
; Due to the syntax of the popsp instruction, using r14 as dummy value
|
|
popsp r14, r15
|
|
tst r15, r19
|
|
; Jump over next 3 instructions: stsr (4 bytes)*2 + popsp (4 bytes)
|
|
bz 12
|
|
popsp r18, r19
|
|
ldsr r19, FPEPC
|
|
ldsr r18, FPSR
|
|
|
|
;Restore general-purpose registers and EIPSW, EIPC, EIIC, CTPSW, CTPC
|
|
popsp r15, r19
|
|
ldsr r19, CTPC
|
|
ldsr r18, CTPSW
|
|
ldsr r17, EIIC
|
|
ldsr r16, EIPC
|
|
ldsr r15, EIPSW
|
|
|
|
$nowarning
|
|
popsp r1, r2
|
|
$warning
|
|
popsp r5, r30
|
|
|
|
dispose 0, lp
|
|
.endm
|
|
|
|
;------------------------------------------------------------------------------
|
|
; Save used registers
|
|
;------------------------------------------------------------------------------
|
|
SAVE_REGISTER .macro
|
|
; Save general-purpose registers and EIPSW, EIPC, EIIC, CTPSW, CTPC into stack.
|
|
; Callee-Save registers (r20 to r30) are not used in interrupt handler and
|
|
; guaranteed no change after function call. So, don't need to save register
|
|
; to optimize the used stack memory.
|
|
pushsp r5, r19
|
|
$nowarning
|
|
pushsp r1, r2
|
|
$warning
|
|
|
|
stsr EIPSW, r19
|
|
stsr EIPC, r18
|
|
stsr EIIC, r17
|
|
mov lp, r16
|
|
mov ep, r15
|
|
stsr CTPSW, r14
|
|
stsr CTPC, r13
|
|
pushsp r13, r18
|
|
|
|
mov FPU_MSK, r16
|
|
tst r16, r19
|
|
bz 8
|
|
stsr FPSR, r17
|
|
stsr FPEPC, r18
|
|
|
|
pushsp r17, r19
|
|
|
|
.endm
|
|
;------------------------------------------------------------------------------
|
|
; Restore used registers
|
|
;------------------------------------------------------------------------------
|
|
RESTORE_REGISTER .macro
|
|
|
|
mov FPU_MSK, r15
|
|
popsp r17, r19
|
|
tst r19, r15
|
|
bz 8
|
|
ldsr r18, FPEPC
|
|
ldsr r17, FPSR
|
|
|
|
popsp r13, r18
|
|
ldsr r13, CTPC
|
|
ldsr r14, CTPSW
|
|
mov r15, ep
|
|
mov r16, lp
|
|
ldsr r17, EIIC
|
|
ldsr r18, EIPC
|
|
ldsr r19, EIPSW
|
|
|
|
$nowarning
|
|
popsp r1, r2
|
|
$warning
|
|
popsp r5, r19
|
|
.endm
|
|
|
|
;------------------------------------------------------------------------------
|
|
; Start the first task.
|
|
;------------------------------------------------------------------------------
|
|
_vPortStartFirstTask:
|
|
portRESTORE_CONTEXT
|
|
eiret
|
|
|
|
;------------------------------------------------------------------------------
|
|
; _vPortYield
|
|
;------------------------------------------------------------------------------
|
|
_vPortYield:
|
|
trap 0
|
|
jmp [lp] ; Return to caller function
|
|
|
|
;------------------------------------------------------------------------------
|
|
; PortYield handler. This is installed as the TRAP exception handler.
|
|
;------------------------------------------------------------------------------
|
|
_vTRAP0_Handler:
|
|
;Save the context of the current task.
|
|
portSAVE_CONTEXT
|
|
|
|
; The use case that portYield() is called from interrupt context as nested interrupt.
|
|
; Context switch should be executed at the most outer of interrupt tree.
|
|
; In that case, set xPortScheduleStatus to flag context switch in interrupt handler.
|
|
jarl _xPortGET_CORE_ID, lp ; return value is contained in r10 (CCRH compiler)
|
|
mov r10, r11
|
|
shl 2, r11
|
|
mov #_uxInterruptNesting, r19
|
|
add r11, r19
|
|
ld.w 0[r19], r18
|
|
cmp r0, r18
|
|
be _vTRAP0_Handler_ContextSwitch
|
|
|
|
mov #_xPortScheduleStatus, r19
|
|
add r11, r19
|
|
|
|
; Set xPortScheduleStatus[coreID]=PORT_SCHEDULER_TASKSWITCH
|
|
mov 1, r17
|
|
st.w r17, 0[r19]
|
|
br _vTRAP0_Handler_Exit
|
|
|
|
_vTRAP0_Handler_ContextSwitch:
|
|
; Pass coreID (r10) as parameter by r6 (CCRH compiler) in SMP support.
|
|
mov r10, r6
|
|
; Call the scheduler to select the next task.
|
|
; vPortYeild may be called to current core again at the end of vTaskSwitchContext.
|
|
; This may case nested interrupt, however, it is not necessary to set
|
|
; uxInterruptNesting (currently 0) for nested trap0 exception. The user interrupt
|
|
; (EI level interrupt) is not accepted inside of trap0 exception.
|
|
jarl _vTaskSwitchContext, lp
|
|
|
|
_vTRAP0_Handler_Exit:
|
|
; Restore the context of the next task to run.
|
|
portRESTORE_CONTEXT
|
|
eiret
|
|
|
|
;------------------------------------------------------------------------------
|
|
; _Irq_Handler
|
|
; Handler interrupt service routine (ISR).
|
|
;------------------------------------------------------------------------------
|
|
_vIrq_Handler:
|
|
; Save used registers.
|
|
SAVE_REGISTER
|
|
|
|
; Get core ID by HTCFG0, thread configuration register.
|
|
; Then, increase nesting count for current core.
|
|
jarl _xPortGET_CORE_ID, lp ; return value is contained in r10 (CCRH compiler)
|
|
shl 2, r10
|
|
mov r10, r17
|
|
|
|
mov #_uxInterruptNesting, r19
|
|
add r17, r19
|
|
ld.w 0[r19], r18
|
|
addi 0x1, r18, r16
|
|
st.w r16, 0[r19]
|
|
|
|
pushsp r17, r19
|
|
|
|
;Call the interrupt handler.
|
|
stsr EIIC, r6
|
|
andi EIIC_MSK, r6, r6
|
|
|
|
; Do not enable interrupt for nesting. Stackover flow may occurs if the
|
|
; depth of nesting interrupt is exceeded.
|
|
mov #_uxPortMaxInterruptDepth, r19
|
|
ld.w 0[r19], r15
|
|
cmp r15, r16
|
|
bge 4 ; Jump over ei instruction
|
|
ei
|
|
jarl _vCommonISRHandler, lp
|
|
di
|
|
synce
|
|
|
|
popsp r17, r19
|
|
st.w r18, 0[r19] ; Restore the old nesting count.
|
|
|
|
; A context switch if no nesting interrupt.
|
|
cmp 0x0, r18
|
|
bne _vIrq_Handler_NotSwitchContext
|
|
|
|
; Check if context switch is requested.
|
|
mov #_xPortScheduleStatus, r19
|
|
add r17, r19
|
|
ld.w 0[r19], r18
|
|
cmp r0, r18
|
|
bne _vIrq_Handler_SwitchContext
|
|
|
|
_vIrq_Handler_NotSwitchContext:
|
|
; No context switch. Restore used registers
|
|
RESTORE_REGISTER
|
|
eiret
|
|
|
|
;This sequence is executed for primary core only to switch context
|
|
_vIrq_Handler_SwitchContext:
|
|
; Clear the context switch pending flag.
|
|
st.w r0, 0[r19]
|
|
|
|
add -1, r18
|
|
bnz _vIrq_Handler_StartFirstTask
|
|
; Restore used registers before saving the context to the task stack.
|
|
RESTORE_REGISTER
|
|
portSAVE_CONTEXT
|
|
|
|
; Get Core ID and pass to vTaskSwitchContext as parameter (CCRH compiler)
|
|
; The parameter is unused in single core, no problem with this redudant setting
|
|
jarl _xPortGET_CORE_ID, lp ; return value is contained in r10 (CCRH compiler)
|
|
mov r10, r6
|
|
|
|
; vPortYeild may be called to current core again at the end of vTaskSwitchContext.
|
|
; This may case nested interrupt, however, it is not necessary to set
|
|
; uxInterruptNesting (currently 0) for trap0 exception. The user interrupt
|
|
; (EI level interrupt) is not accepted inside of trap0 exception.
|
|
jarl _vTaskSwitchContext, lp ;
|
|
portRESTORE_CONTEXT
|
|
eiret
|
|
|
|
_vIrq_Handler_StartFirstTask:
|
|
RESTORE_REGISTER
|
|
jr _vPortStartFirstTask
|
|
|