| ;/* |
| ; * 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 |
| |