123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601 |
- /*
- * FreeRTOS Kernel V10.4.6
- * Copyright (C) 2015-2019 Cadence Design Systems, Inc.
- * 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
- *
- */
- #include "xtensa_rtos.h"
- #define TOPOFSTACK_OFFS 0x00 /* StackType_t *pxTopOfStack */
- #define CP_TOPOFSTACK_OFFS 0x04 /* xMPU_SETTINGS.coproc_area */
- .extern pxCurrentTCB
- /*
- *******************************************************************************
- * Interrupt stack. The size of the interrupt stack is determined by the config
- * parameter "configISR_STACK_SIZE" in FreeRTOSConfig.h
- *******************************************************************************
- */
- .data
- .align 16
- .global port_IntStack
- port_IntStack:
- .space configISR_STACK_SIZE
- port_IntStackTop:
- .word 0
- port_switch_flag:
- .word 0
- .text
- /*
- *******************************************************************************
- * _frxt_setup_switch
- * void _frxt_setup_switch(void);
- *
- * Sets an internal flag indicating that a task switch is required on return
- * from interrupt handling.
- *
- *******************************************************************************
- */
- .global _frxt_setup_switch
- .type _frxt_setup_switch,@function
- .align 4
- _frxt_setup_switch:
- ENTRY(16)
- movi a2, port_switch_flag
- movi a3, 1
- s32i a3, a2, 0
- RET(16)
- /*
- *******************************************************************************
- * _frxt_int_enter
- * void _frxt_int_enter(void)
- *
- * Implements the Xtensa RTOS porting layer's XT_RTOS_INT_ENTER function for
- * freeRTOS. Saves the rest of the interrupt context (not already saved).
- * May only be called from assembly code by the 'call0' instruction, with
- * interrupts disabled.
- * See the detailed description of the XT_RTOS_ENTER macro in xtensa_rtos.h.
- *
- *******************************************************************************
- */
- .globl _frxt_int_enter
- .type _frxt_int_enter,@function
- .align 4
- _frxt_int_enter:
- /* Save a12-13 in the stack frame as required by _xt_context_save. */
- s32i a12, a1, XT_STK_A12
- s32i a13, a1, XT_STK_A13
- /* Save return address in a safe place (free a0). */
- mov a12, a0
- /* Save the rest of the interrupted context (preserves A12-13). */
- call0 _xt_context_save
- /*
- Save interrupted task's SP in TCB only if not nesting.
- Manage nesting directly rather than call the generic IntEnter()
- (in windowed ABI we can't call a C function here anyway because PS.EXCM is still set).
- */
- movi a2, port_xSchedulerRunning
- movi a3, port_interruptNesting
- l32i a2, a2, 0 /* a2 = port_xSchedulerRunning */
- beqz a2, 1f /* scheduler not running, no tasks */
- l32i a2, a3, 0 /* a2 = port_interruptNesting */
- addi a2, a2, 1 /* increment nesting count */
- s32i a2, a3, 0 /* save nesting count */
- bnei a2, 1, .Lnested /* !=0 before incr, so nested */
- movi a2, pxCurrentTCB
- l32i a2, a2, 0 /* a2 = current TCB */
- beqz a2, 1f
- s32i a1, a2, TOPOFSTACK_OFFS /* pxCurrentTCB->pxTopOfStack = SP */
- movi a1, port_IntStackTop /* a1 = top of intr stack */
- .Lnested:
- 1:
- mov a0, a12 /* restore return addr and return */
- ret
- /*
- *******************************************************************************
- * _frxt_int_exit
- * void _frxt_int_exit(void)
- *
- * Implements the Xtensa RTOS porting layer's XT_RTOS_INT_EXIT function for
- * FreeRTOS. If required, calls vPortYieldFromInt() to perform task context
- * switching, restore the (possibly) new task's context, and return to the
- * exit dispatcher saved in the task's stack frame at XT_STK_EXIT.
- * May only be called from assembly code by the 'call0' instruction. Does not
- * return to caller.
- * See the description of the XT_RTOS_ENTER macro in xtensa_rtos.h.
- *
- *******************************************************************************
- */
- .globl _frxt_int_exit
- .type _frxt_int_exit,@function
- .align 4
- _frxt_int_exit:
- movi a2, port_xSchedulerRunning
- movi a3, port_interruptNesting
- rsil a0, XCHAL_EXCM_LEVEL /* lock out interrupts */
- l32i a2, a2, 0 /* a2 = port_xSchedulerRunning */
- beqz a2, .Lnoswitch /* scheduler not running, no tasks */
- l32i a2, a3, 0 /* a2 = port_interruptNesting */
- addi a2, a2, -1 /* decrement nesting count */
- s32i a2, a3, 0 /* save nesting count */
- bnez a2, .Lnesting /* !=0 after decr so still nested */
- movi a2, pxCurrentTCB
- l32i a2, a2, 0 /* a2 = current TCB */
- beqz a2, 1f /* no task ? go to dispatcher */
- l32i a1, a2, TOPOFSTACK_OFFS /* SP = pxCurrentTCB->pxTopOfStack */
- movi a2, port_switch_flag /* address of switch flag */
- l32i a3, a2, 0 /* a3 = port_switch_flag */
- beqz a3, .Lnoswitch /* flag = 0 means no switch reqd */
- movi a3, 0
- s32i a3, a2, 0 /* zero out the flag for next time */
- 1:
- /*
- Call0 ABI callee-saved regs a12-15 need to be saved before possible preemption.
- However a12-13 were already saved by _frxt_int_enter().
- */
- #ifdef __XTENSA_CALL0_ABI__
- s32i a14, a1, XT_STK_A14
- s32i a15, a1, XT_STK_A15
- #endif
- #ifdef __XTENSA_CALL0_ABI__
- call0 vPortYieldFromInt /* call dispatch inside the function; never returns */
- #else
- call4 vPortYieldFromInt /* this one returns */
- call0 _frxt_dispatch /* tail-call dispatcher */
- /* Never returns here. */
- #endif
- .Lnoswitch:
- /*
- If we came here then about to resume the interrupted task.
- */
- .Lnesting:
- /*
- We come here only if there was no context switch, that is if this
- is a nested interrupt, or the interrupted task was not preempted.
- In either case there's no need to load the SP.
- */
- /* Restore full context from interrupt stack frame */
- call0 _xt_context_restore
- /*
- Must return via the exit dispatcher corresponding to the entrypoint from which
- this was called. Interruptee's A0, A1, PS, PC are restored and the interrupt
- stack frame is deallocated in the exit dispatcher.
- */
- l32i a0, a1, XT_STK_EXIT
- ret
- /*
- **********************************************************************************************************
- * _frxt_timer_int
- * void _frxt_timer_int(void)
- *
- * Implements the Xtensa RTOS porting layer's XT_RTOS_TIMER_INT function for FreeRTOS.
- * Called every timer interrupt.
- * Manages the tick timer and calls xPortSysTickHandler() every tick.
- * See the detailed description of the XT_RTOS_ENTER macro in xtensa_rtos.h.
- *
- * Callable from C (obeys ABI conventions). Implemented in assmebly code for performance.
- *
- **********************************************************************************************************
- */
- .globl _frxt_timer_int
- .type _frxt_timer_int,@function
- .align 4
- _frxt_timer_int:
- /*
- Xtensa timers work by comparing a cycle counter with a preset value. Once the match occurs
- an interrupt is generated, and the handler has to set a new cycle count into the comparator.
- To avoid clock drift due to interrupt latency, the new cycle count is computed from the old,
- not the time the interrupt was serviced. However if a timer interrupt is ever serviced more
- than one tick late, it is necessary to process multiple ticks until the new cycle count is
- in the future, otherwise the next timer interrupt would not occur until after the cycle
- counter had wrapped (2^32 cycles later).
- do {
- ticks++;
- old_ccompare = read_ccompare_i();
- write_ccompare_i( old_ccompare + divisor );
- service one tick;
- diff = read_ccount() - old_ccompare;
- } while ( diff > divisor );
- */
- ENTRY(16)
- .L_xt_timer_int_catchup:
- /* Update the timer comparator for the next tick. */
- #ifdef XT_CLOCK_FREQ
- movi a2, XT_TICK_DIVISOR /* a2 = comparator increment */
- #else
- movi a3, _xt_tick_divisor
- l32i a2, a3, 0 /* a2 = comparator increment */
- #endif
- rsr a3, XT_CCOMPARE /* a3 = old comparator value */
- add a4, a3, a2 /* a4 = new comparator value */
- wsr a4, XT_CCOMPARE /* update comp. and clear interrupt */
- esync
- #ifdef __XTENSA_CALL0_ABI__
- /* Preserve a2 and a3 across C calls. */
- s32i a2, sp, 4
- s32i a3, sp, 8
- #endif
- /* Call the FreeRTOS tick handler (see port.c). */
- #ifdef __XTENSA_CALL0_ABI__
- call0 xPortSysTickHandler
- #else
- call4 xPortSysTickHandler
- #endif
- #ifdef __XTENSA_CALL0_ABI__
- /* Restore a2 and a3. */
- l32i a2, sp, 4
- l32i a3, sp, 8
- #endif
- /* Check if we need to process more ticks to catch up. */
- esync /* ensure comparator update complete */
- rsr a4, CCOUNT /* a4 = cycle count */
- sub a4, a4, a3 /* diff = ccount - old comparator */
- blt a2, a4, .L_xt_timer_int_catchup /* repeat while diff > divisor */
- RET(16)
- /*
- **********************************************************************************************************
- * _frxt_tick_timer_init
- * void _frxt_tick_timer_init(void)
- *
- * Initialize timer and timer interrrupt handler (_xt_tick_divisor_init() has already been been called).
- * Callable from C (obeys ABI conventions on entry).
- *
- **********************************************************************************************************
- */
- .globl _frxt_tick_timer_init
- .type _frxt_tick_timer_init,@function
- .align 4
- _frxt_tick_timer_init:
- ENTRY(16)
- /* Set up the periodic tick timer (assume enough time to complete init). */
- #ifdef XT_CLOCK_FREQ
- movi a3, XT_TICK_DIVISOR
- #else
- movi a2, _xt_tick_divisor
- l32i a3, a2, 0
- #endif
- rsr a2, CCOUNT /* current cycle count */
- add a2, a2, a3 /* time of first timer interrupt */
- wsr a2, XT_CCOMPARE /* set the comparator */
- /*
- Enable the timer interrupt at the device level. Don't write directly
- to the INTENABLE register because it may be virtualized.
- */
- #ifdef __XTENSA_CALL0_ABI__
- movi a2, XT_TIMER_INTEN
- call0 xt_ints_on
- #else
- movi a6, XT_TIMER_INTEN
- call4 xt_ints_on
- #endif
- RET(16)
- /*
- **********************************************************************************************************
- * DISPATCH THE HIGH READY TASK
- * void _frxt_dispatch(void)
- *
- * Switch context to the highest priority ready task, restore its state and dispatch control to it.
- *
- * This is a common dispatcher that acts as a shared exit path for all the context switch functions
- * including vPortYield() and vPortYieldFromInt(), all of which tail-call this dispatcher
- * (for windowed ABI vPortYieldFromInt() calls it indirectly via _frxt_int_exit() ).
- *
- * The Xtensa port uses different stack frames for solicited and unsolicited task suspension (see
- * comments on stack frames in xtensa_context.h). This function restores the state accordingly.
- * If restoring a task that solicited entry, restores the minimal state and leaves CPENABLE clear.
- * If restoring a task that was preempted, restores all state including the task's CPENABLE.
- *
- * Entry:
- * pxCurrentTCB points to the TCB of the task to suspend,
- * Because it is tail-called without a true function entrypoint, it needs no 'entry' instruction.
- *
- * Exit:
- * If incoming task called vPortYield() (solicited), this function returns as if from vPortYield().
- * If incoming task was preempted by an interrupt, this function jumps to exit dispatcher.
- *
- **********************************************************************************************************
- */
- .globl _frxt_dispatch
- .type _frxt_dispatch,@function
- .align 4
- _frxt_dispatch:
- #ifdef __XTENSA_CALL0_ABI__
- call0 vTaskSwitchContext // Get next TCB to resume
- movi a2, pxCurrentTCB
- #else
- movi a2, pxCurrentTCB
- call4 vTaskSwitchContext // Get next TCB to resume
- #endif
- l32i a3, a2, 0
- l32i sp, a3, TOPOFSTACK_OFFS /* SP = next_TCB->pxTopOfStack; */
- s32i a3, a2, 0
- /* Determine the type of stack frame. */
- l32i a2, sp, XT_STK_EXIT /* exit dispatcher or solicited flag */
- bnez a2, .L_frxt_dispatch_stk
- .L_frxt_dispatch_sol:
- /* Solicited stack frame. Restore minimal context and return from vPortYield(). */
- l32i a3, sp, XT_SOL_PS
- #ifdef __XTENSA_CALL0_ABI__
- l32i a12, sp, XT_SOL_A12
- l32i a13, sp, XT_SOL_A13
- l32i a14, sp, XT_SOL_A14
- l32i a15, sp, XT_SOL_A15
- #endif
- l32i a0, sp, XT_SOL_PC
- #if XCHAL_CP_NUM > 0
- /* Ensure wsr.CPENABLE is complete (should be, it was cleared on entry). */
- rsync
- #endif
- /* As soons as PS is restored, interrupts can happen. No need to sync PS. */
- wsr a3, PS
- #ifdef __XTENSA_CALL0_ABI__
- addi sp, sp, XT_SOL_FRMSZ
- ret
- #else
- retw
- #endif
- .L_frxt_dispatch_stk:
- #if XCHAL_CP_NUM > 0
- /* Restore CPENABLE from task's co-processor save area. */
- movi a3, pxCurrentTCB /* cp_state = */
- l32i a3, a3, 0
- l32i a2, a3, CP_TOPOFSTACK_OFFS /* StackType_t *pxStack; */
- l16ui a3, a2, XT_CPENABLE /* CPENABLE = cp_state->cpenable; */
- wsr a3, CPENABLE
- #endif
- /* Interrupt stack frame. Restore full context and return to exit dispatcher. */
- call0 _xt_context_restore
- /* In Call0 ABI, restore callee-saved regs (A12, A13 already restored). */
- #ifdef __XTENSA_CALL0_ABI__
- l32i a14, sp, XT_STK_A14
- l32i a15, sp, XT_STK_A15
- #endif
- #if XCHAL_CP_NUM > 0
- /* Ensure wsr.CPENABLE has completed. */
- rsync
- #endif
- /*
- Must return via the exit dispatcher corresponding to the entrypoint from which
- this was called. Interruptee's A0, A1, PS, PC are restored and the interrupt
- stack frame is deallocated in the exit dispatcher.
- */
- l32i a0, sp, XT_STK_EXIT
- ret
- /*
- **********************************************************************************************************
- * PERFORM A SOLICTED CONTEXT SWITCH (from a task)
- * void vPortYield(void)
- *
- * This function saves the minimal state needed for a solicited task suspension, clears CPENABLE,
- * then tail-calls the dispatcher _frxt_dispatch() to perform the actual context switch
- *
- * At Entry:
- * pxCurrentTCB points to the TCB of the task to suspend
- * Callable from C (obeys ABI conventions on entry).
- *
- * Does not return to caller.
- *
- **********************************************************************************************************
- */
- .globl vPortYield
- .type vPortYield,@function
- .align 4
- vPortYield:
- #ifdef __XTENSA_CALL0_ABI__
- addi sp, sp, -XT_SOL_FRMSZ
- #else
- entry sp, XT_SOL_FRMSZ
- #endif
- rsr a2, PS
- s32i a0, sp, XT_SOL_PC
- s32i a2, sp, XT_SOL_PS
- #ifdef __XTENSA_CALL0_ABI__
- s32i a12, sp, XT_SOL_A12 /* save callee-saved registers */
- s32i a13, sp, XT_SOL_A13
- s32i a14, sp, XT_SOL_A14
- s32i a15, sp, XT_SOL_A15
- #else
- /* Spill register windows. Calling xthal_window_spill() causes extra */
- /* spills and reloads, so we will set things up to call the _nw version */
- /* instead to save cycles. */
- movi a6, ~(PS_WOE_MASK|PS_INTLEVEL_MASK) /* spills a4-a7 if needed */
- and a2, a2, a6 /* clear WOE, INTLEVEL */
- addi a2, a2, XCHAL_EXCM_LEVEL /* set INTLEVEL */
- wsr a2, PS
- rsync
- call0 xthal_window_spill_nw
- l32i a2, sp, XT_SOL_PS /* restore PS */
- wsr a2, PS
- #endif
- rsil a2, XCHAL_EXCM_LEVEL /* disable low/med interrupts */
- #if XCHAL_CP_NUM > 0
- /* Save coprocessor callee-saved state (if any). At this point CPENABLE */
- /* should still reflect which CPs were in use (enabled). */
- call0 _xt_coproc_savecs
- #endif
- movi a2, pxCurrentTCB
- movi a3, 0
- l32i a2, a2, 0 /* a2 = pxCurrentTCB */
- s32i a3, sp, XT_SOL_EXIT /* 0 to flag as solicited frame */
- s32i sp, a2, TOPOFSTACK_OFFS /* pxCurrentTCB->pxTopOfStack = SP */
- #if XCHAL_CP_NUM > 0
- /* Clear CPENABLE, also in task's co-processor state save area. */
- l32i a2, a2, CP_TOPOFSTACK_OFFS /* a2 = pxCurrentTCB->cp_state */
- movi a3, 0
- wsr a3, CPENABLE
- beqz a2, 1f
- s16i a3, a2, XT_CPENABLE /* clear saved cpenable */
- 1:
- #endif
- /* Tail-call dispatcher. */
- call0 _frxt_dispatch
- /* Never reaches here. */
- /*
- **********************************************************************************************************
- * PERFORM AN UNSOLICITED CONTEXT SWITCH (from an interrupt)
- * void vPortYieldFromInt(void)
- *
- * This calls the context switch hook (removed), saves and clears CPENABLE, then tail-calls the dispatcher
- * _frxt_dispatch() to perform the actual context switch.
- *
- * At Entry:
- * Interrupted task context has been saved in an interrupt stack frame at pxCurrentTCB->pxTopOfStack.
- * pxCurrentTCB points to the TCB of the task to suspend,
- * Callable from C (obeys ABI conventions on entry).
- *
- * At Exit:
- * Windowed ABI defers the actual context switch until the stack is unwound to interrupt entry.
- * Call0 ABI tail-calls the dispatcher directly (no need to unwind) so does not return to caller.
- *
- **********************************************************************************************************
- */
- .globl vPortYieldFromInt
- .type vPortYieldFromInt,@function
- .align 4
- vPortYieldFromInt:
- ENTRY(16)
- #if XCHAL_CP_NUM > 0
- /* Save CPENABLE in task's co-processor save area, and clear CPENABLE. */
- movi a3, pxCurrentTCB /* cp_state = */
- l32i a3, a3, 0
- l32i a2, a3, CP_TOPOFSTACK_OFFS
- rsr a3, CPENABLE
- s16i a3, a2, XT_CPENABLE /* cp_state->cpenable = CPENABLE; */
- movi a3, 0
- wsr a3, CPENABLE /* disable all co-processors */
- #endif
- #ifdef __XTENSA_CALL0_ABI__
- /* Tail-call dispatcher. */
- call0 _frxt_dispatch
- /* Never reaches here. */
- #else
- RET(16)
- #endif
- /*
- **********************************************************************************************************
- * _frxt_task_coproc_state
- * void _frxt_task_coproc_state(void)
- *
- * Implements the Xtensa RTOS porting layer's XT_RTOS_CP_STATE function for FreeRTOS.
- *
- * May only be called when a task is running, not within an interrupt handler (returns 0 in that case).
- * May only be called from assembly code by the 'call0' instruction. Does NOT obey ABI conventions.
- * Returns in A15 a pointer to the base of the co-processor state save area for the current task.
- * See the detailed description of the XT_RTOS_ENTER macro in xtensa_rtos.h.
- *
- **********************************************************************************************************
- */
- #if XCHAL_CP_NUM > 0
- .globl _frxt_task_coproc_state
- .type _frxt_task_coproc_state,@function
- .align 4
- _frxt_task_coproc_state:
- movi a15, port_xSchedulerRunning /* if (port_xSchedulerRunning */
- l32i a15, a15, 0
- beqz a15, 1f
- movi a15, port_interruptNesting /* && port_interruptNesting == 0 */
- l32i a15, a15, 0
- bnez a15, 1f
- movi a15, pxCurrentTCB
- l32i a15, a15, 0 /* && pxCurrentTCB != 0) { */
- beqz a15, 2f
- l32i a15, a15, CP_TOPOFSTACK_OFFS
- ret
- 1: movi a15, 0
- 2: ret
- #endif /* XCHAL_CP_NUM > 0 */
|