mirror of
				https://github.com/FreeRTOS/FreeRTOS-Kernel.git
				synced 2025-11-04 11:09:01 +01: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
 | 
						|
 |