| /* |
| * SPDX-FileCopyrightText: 2015-2019 Cadence Design Systems, Inc. |
| * |
| * SPDX-License-Identifier: MIT |
| * |
| * SPDX-FileContributor: 2016-2022 Espressif Systems (Shanghai) CO LTD |
| */ |
| /* |
| * Copyright (c) 2015-2019 Cadence Design Systems, Inc. |
| * |
| * 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. |
| */ |
| |
| #include "xtensa_rtos.h" |
| #include "sdkconfig.h" |
| #include "esp_idf_version.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 |
| .global port_IntStackTop |
| .global port_switch_flag |
| port_IntStack: |
| .space configISR_STACK_SIZE*portNUM_PROCESSORS /* This allocates stacks for each individual CPU. */ |
| port_IntStackTop: |
| .word 0 |
| port_switch_flag: |
| .space portNUM_PROCESSORS*4 /* One flag for each individual CPU. */ |
| |
| .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) |
| |
| getcoreid a3 |
| movi a2, port_switch_flag |
| addx4 a2, a3, a2 |
| |
| 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). |
| */ |
| getcoreid a4 |
| movi a2, port_xSchedulerRunning |
| addx4 a2, a4, a2 |
| movi a3, port_interruptNesting |
| addx4 a3, a4, a3 |
| 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 |
| addx4 a2, a4, a2 |
| l32i a2, a2, 0 /* a2 = current TCB */ |
| beqz a2, 1f |
| s32i a1, a2, TOPOFSTACK_OFFS /* pxCurrentTCB->pxTopOfStack = SP */ |
| movi a1, port_IntStack+configISR_STACK_SIZE /* a1 = top of intr stack for CPU 0 */ |
| movi a2, configISR_STACK_SIZE /* add configISR_STACK_SIZE * cpu_num to arrive at top of stack for cpu_num */ |
| mull a2, a4, a2 |
| add a1, a1, a2 /* for current proc */ |
| |
| #if (ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 2, 0)) |
| #ifdef CONFIG_FREERTOS_FPU_IN_ISR |
| #if XCHAL_CP_NUM > 0 |
| rsr a3, CPENABLE /* Restore thread scope CPENABLE */ |
| addi sp, sp,-4 /* ISR will manage FPU coprocessor by forcing */ |
| s32i a3, a1, 0 /* its trigger */ |
| #endif |
| #endif |
| #endif /* ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 2, 0) */ |
| |
| .Lnested: |
| 1: |
| #if (ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 2, 0)) |
| #ifdef CONFIG_FREERTOS_FPU_IN_ISR |
| #if XCHAL_CP_NUM > 0 |
| movi a3, 0 /* whilst ISRs pending keep CPENABLE exception active */ |
| wsr a3, CPENABLE |
| rsync |
| #endif |
| #endif |
| #endif /* ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 2, 0) */ |
| |
| 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: |
| |
| getcoreid a4 |
| movi a2, port_xSchedulerRunning |
| addx4 a2, a4, a2 |
| movi a3, port_interruptNesting |
| addx4 a3, a4, a3 |
| 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 */ |
| |
| #if (ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 2, 0)) |
| #ifdef CONFIG_FREERTOS_FPU_IN_ISR |
| #if XCHAL_CP_NUM > 0 |
| l32i a3, sp, 0 /* Grab last CPENABLE before leave ISR */ |
| addi sp, sp, 4 |
| wsr a3, CPENABLE |
| rsync /* ensure CPENABLE was modified */ |
| #endif |
| #endif |
| #endif /* ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 2, 0) */ |
| |
| movi a2, pxCurrentTCB |
| addx4 a2, a4, a2 |
| 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 */ |
| addx4 a2, a4, a2 /* point to flag for this cpu */ |
| 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. |
| * |
| ********************************************************************************************************** |
| */ |
| #ifdef CONFIG_FREERTOS_SYSTICK_USES_CCOUNT |
| .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) |
| |
| #ifdef CONFIG_PM_TRACE |
| movi a6, 1 /* = ESP_PM_TRACE_TICK */ |
| getcoreid a7 |
| call4 esp_pm_trace_enter |
| #endif // CONFIG_PM_TRACE |
| |
| .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_systick.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 */ |
| |
| #ifdef CONFIG_PM_TRACE |
| movi a6, 1 /* = ESP_PM_TRACE_TICK */ |
| getcoreid a7 |
| call4 esp_pm_trace_exit |
| #endif // CONFIG_PM_TRACE |
| |
| RET(16) |
| #endif // CONFIG_FREERTOS_SYSTICK_USES_CCOUNT |
| |
| /* |
| ********************************************************************************************************** |
| * _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). |
| * |
| ********************************************************************************************************** |
| */ |
| #ifdef CONFIG_FREERTOS_SYSTICK_USES_CCOUNT |
| .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) |
| #endif // CONFIG_FREERTOS_SYSTICK_USES_CCOUNT |
| |
| /* |
| ********************************************************************************************************** |
| * 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 |
| getcoreid a3 |
| addx4 a2, a3, a2 |
| #else |
| call4 vTaskSwitchContext // Get next TCB to resume |
| movi a2, pxCurrentTCB |
| getcoreid a3 |
| addx4 a2, a3, a2 |
| #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(). */ |
| #if XCHAL_HAVE_THREADPTR |
| l32i a2, sp, XT_SOL_THREADPTR |
| wur.threadptr a2 |
| #endif |
| 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 = */ |
| getcoreid a2 |
| addx4 a3, a2, a3 |
| 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 |
| #if XCHAL_HAVE_THREADPTR |
| rur.threadptr a2 |
| s32i a2, sp, XT_SOL_THREADPTR |
| #endif |
| #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 |
| getcoreid a3 |
| addx4 a2, a3, a2 |
| l32i a2, a2, 0 /* a2 = pxCurrentTCB */ |
| movi a3, 0 |
| 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 = */ |
| getcoreid a2 |
| addx4 a3, a2, a3 |
| 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: |
| |
| |
| /* We can use a3 as a scratchpad, the instances of code calling XT_RTOS_CP_STATE don't seem to need it saved. */ |
| getcoreid a3 |
| movi a15, port_xSchedulerRunning /* if (port_xSchedulerRunning */ |
| addx4 a15, a3,a15 |
| l32i a15, a15, 0 |
| beqz a15, 1f |
| movi a15, port_interruptNesting /* && port_interruptNesting == 0 */ |
| addx4 a15, a3, a15 |
| l32i a15, a15, 0 |
| bnez a15, 1f |
| |
| movi a15, pxCurrentTCB |
| addx4 a15, a3, a15 |
| 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 */ |