diff --git a/arch/xtensa/Kconfig b/arch/xtensa/Kconfig index a163955de3d..e90643c82fa 100644 --- a/arch/xtensa/Kconfig +++ b/arch/xtensa/Kconfig @@ -38,6 +38,17 @@ config XTENSA_CALL0_ABI The Window ABI is not supported. Only the CALL0 ABI is supported in the current implementation. +config XTENSA_CP_LAZY + bool "Lazy Co-processor Save" + default y + ---help--- + If this option is selected, then the co-processor state will be saved + only on context switches. As a consequency of this, co-processors + may NOT be used within interrupt handlers. If you must use co- + processors in interrupt handling, then disable this option. There + will be a significant interrupt latency performance impact if this + option is disabled. + config XTENSA_USE_OVLY bool default n diff --git a/arch/xtensa/src/common/xtensa.h b/arch/xtensa/src/common/xtensa.h index 6cd35c9d085..df81f64ba45 100644 --- a/arch/xtensa/src/common/xtensa.h +++ b/arch/xtensa/src/common/xtensa.h @@ -226,12 +226,16 @@ void lowconsole_init(void); /* Debug */ #ifdef CONFIG_ARCH_STACKDUMP -void xtensa_dumpstate(void); +void xtensa_dumpstate(void);s #else # define xtensa_dumpstate() #endif /* Common XTENSA functions */ +/* Initialization */ + +void xtensa_coproc_init(void); + /* IRQs */ uint32_t *xtensa_int_decode(uint32_t *regs); diff --git a/arch/xtensa/src/common/xtensa_context.S b/arch/xtensa/src/common/xtensa_context.S index 3e41111a604..50cfda14db3 100644 --- a/arch/xtensa/src/common/xtensa_context.S +++ b/arch/xtensa/src/common/xtensa_context.S @@ -148,7 +148,7 @@ _xtensa_context_save: #if XTENSA_EXTRA_SA_SIZE > 0 || !defined(CONFIG_XTENSA_CALL0_ABI) mov a9, a0 /* Preserve ret addr */ - #endif +#endif #ifndef CONFIG_XTENSA_CALL0_ABI /* To spill the reg windows, temp. need pre-interrupt stack ptr and @@ -208,6 +208,8 @@ _xtensa_context_save: ret + .size _xtensa_context_save, . - _xtensa_context_save + /**************************************************************************** * Name: xtensa_context_save * @@ -267,8 +269,11 @@ xtensa_context_save: l32i a0, a2, (4 * REG_A0) /* Recover return addess */ movi a2, 0 /* Return zero */ + ret + .size xtensa_context_save, . - xtensa_context_save + /**************************************************************************** * Name: _xtensa_context_restore * @@ -370,6 +375,8 @@ _xtensa_context_restore: ret + .size _xtensa_context_restore, . - _xtensa_context_restore + /**************************************************************************** * Name: xtensa_context_restore * @@ -418,5 +425,7 @@ xtensa_context_restore: rsr a0, EPC l32i a0, a2, (4 * REG_A0) /* Restore a0 */ l32i a2, a2, (4 * REG_A2) /* Restore A2 */ + rfe /* And return from "exception" */ + .size xtensa_context_restore, . - xtensa_context_restore diff --git a/arch/xtensa/src/common/xtensa_coproc.S b/arch/xtensa/src/common/xtensa_coproc.S index 7e9843c7960..18e1efda596 100644 --- a/arch/xtensa/src/common/xtensa_coproc.S +++ b/arch/xtensa/src/common/xtensa_coproc.S @@ -37,9 +37,350 @@ * Included Files ****************************************************************************/ +#include + +#include +#include + +#if XCHAL_CP_NUM > 0 + /**************************************************************************** * Public Functions ****************************************************************************/ .text +/**************************************************************************** + * Name: xtensa_coproc_init + * + * Description: + * Initializes.global co-processor management data, setting all co- + * processors to "unowned". Leaves CPENABLE as it found it (does NOT clear + * it). + * + * Called during initialization of the NuttX, before any threads run. + * + * This may be called from normal Xtensa single-threaded application code + * which might use co-processors. The Xtensa run-time initialization + * enables all co-processors. They must remain enabled here, else a co- + * processor exception might occur outside of a thread, which the exception + * handler doesn't expect. + * + * Entry Conditions: + * Xtensa single-threaded run-time environment is in effect. + * No thread is yet running. + * + * Exit conditions: + * None. + * + * Obeys ABI conventions per prototype: + * void xtensa_coproc_init(void) + * + ****************************************************************************/ + + .global xtensa_coproc_init + .type xtensa_coproc_init, @function + + .align 4 + .literal_position + .align 4 + +xtensa_coproc_init: + + /* Initialize thread co-processor ownerships to 0 (unowned). */ + + movi a2, _xt_coproc_owner_sa /* a2 = base of owner array */ + addi a3, a2, (XTENSA_CP_MAX*portNUM_PROCESSORS) << 2 /* a3 = top+1 of owner array */ + movi a4, 0 /* a4 = 0 (unowned) */ +1: s32i a4, a2, 0 + addi a2, a2, 4 + bltu a2, a3, 1b + + ret + + .size xtensa_coproc_init, . - xtensa_coproc_init + +/**************************************************************************** + * Name: xtensa_coproc_release + * + * Description: + * Releases any and all co-processors owned by a given thread. The thread + * is identified by it's co-processor state save area defined in + * xtensa_context.h . + * + * Must be called before a thread's co-proc save area is deleted to avoid + * memory corruption when the exception handler tries to save the state. + * May be called when a thread terminates or completes but does not delete + * the co-proc save area, to avoid the exception handler having to save the + * thread's co-proc state before another thread can use it (optimization). + * + * Entry Conditions: + * A2 = Pointer to base of co-processor state save area. + * + * Exit conditions: + * None. + * + * Obeys ABI conventions per prototype: + * void xtensa_coproc_release(void * coproc_sa_base) + * + ****************************************************************************/ + +#if 0 /* Not used in current design */ + .global xtensa_coproc_release + .type xtensa_coproc_release, @function + + .align 4 + .literal_position + .align 4 + +xtensa_coproc_release: + getcoreid a5 + movi a3, XTENSA_CP_MAX << 2 + mull a5, a5, a3 + movi a3, _xt_coproc_owner_sa /* a3 = base of owner array */ + add a3, a3, a5 + + addi a4, a3, XTENSA_CP_MAX << 2 /* a4 = top+1 of owner array */ + movi a5, 0 /* a5 = 0 (unowned) */ + + rsil a6, XCHAL_EXCM_LEVEL /* Lock interrupts */ + +1: l32i a7, a3, 0 /* a7 = owner at a3 */ + bne a2, a7, 2f /* if (coproc_sa_base == owner) */ + s32i a5, a3, 0 /* owner = unowned */ +2: addi a3, a3, 1<<2 /* a3 = next entry in owner array */ + bltu a3, a4, 1b /* repeat until end of array */ + +3: wsr a6, PS /* Restore interrupts */ + + ret + + .size xtensa_coproc_release, . - xtensa_coproc_release +#endif + +/**************************************************************************** + * Name: _xtensa_coproc_savestate + * + * Description: + * If there is a current thread and it has a coprocessor state save area, + * then save all callee-saved state into this area. This function is + * called from the solicited context switch handler. It calls a system- + * specific function to get the coprocessor save area base address. + * + * Entry Conditions: + * - The thread being switched out is still the current thread. + * - CPENABLE state reflects which coprocessors are active. + * - Registers have been saved/spilled already. + * + * Exit conditions: + * - All necessary CP callee-saved state has been saved. + * - Registers a2-a7, a13-a15 have been trashed. + * + * Must be called from assembly code only, using CALL0. + * + ****************************************************************************/ + + .extern _xt_coproc_sa_offset /* External reference */ + + .global _xtensa_coproc_savestate + .type _xtensa_coproc_savestate, @function + + .align 4 + .literal_position + .align 4 + +_xtensa_coproc_savestate: + + /* At entry, CPENABLE should be showing which CPs are enabled. */ + + rsr a2, CPENABLE /* a2 = which CPs are enabled */ + beqz a2, .Ldone /* Quick exit if none */ + mov a14, a0 /* Save return address */ + call0 XT_RTOS_CP_STATE /* Get address of CP save area */ + mov a0, a14 /* Restore return address */ + beqz a15, .Ldone /* if none then nothing to do */ + s16i a2, a15, XT_CP_CS_ST /* Save mask of CPs being stored */ + movi a13, _xt_coproc_sa_offset /* Array of CP save offsets */ + l32i a15, a15, XT_CP_ASA /* a15 = base of aligned save area */ + +#if XTENSA_CP0_SA_SIZE > 0 + bbci.l a2, 0, 2f /* CP 0 not enabled */ + l32i a14, a13, 0 /* a14 = _xt_coproc_sa_offset[0] */ + add a3, a14, a15 /* a3 = save area for CP 0 */ + xchal_cp0_store a3, a4, a5, a6, a7 continue=0 ofs=-1 select=XTHAL_SAS_TIE|XTHAL_SAS_NOCC|XTHAL_SAS_CALE alloc=XTHAL_SAS_ALL +2: +#endif + +#if XTENSA_CP1_SA_SIZE > 0 + bbci.l a2, 1, 2f /* CP 1 not enabled */ + l32i a14, a13, 4 /* a14 = _xt_coproc_sa_offset[1] */ + add a3, a14, a15 /* a3 = save area for CP 1 */ + xchal_cp1_store a3, a4, a5, a6, a7 continue=0 ofs=-1 select=XTHAL_SAS_TIE|XTHAL_SAS_NOCC|XTHAL_SAS_CALE alloc=XTHAL_SAS_ALL +2: +#endif + +#if XTENSA_CP2_SA_SIZE > 0 + bbci.l a2, 2, 2f + l32i a14, a13, 8 + add a3, a14, a15 + xchal_cp2_store a3, a4, a5, a6, a7 continue=0 ofs=-1 select=XTHAL_SAS_TIE|XTHAL_SAS_NOCC|XTHAL_SAS_CALE alloc=XTHAL_SAS_ALL +2: +#endif + +#if XTENSA_CP3_SA_SIZE > 0 + bbci.l a2, 3, 2f + l32i a14, a13, 12 + add a3, a14, a15 + xchal_cp3store a3, a4, a5, a6, a7 continue=0 ofs=-1 select=XTHAL_SAS_TIE|XTHAL_SAS_NOCC|XTHAL_SAS_CALE alloc=XTHAL_SAS_ALL +2: +#endif + +#if XTENSA_CP4_SA_SIZE > 0 + bbci.l a2, 4, 2f + l32i a14, a13, 16 + add a3, a14, a15 + xchal_cp4store a3, a4, a5, a6, a7 continue=0 ofs=-1 select=XTHAL_SAS_TIE|XTHAL_SAS_NOCC|XTHAL_SAS_CALE alloc=XTHAL_SAS_ALL +2: +#endif + +#if XTENSA_CP5_SA_SIZE > 0 + bbci.l a2, 5, 2f + l32i a14, a13, 20 + add a3, a14, a15 + xchal_cp5store a3, a4, a5, a6, a7 continue=0 ofs=-1 select=XTHAL_SAS_TIE|XTHAL_SAS_NOCC|XTHAL_SAS_CALE alloc=XTHAL_SAS_ALL +2: +#endif + +#if XTENSA_CP6_SA_SIZE > 0 + bbci.l a2, 6, 2f + l32i a14, a13, 24 + add a3, a14, a15 + xchal_cp6store a3, a4, a5, a6, a7 continue=0 ofs=-1 select=XTHAL_SAS_TIE|XTHAL_SAS_NOCC|XTHAL_SAS_CALE alloc=XTHAL_SAS_ALL +2: +#endif + +#if XTENSA_CP7_SA_SIZE > 0 + bbci.l a2, 7, 2f + l32i a14, a13, 28 + add a3, a14, a15 + xchal_cp7store a3, a4, a5, a6, a7 continue=0 ofs=-1 select=XTHAL_SAS_TIE|XTHAL_SAS_NOCC|XTHAL_SAS_CALE alloc=XTHAL_SAS_ALL +2: +#endif + +.Ldone: + ret + + .size _xtensa_coproc_savestate, . - _xtensa_coproc_savestate + +/**************************************************************************** + * Name: _xtensa_coproc_restorestate + * + * Description: + * Restore any callee-saved coprocessor state for the incoming thread. + * This function is called from coprocessor exception handling, when + * giving ownership to a thread that solicited a context switch earlier. + * It calls a system-specific function to get the coprocessor save area base address. + * + * Entry Conditions: + * - The incoming thread is set as the current thread. + * - CPENABLE is set up correctly for all required coprocessors. + * - a2 = mask of coprocessors to be restored. + * + * Exit conditions: + * - All necessary CP callee-saved state has been restored. + * - CPENABLE - unchanged. + * - Registers a2-a7, a13-a15 have been trashed. + * + * Must be called from assembly code only, using CALL0. + * + ****************************************************************************/ + + .global _xtensa_coproc_restorestate + .type _xtensa_coproc_restorestate, @function + + .align 4 + .literal_position + .align 4 + +_xtensa_coproc_restorestate: + + mov a14, a0 /* Save return address */ + call0 XT_RTOS_CP_STATE /* Get address of CP save area */ + mov a0, a14 /* Restore return address */ + beqz a15, .Ldone2 /* if none then nothing to do */ + l16ui a3, a15, XT_CP_CS_ST /* a3 = which CPs have been saved */ + xor a3, a3, a2 /* Clear the ones being restored */ + s32i a3, a15, XT_CP_CS_ST /* Update saved CP mask */ + movi a13, _xt_coproc_sa_offset /* Array of CP save offsets */ + l32i a15, a15, XT_CP_ASA /* a15 = base of aligned save area */ + +#if XTENSA_CP0_SA_SIZE + bbci.l a2, 0, 2f /* CP 0 not enabled */ + l32i a14, a13, 0 /* a14 = _xt_coproc_sa_offset[0] */ + add a3, a14, a15 /* a3 = save area for CP 0 */ + xchal_cp0_load a3, a4, a5, a6, a7 continue=0 ofs=-1 select=XTHAL_SAS_TIE|XTHAL_SAS_NOCC|XTHAL_SAS_CALE alloc=XTHAL_SAS_ALL +2: +#endif + +#if XTENSA_CP1_SA_SIZE + bbci.l a2, 1, 2f /* CP 1 not enabled */ + l32i a14, a13, 4 /* a14 = _xt_coproc_sa_offset[1] */ + add a3, a14, a15 /* a3 = save area for CP 1 */ + xchal_cp1_load a3, a4, a5, a6, a7 continue=0 ofs=-1 select=XTHAL_SAS_TIE|XTHAL_SAS_NOCC|XTHAL_SAS_CALE alloc=XTHAL_SAS_ALL +2: +#endif + +#if XTENSA_CP2_SA_SIZE + bbci.l a2, 2, 2f + l32i a14, a13, 8 + add a3, a14, a15 + xchal_cp2_load a3, a4, a5, a6, a7 continue=0 ofs=-1 select=XTHAL_SAS_TIE|XTHAL_SAS_NOCC|XTHAL_SAS_CALE alloc=XTHAL_SAS_ALL +2: +#endif + +#if XTENSA_CP3_SA_SIZE + bbci.l a2, 3, 2f + l32i a14, a13, 12 + add a3, a14, a15 + xchal_cp3_load a3, a4, a5, a6, a7 continue=0 ofs=-1 select=XTHAL_SAS_TIE|XTHAL_SAS_NOCC|XTHAL_SAS_CALE alloc=XTHAL_SAS_ALL +2: +#endif + +#if XTENSA_CP4_SA_SIZE + bbci.l a2, 4, 2f + l32i a14, a13, 16 + add a3, a14, a15 + xchal_cp4_load a3, a4, a5, a6, a7 continue=0 ofs=-1 select=XTHAL_SAS_TIE|XTHAL_SAS_NOCC|XTHAL_SAS_CALE alloc=XTHAL_SAS_ALL +2: +#endif + +#if XTENSA_CP5_SA_SIZE + bbci.l a2, 5, 2f + l32i a14, a13, 20 + add a3, a14, a15 + xchal_cp5_load a3, a4, a5, a6, a7 continue=0 ofs=-1 select=XTHAL_SAS_TIE|XTHAL_SAS_NOCC|XTHAL_SAS_CALE alloc=XTHAL_SAS_ALL +2: +#endif + +#if XTENSA_CP6_SA_SIZE + bbci.l a2, 6, 2f + l32i a14, a13, 24 + add a3, a14, a15 + xchal_cp6_load a3, a4, a5, a6, a7 continue=0 ofs=-1 select=XTHAL_SAS_TIE|XTHAL_SAS_NOCC|XTHAL_SAS_CALE alloc=XTHAL_SAS_ALL +2: +#endif + +#if XTENSA_CP7_SA_SIZE + bbci.l a2, 7, 2f + l32i a14, a13, 28 + add a3, a14, a15 + xchal_cp7_load a3, a4, a5, a6, a7 continue=0 ofs=-1 select=XTHAL_SAS_TIE|XTHAL_SAS_NOCC|XTHAL_SAS_CALE alloc=XTHAL_SAS_ALL +2: +#endif + +.Ldone2: + ret + + .size _xtensa_coproc_restorestate, . - _xtensa_coproc_restorestate + +#endif /* XCHAL_CP_NUM > 0 */ diff --git a/arch/xtensa/src/common/xtensa_initialize.c b/arch/xtensa/src/common/xtensa_initialize.c index 2041548b175..979ffd2c113 100644 --- a/arch/xtensa/src/common/xtensa_initialize.c +++ b/arch/xtensa/src/common/xtensa_initialize.c @@ -134,6 +134,12 @@ void up_initialize(void) xtensa_add_region(); +#if XCHAL_CP_NUM > 0 + /* Initialize co-processor management. */ + + xtensa_coproc_init(); +#endif + /* Initialize the interrupt subsystem */ xtensa_irq_initialize(); diff --git a/arch/xtensa/src/common/xtensa_inthandlers.S b/arch/xtensa/src/common/xtensa_inthandlers.S index b716c9f1f23..cc34881b785 100644 --- a/arch/xtensa/src/common/xtensa_inthandlers.S +++ b/arch/xtensa/src/common/xtensa_inthandlers.S @@ -170,7 +170,6 @@ beq a2, sp, 3f /* If timer interrupt then skip table */ /* Switch stacks */ -#warning REVIST: Should use register save are on stack. l32i a4, sp, (4 * REG_A1) /* Retrieve stack ptr and replace */ addi sp, a4, -(4 * XCPTCONTEXT_SIZE) diff --git a/arch/xtensa/src/common/xtensa_irqdispatch.c b/arch/xtensa/src/common/xtensa_irqdispatch.c index 2a3e9920e0b..1ed07feb216 100644 --- a/arch/xtensa/src/common/xtensa_irqdispatch.c +++ b/arch/xtensa/src/common/xtensa_irqdispatch.c @@ -46,6 +46,7 @@ #include #include +#include #include "xtensa.h" @@ -57,10 +58,19 @@ uint32_t *xtensa_irq_dispatch(int irq, uint32_t *regs) { - board_autoled_on(LED_INIRQ); #ifdef CONFIG_SUPPRESS_INTERRUPTS + board_autoled_on(LED_INIRQ); PANIC(); + #else +#if XCHAL_CP_NUM > 0 + /* Save the TCB of in case we need to save co-processor state */ + + struct tcb_s *tcb = this_task(); +#endif + + board_autoled_on(LED_INIRQ); + /* Nested interrupts are not supported */ DEBUGASSERT(CURRENT_REGS == NULL); @@ -71,24 +81,45 @@ uint32_t *xtensa_irq_dispatch(int irq, uint32_t *regs) CURRENT_REGS = regs; +#if XCHAL_CP_NUM > 0 && !defined(CONFIG_XTENSA_CP_LAZY) + /* Save the current co processor state on entry int each interrupt. */ + + esp32_coproc_savestate(tcb->xcp.cpstate); +#endif + /* Deliver the IRQ */ irq_dispatch(irq, regs); #if defined(CONFIG_ARCH_FPU) || defined(CONFIG_ARCH_ADDRENV) /* Check for a context switch. If a context switch occurred, then - * CURRENT_REGS will have a different value than it did on entry. If an - * interrupt level context switch has occurred, then restore the floating - * point state and the establish the correct address environment before - * returning from the interrupt. + * CURRENT_REGS will have a different value than it did on entry. */ if (regs != CURRENT_REGS) { -#ifdef CONFIG_ARCH_FPU - /* Restore floating point registers */ +#if XCHAL_CP_NUM > 0 +#ifdef CONFIG_XTENSA_CP_LAZY + /* If an interrupt level context switch has occurred, then save the + * co-processor state in in the suspended thread's co-processor save + * area. + * + * NOTE 1. The state of the co-processor has not been altered and + * still represents the to-be-suspended thread. + * NOTE 2. We saved a reference TCB of the original thread on entry. + */ - up_restorefpu((uint32_t *)CURRENT_REGS); + esp32_coproc_savestate(tcb->xcp.cpstate); +#endif + + /* Set up the co-processor state for the to-be-started thread. + * + * NOTE: The current thread for this CPU is the to-be-started + * thread. + */ + + tcb = this_task(); + esp32_coproc_restorestate(tcb->xcp.cpstate); #endif #ifdef CONFIG_ARCH_ADDRENV