+/*******************************************************************************\r
+Copyright (c) 2006-2015 Cadence Design Systems Inc.\r
+\r
+Permission is hereby granted, free of charge, to any person obtaining\r
+a copy of this software and associated documentation files (the\r
+"Software"), to deal in the Software without restriction, including\r
+without limitation the rights to use, copy, modify, merge, publish,\r
+distribute, sublicense, and/or sell copies of the Software, and to\r
+permit persons to whom the Software is furnished to do so, subject to\r
+the following conditions:\r
+\r
+The above copyright notice and this permission notice shall be included\r
+in all copies or substantial portions of the Software.\r
+\r
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\r
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\r
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\r
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\r
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\r
+--------------------------------------------------------------------------------\r
+\r
+ XTENSA VECTORS AND LOW LEVEL HANDLERS FOR AN RTOS\r
+\r
+ Xtensa low level exception and interrupt vectors and handlers for an RTOS.\r
+\r
+ Interrupt handlers and user exception handlers support interaction with\r
+ the RTOS by calling XT_RTOS_INT_ENTER and XT_RTOS_INT_EXIT before and\r
+ after user's specific interrupt handlers. These macros are defined in\r
+ xtensa_<rtos>.h to call suitable functions in a specific RTOS.\r
+\r
+ Users can install application-specific interrupt handlers for low and\r
+ medium level interrupts, by calling xt_set_interrupt_handler(). These\r
+ handlers can be written in C, and must obey C calling convention. The\r
+ handler table is indexed by the interrupt number. Each handler may be\r
+ provided with an argument. \r
+\r
+ Note that the system timer interrupt is handled specially, and is\r
+ dispatched to the RTOS-specific handler. This timer cannot be hooked\r
+ by application code.\r
+\r
+ Optional hooks are also provided to install a handler per level at \r
+ run-time, made available by compiling this source file with \r
+ '-DXT_INTEXC_HOOKS' (useful for automated testing).\r
+\r
+!! This file is a template that usually needs to be modified to handle !!\r
+!! application specific interrupts. Search USER_EDIT for helpful comments !!\r
+!! on where to insert handlers and how to write them. !!\r
+\r
+ Users can also install application-specific exception handlers in the\r
+ same way, by calling xt_set_exception_handler(). One handler slot is\r
+ provided for each exception type. Note that some exceptions are handled\r
+ by the porting layer itself, and cannot be taken over by application\r
+ code in this manner. These are the alloca, syscall, and coprocessor\r
+ exceptions.\r
+\r
+ The exception handlers can be written in C, and must follow C calling\r
+ convention. Each handler is passed a pointer to an exception frame as\r
+ its single argument. The exception frame is created on the stack, and\r
+ holds the saved context of the thread that took the exception. If the\r
+ handler returns, the context will be restored and the instruction that\r
+ caused the exception will be retried. If the handler makes any changes\r
+ to the saved state in the exception frame, the changes will be applied\r
+ when restoring the context.\r
+\r
+ Because Xtensa is a configurable architecture, this port supports all user\r
+ generated configurations (except restrictions stated in the release notes).\r
+ This is accomplished by conditional compilation using macros and functions\r
+ defined in the Xtensa HAL (hardware adaptation layer) for your configuration.\r
+ Only the relevant parts of this file will be included in your RTOS build.\r
+ For example, this file provides interrupt vector templates for all types and\r
+ all priority levels, but only the ones in your configuration are built.\r
+\r
+ NOTES on the use of 'call0' for long jumps instead of 'j':\r
+ 1. This file should be assembled with the -mlongcalls option to xt-xcc.\r
+ 2. The -mlongcalls compiler option causes 'call0 dest' to be expanded to\r
+ a sequence 'l32r a0, dest' 'callx0 a0' which works regardless of the\r
+ distance from the call to the destination. The linker then relaxes\r
+ it back to 'call0 dest' if it determines that dest is within range.\r
+ This allows more flexibility in locating code without the performance\r
+ overhead of the 'l32r' literal data load in cases where the destination\r
+ is in range of 'call0'. There is an additional benefit in that 'call0'\r
+ has a longer range than 'j' due to the target being word-aligned, so \r
+ the 'l32r' sequence is less likely needed.\r
+ 3. The use of 'call0' with -mlongcalls requires that register a0 not be \r
+ live at the time of the call, which is always the case for a function \r
+ call but needs to be ensured if 'call0' is used as a jump in lieu of 'j'.\r
+ 4. This use of 'call0' is independent of the C function call ABI.\r
+\r
+*******************************************************************************/\r
+\r
+#include "xtensa_rtos.h"\r
+\r
+\r
+/* Enable stack backtrace across exception/interrupt - see below */\r
+#define XT_DEBUG_BACKTRACE 1\r
+\r
+\r
+/*\r
+--------------------------------------------------------------------------------\r
+ Defines used to access _xtos_interrupt_table.\r
+--------------------------------------------------------------------------------\r
+*/\r
+#define XIE_HANDLER 0\r
+#define XIE_ARG 4\r
+#define XIE_SIZE 8\r
+\r
+/*\r
+--------------------------------------------------------------------------------\r
+ Macro extract_msb - return the input with only the highest bit set.\r
+\r
+ Input : "ain" - Input value, clobbered.\r
+ Output : "aout" - Output value, has only one bit set, MSB of "ain".\r
+ The two arguments must be different AR registers.\r
+--------------------------------------------------------------------------------\r
+*/\r
+\r
+ .macro extract_msb aout ain\r
+1:\r
+ addi \aout, \ain, -1 /* aout = ain - 1 */\r
+ and \ain, \ain, \aout /* ain = ain & aout */\r
+ bnez \ain, 1b /* repeat until ain == 0 */\r
+ addi \aout, \aout, 1 /* return aout + 1 */\r
+ .endm\r
+\r
+/*\r
+--------------------------------------------------------------------------------\r
+ Macro dispatch_c_isr - dispatch interrupts to user ISRs.\r
+ This will dispatch to user handlers (if any) that are registered in the\r
+ XTOS dispatch table (_xtos_interrupt_table). These handlers would have\r
+ been registered by calling _xtos_set_interrupt_handler(). There is one\r
+ exception - the timer interrupt used by the OS will not be dispatched\r
+ to a user handler - this must be handled by the caller of this macro.\r
+\r
+ Level triggered and software interrupts are automatically deasserted by\r
+ this code.\r
+\r
+ ASSUMPTIONS:\r
+ -- PS.INTLEVEL is set to "level" at entry\r
+ -- PS.EXCM = 0, C calling enabled\r
+\r
+ NOTE: For CALL0 ABI, a12-a15 have not yet been saved.\r
+\r
+ NOTE: This macro will use registers a0 and a2-a6. The arguments are:\r
+ level -- interrupt level\r
+ mask -- interrupt bitmask for this level\r
+--------------------------------------------------------------------------------\r
+*/\r
+\r
+ .macro dispatch_c_isr level mask\r
+\r
+ /* Get mask of pending, enabled interrupts at this level into a2. */\r
+\r
+.L_xt_user_int_&level&:\r
+ rsr a2, INTENABLE\r
+ rsr a3, INTERRUPT\r
+ movi a4, \mask\r
+ and a2, a2, a3\r
+ and a2, a2, a4\r
+ beqz a2, 9f /* nothing to do */\r
+\r
+ /* This bit of code provides a nice debug backtrace in the debugger.\r
+ It does take a few more instructions, so undef XT_DEBUG_BACKTRACE\r
+ if you want to save the cycles.\r
+ */\r
+ #if XT_DEBUG_BACKTRACE\r
+ #ifndef __XTENSA_CALL0_ABI__\r
+ rsr a0, EPC_1 + \level - 1 /* return address */\r
+ movi a4, 0xC0000000 /* constant with top 2 bits set (call size) */\r
+ or a0, a0, a4 /* set top 2 bits */\r
+ addx2 a0, a4, a0 /* clear top bit -- simulating call4 size */\r
+ #endif\r
+ #endif\r
+\r
+ #ifdef XT_INTEXC_HOOKS\r
+ /* Call interrupt hook if present to (pre)handle interrupts. */\r
+ movi a4, _xt_intexc_hooks\r
+ l32i a4, a4, \level << 2\r
+ beqz a4, 2f\r
+ #ifdef __XTENSA_CALL0_ABI__\r
+ callx0 a4\r
+ beqz a2, 9f\r
+ #else\r
+ mov a6, a2\r
+ callx4 a4\r
+ beqz a6, 9f\r
+ mov a2, a6\r
+ #endif\r
+2:\r
+ #endif\r
+\r
+ /* Now look up in the dispatch table and call user ISR if any. */\r
+ /* If multiple bits are set then MSB has highest priority. */\r
+\r
+ extract_msb a4, a2 /* a4 = MSB of a2, a2 trashed */\r
+\r
+ #ifdef XT_USE_SWPRI\r
+ /* Enable all interrupts at this level that are numerically higher\r
+ than the one we just selected, since they are treated as higher\r
+ priority.\r
+ */\r
+ movi a3, \mask /* a3 = all interrupts at this level */\r
+ add a2, a4, a4 /* a2 = a4 << 1 */\r
+ addi a2, a2, -1 /* a2 = mask of 1's <= a4 bit */\r
+ and a2, a2, a3 /* a2 = mask of all bits <= a4 at this level */\r
+ movi a3, _xt_intdata\r
+ l32i a6, a3, 4 /* a6 = _xt_vpri_mask */\r
+ neg a2, a2\r
+ addi a2, a2, -1 /* a2 = mask to apply */\r
+ and a5, a6, a2 /* mask off all bits <= a4 bit */\r
+ s32i a5, a3, 4 /* update _xt_vpri_mask */\r
+ rsr a3, INTENABLE\r
+ and a3, a3, a2 /* mask off all bits <= a4 bit */\r
+ wsr a3, INTENABLE\r
+ rsil a3, \level - 1 /* lower interrupt level by 1 */\r
+ #endif\r
+\r
+ movi a3, XT_TIMER_INTEN /* a3 = timer interrupt bit */\r
+ wsr a4, INTCLEAR /* clear sw or edge-triggered interrupt */\r
+ beq a3, a4, 7f /* if timer interrupt then skip table */\r
+\r
+ find_ms_setbit a3, a4, a3, 0 /* a3 = interrupt number */\r
+\r
+ movi a4, _xt_interrupt_table\r
+ addx8 a3, a3, a4 /* a3 = address of interrupt table entry */\r
+ l32i a4, a3, XIE_HANDLER /* a4 = handler address */\r
+ #ifdef __XTENSA_CALL0_ABI__\r
+ mov a12, a6 /* save in callee-saved reg */\r
+ l32i a2, a3, XIE_ARG /* a2 = handler arg */\r
+ callx0 a4 /* call handler */\r
+ mov a2, a12\r
+ #else\r
+ mov a2, a6 /* save in windowed reg */\r
+ l32i a6, a3, XIE_ARG /* a6 = handler arg */\r
+ callx4 a4 /* call handler */\r
+ #endif\r
+\r
+ #ifdef XT_USE_SWPRI\r
+ j 8f\r
+ #else\r
+ j .L_xt_user_int_&level& /* check for more interrupts */\r
+ #endif\r
+\r
+7:\r
+\r
+ .ifeq XT_TIMER_INTPRI - \level\r
+.L_xt_user_int_timer_&level&:\r
+ /*\r
+ Interrupt handler for the RTOS tick timer if at this level.\r
+ We'll be reading the interrupt state again after this call\r
+ so no need to preserve any registers except a6 (vpri_mask).\r
+ */\r
+\r
+ #ifdef __XTENSA_CALL0_ABI__\r
+ mov a12, a6\r
+ call0 XT_RTOS_TIMER_INT\r
+ mov a2, a12\r
+ #else\r
+ mov a2, a6\r
+ call4 XT_RTOS_TIMER_INT\r
+ #endif\r
+ .endif\r
+\r
+ #ifdef XT_USE_SWPRI\r
+ j 8f\r
+ #else\r
+ j .L_xt_user_int_&level& /* check for more interrupts */\r
+ #endif\r
+\r
+ #ifdef XT_USE_SWPRI\r
+8:\r
+ /* Restore old value of _xt_vpri_mask from a2. Also update INTENABLE from\r
+ virtual _xt_intenable which _could_ have changed during interrupt\r
+ processing. */\r
+\r
+ movi a3, _xt_intdata\r
+ l32i a4, a3, 0 /* a4 = _xt_intenable */\r
+ s32i a2, a3, 4 /* update _xt_vpri_mask */\r
+ and a4, a4, a2 /* a4 = masked intenable */\r
+ wsr a4, INTENABLE /* update INTENABLE */\r
+ #endif\r
+\r
+9:\r
+ /* done */\r
+\r
+ .endm\r
+\r
+\r
+/*\r
+--------------------------------------------------------------------------------\r
+ Panic handler.\r
+ Should be reached by call0 (preferable) or jump only. If call0, a0 says where \r
+ from. If on simulator, display panic message and abort, else loop indefinitely.\r
+--------------------------------------------------------------------------------\r
+*/\r
+\r
+ .text\r
+ .global _xt_panic\r
+ .type _xt_panic,@function\r
+ .align 4\r
+\r
+_xt_panic:\r
+ #ifdef XT_SIMULATOR\r
+ addi a4, a0, -3 /* point to call0 */\r
+ movi a3, _xt_panic_message\r
+ movi a2, SYS_log_msg\r
+ simcall\r
+ movi a2, SYS_gdb_abort\r
+ simcall\r
+ #else\r
+ rsil a2, XCHAL_EXCM_LEVEL /* disable all low & med ints */\r
+1: j 1b /* loop infinitely */\r
+ #endif\r
+\r
+ .section .rodata, "a"\r
+ .align 4\r
+\r
+_xt_panic_message:\r
+ .string "\n*** _xt_panic() was called from 0x%08x or jumped to. ***\n"\r
+\r
+\r
+/*\r
+--------------------------------------------------------------------------------\r
+ Hooks to dynamically install handlers for exceptions and interrupts.\r
+ Allows automated regression frameworks to install handlers per test.\r
+ Consists of an array of function pointers indexed by interrupt level, \r
+ with index 0 containing the entry for user exceptions.\r
+ Initialized with all 0s, meaning no handler is installed at each level.\r
+ See comment in xtensa_rtos.h for more details.\r
+--------------------------------------------------------------------------------\r
+*/\r
+\r
+ #ifdef XT_INTEXC_HOOKS\r
+ .data\r
+ .global _xt_intexc_hooks\r
+ .type _xt_intexc_hooks,@object\r
+ .align 4\r
+\r
+_xt_intexc_hooks:\r
+ .fill XT_INTEXC_HOOK_NUM, 4, 0\r
+ #endif\r
+\r
+\r
+/*\r
+--------------------------------------------------------------------------------\r
+ EXCEPTION AND LEVEL 1 INTERRUPT VECTORS AND LOW LEVEL HANDLERS\r
+ (except window exception vectors).\r
+\r
+ Each vector goes at a predetermined location according to the Xtensa\r
+ hardware configuration, which is ensured by its placement in a special\r
+ section known to the Xtensa linker support package (LSP). It performs\r
+ the minimum necessary before jumping to the handler in the .text section.\r
+\r
+ The corresponding handler goes in the normal .text section. It sets up\r
+ the appropriate stack frame, saves a few vector-specific registers and\r
+ calls XT_RTOS_INT_ENTER to save the rest of the interrupted context\r
+ and enter the RTOS, then sets up a C environment. It then calls the\r
+ user's interrupt handler code (which may be coded in C) and finally \r
+ calls XT_RTOS_INT_EXIT to transfer control to the RTOS for scheduling.\r
+\r
+ While XT_RTOS_INT_EXIT does not return directly to the interruptee,\r
+ eventually the RTOS scheduler will want to dispatch the interrupted\r
+ task or handler. The scheduler will return to the exit point that was\r
+ saved in the interrupt stack frame at XT_STK_EXIT.\r
+--------------------------------------------------------------------------------\r
+*/\r
+\r
+\r
+/*\r
+--------------------------------------------------------------------------------\r
+Debug Exception.\r
+--------------------------------------------------------------------------------\r
+*/\r
+\r
+#if XCHAL_HAVE_DEBUG\r
+\r
+ .begin literal_prefix .DebugExceptionVector\r
+ .section .DebugExceptionVector.text, "ax"\r
+ .global _DebugExceptionVector\r
+ .align 4\r
+\r
+_DebugExceptionVector:\r
+\r
+ #ifdef XT_SIMULATOR\r
+ /*\r
+ In the simulator, let the debugger (if any) handle the debug exception,\r
+ or simply stop the simulation:\r
+ */\r
+ wsr a2, EXCSAVE+XCHAL_DEBUGLEVEL /* save a2 where sim expects it */\r
+ movi a2, SYS_gdb_enter_sktloop\r
+ simcall /* have ISS handle debug exc. */\r
+ #elif 0 /* change condition to 1 to use the HAL minimal debug handler */\r
+ wsr a3, EXCSAVE+XCHAL_DEBUGLEVEL\r
+ movi a3, xthal_debugexc_defhndlr_nw /* use default debug handler */\r
+ jx a3\r
+ #else\r
+ wsr a0, EXCSAVE+XCHAL_DEBUGLEVEL /* save original a0 somewhere */\r
+ call0 _xt_panic /* does not return */\r
+ rfi XCHAL_DEBUGLEVEL /* make a0 point here not later */\r
+ #endif\r
+\r
+ .end literal_prefix\r
+\r
+#endif\r
+\r
+/*\r
+--------------------------------------------------------------------------------\r
+Double Exception.\r
+Double exceptions are not a normal occurrence. They indicate a bug of some kind.\r
+--------------------------------------------------------------------------------\r
+*/\r
+\r
+#ifdef XCHAL_DOUBLEEXC_VECTOR_VADDR\r
+\r
+ .begin literal_prefix .DoubleExceptionVector\r
+ .section .DoubleExceptionVector.text, "ax"\r
+ .global _DoubleExceptionVector\r
+ .align 4\r
+\r
+_DoubleExceptionVector:\r
+\r
+ #if XCHAL_HAVE_DEBUG\r
+ break 1, 4 /* unhandled double exception */\r
+ #endif\r
+ call0 _xt_panic /* does not return */\r
+ rfde /* make a0 point here not later */\r
+\r
+ .end literal_prefix\r
+\r
+#endif /* XCHAL_DOUBLEEXC_VECTOR_VADDR */\r
+\r
+/*\r
+--------------------------------------------------------------------------------\r
+Kernel Exception (including Level 1 Interrupt from kernel mode).\r
+--------------------------------------------------------------------------------\r
+*/\r
+\r
+ .begin literal_prefix .KernelExceptionVector\r
+ .section .KernelExceptionVector.text, "ax"\r
+ .global _KernelExceptionVector\r
+ .align 4\r
+\r
+_KernelExceptionVector:\r
+\r
+ wsr a0, EXCSAVE_1 /* preserve a0 */\r
+ call0 _xt_kernel_exc /* kernel exception handler */\r
+ /* never returns here - call0 is used as a jump (see note at top) */\r
+\r
+ .end literal_prefix\r
+\r
+ .text\r
+ .align 4\r
+\r
+_xt_kernel_exc:\r
+ #if XCHAL_HAVE_DEBUG\r
+ break 1, 0 /* unhandled kernel exception */\r
+ #endif\r
+ call0 _xt_panic /* does not return */\r
+ rfe /* make a0 point here not there */\r
+\r
+\r
+/*\r
+--------------------------------------------------------------------------------\r
+User Exception (including Level 1 Interrupt from user mode).\r
+--------------------------------------------------------------------------------\r
+*/\r
+\r
+ .begin literal_prefix .UserExceptionVector\r
+ .section .UserExceptionVector.text, "ax"\r
+ .global _UserExceptionVector\r
+ .type _UserExceptionVector,@function\r
+ .align 4\r
+\r
+_UserExceptionVector:\r
+\r
+ wsr a0, EXCSAVE_1 /* preserve a0 */\r
+ call0 _xt_user_exc /* user exception handler */\r
+ /* never returns here - call0 is used as a jump (see note at top) */\r
+\r
+ .end literal_prefix\r
+\r
+/*\r
+--------------------------------------------------------------------------------\r
+ Insert some waypoints for jumping beyond the signed 8-bit range of\r
+ conditional branch instructions, so the conditional branchces to specific\r
+ exception handlers are not taken in the mainline. Saves some cycles in the\r
+ mainline.\r
+--------------------------------------------------------------------------------\r
+*/\r
+\r
+ .text\r
+\r
+ #if XCHAL_HAVE_WINDOWED\r
+ .align 4\r
+_xt_to_alloca_exc:\r
+ call0 _xt_alloca_exc /* in window vectors section */\r
+ /* never returns here - call0 is used as a jump (see note at top) */\r
+ #endif\r
+\r
+ .align 4\r
+_xt_to_syscall_exc:\r
+ call0 _xt_syscall_exc\r
+ /* never returns here - call0 is used as a jump (see note at top) */\r
+\r
+ #if XCHAL_CP_NUM > 0\r
+ .align 4\r
+_xt_to_coproc_exc:\r
+ call0 _xt_coproc_exc\r
+ /* never returns here - call0 is used as a jump (see note at top) */\r
+ #endif\r
+\r
+\r
+/*\r
+--------------------------------------------------------------------------------\r
+ User exception handler.\r
+--------------------------------------------------------------------------------\r
+*/\r
+\r
+ .type _xt_user_exc,@function\r
+ .align 4\r
+\r
+_xt_user_exc:\r
+\r
+ /* If level 1 interrupt then jump to the dispatcher */\r
+ rsr a0, EXCCAUSE\r
+ beqi a0, EXCCAUSE_LEVEL1INTERRUPT, _xt_lowint1\r
+\r
+ /* Handle any coprocessor exceptions. Rely on the fact that exception\r
+ numbers above EXCCAUSE_CP0_DISABLED all relate to the coprocessors.\r
+ */\r
+ #if XCHAL_CP_NUM > 0\r
+ bgeui a0, EXCCAUSE_CP0_DISABLED, _xt_to_coproc_exc\r
+ #endif\r
+\r
+ /* Handle alloca and syscall exceptions */\r
+ #if XCHAL_HAVE_WINDOWED\r
+ beqi a0, EXCCAUSE_ALLOCA, _xt_to_alloca_exc\r
+ #endif\r
+ beqi a0, EXCCAUSE_SYSCALL, _xt_to_syscall_exc\r
+\r
+ /* Handle all other exceptions. All can have user-defined handlers. */\r
+ /* NOTE: we'll stay on the user stack for exception handling. */\r
+\r
+ /* Allocate exception frame and save minimal context. */\r
+ mov a0, sp\r
+ addi sp, sp, -XT_STK_FRMSZ\r
+ s32i a0, sp, XT_STK_A1\r
+ #if XCHAL_HAVE_WINDOWED\r
+ s32e a0, sp, -12 /* for debug backtrace */\r
+ #endif\r
+ rsr a0, PS /* save interruptee's PS */\r
+ s32i a0, sp, XT_STK_PS\r
+ rsr a0, EPC_1 /* save interruptee's PC */\r
+ s32i a0, sp, XT_STK_PC\r
+ rsr a0, EXCSAVE_1 /* save interruptee's a0 */\r
+ s32i a0, sp, XT_STK_A0\r
+ #if XCHAL_HAVE_WINDOWED\r
+ s32e a0, sp, -16 /* for debug backtrace */\r
+ #endif\r
+ s32i a12, sp, XT_STK_A12 /* _xt_context_save requires A12- */\r
+ s32i a13, sp, XT_STK_A13 /* A13 to have already been saved */\r
+ call0 _xt_context_save\r
+\r
+ /* Save exc cause and vaddr into exception frame */\r
+ rsr a0, EXCCAUSE\r
+ s32i a0, sp, XT_STK_EXCCAUSE\r
+ rsr a0, EXCVADDR\r
+ s32i a0, sp, XT_STK_EXCVADDR\r
+\r
+ /* Set up PS for C, reenable hi-pri interrupts, and clear EXCM. */\r
+ #ifdef __XTENSA_CALL0_ABI__\r
+ movi a0, PS_INTLEVEL(XCHAL_EXCM_LEVEL) | PS_UM\r
+ #else\r
+ movi a0, PS_INTLEVEL(XCHAL_EXCM_LEVEL) | PS_UM | PS_WOE\r
+ #endif\r
+ wsr a0, PS\r
+\r
+ #ifdef XT_DEBUG_BACKTRACE\r
+ #ifndef __XTENSA_CALL0_ABI__\r
+ rsr a0, EPC_1 /* return address for debug backtrace */\r
+ movi a5, 0xC0000000 /* constant with top 2 bits set (call size) */\r
+ rsync /* wait for WSR.PS to complete */\r
+ or a0, a0, a5 /* set top 2 bits */\r
+ addx2 a0, a5, a0 /* clear top bit -- thus simulating call4 size */\r
+ #else\r
+ rsync /* wait for WSR.PS to complete */\r
+ #endif\r
+ #endif\r
+\r
+ rsr a2, EXCCAUSE /* recover exc cause */\r
+\r
+ #ifdef XT_INTEXC_HOOKS\r
+ /*\r
+ Call exception hook to pre-handle exceptions (if installed).\r
+ Pass EXCCAUSE in a2, and check result in a2 (if -1, skip default handling).\r
+ */\r
+ movi a4, _xt_intexc_hooks\r
+ l32i a4, a4, 0 /* user exception hook index 0 */\r
+ beqz a4, 1f\r
+.Ln_xt_user_exc_call_hook:\r
+ #ifdef __XTENSA_CALL0_ABI__\r
+ callx0 a4\r
+ beqi a2, -1, .L_xt_user_done\r
+ #else\r
+ mov a6, a2\r
+ callx4 a4\r
+ beqi a6, -1, .L_xt_user_done\r
+ mov a2, a6\r
+ #endif\r
+1:\r
+ #endif\r
+\r
+ rsr a2, EXCCAUSE /* recover exc cause */\r
+ movi a3, _xt_exception_table\r
+ addx4 a4, a2, a3 /* a4 = address of exception table entry */\r
+ l32i a4, a4, 0 /* a4 = handler address */\r
+ #ifdef __XTENSA_CALL0_ABI__\r
+ mov a2, sp /* a2 = pointer to exc frame */\r
+ callx0 a4 /* call handler */\r
+ #else\r
+ mov a6, sp /* a6 = pointer to exc frame */\r
+ callx4 a4 /* call handler */\r
+ #endif\r
+\r
+.L_xt_user_done:\r
+\r
+ /* Restore context and return */\r
+ call0 _xt_context_restore\r
+ l32i a0, sp, XT_STK_PS /* retrieve interruptee's PS */\r
+ wsr a0, PS\r
+ l32i a0, sp, XT_STK_PC /* retrieve interruptee's PC */\r
+ wsr a0, EPC_1\r
+ l32i a0, sp, XT_STK_A0 /* retrieve interruptee's A0 */\r
+ l32i sp, sp, XT_STK_A1 /* remove exception frame */\r
+ rsync /* ensure PS and EPC written */\r
+ rfe /* PS.EXCM is cleared */\r
+\r
+\r
+/*\r
+--------------------------------------------------------------------------------\r
+ Exit point for dispatch. Saved in interrupt stack frame at XT_STK_EXIT\r
+ on entry and used to return to a thread or interrupted interrupt handler.\r
+--------------------------------------------------------------------------------\r
+*/\r
+\r
+ .global _xt_user_exit\r
+ .type _xt_user_exit,@function\r
+ .align 4\r
+_xt_user_exit:\r
+ l32i a0, sp, XT_STK_PS /* retrieve interruptee's PS */\r
+ wsr a0, PS\r
+ l32i a0, sp, XT_STK_PC /* retrieve interruptee's PC */\r
+ wsr a0, EPC_1\r
+ l32i a0, sp, XT_STK_A0 /* retrieve interruptee's A0 */\r
+ l32i sp, sp, XT_STK_A1 /* remove interrupt stack frame */\r
+ rsync /* ensure PS and EPC written */\r
+ rfe /* PS.EXCM is cleared */\r
+\r
+\r
+/*\r
+--------------------------------------------------------------------------------\r
+Syscall Exception Handler (jumped to from User Exception Handler).\r
+Syscall 0 is required to spill the register windows (no-op in Call 0 ABI).\r
+Only syscall 0 is handled here. Other syscalls return -1 to caller in a2.\r
+--------------------------------------------------------------------------------\r
+*/\r
+\r
+ .text\r
+ .type _xt_syscall_exc,@function\r
+ .align 4\r
+_xt_syscall_exc:\r
+\r
+ #ifdef __XTENSA_CALL0_ABI__\r
+ /*\r
+ Save minimal regs for scratch. Syscall 0 does nothing in Call0 ABI.\r
+ Use a minimal stack frame (16B) to save A2 & A3 for scratch.\r
+ PS.EXCM could be cleared here, but unlikely to improve worst-case latency.\r
+ rsr a0, PS\r
+ addi a0, a0, -PS_EXCM_MASK\r
+ wsr a0, PS\r
+ */\r
+ addi sp, sp, -16\r
+ s32i a2, sp, 8\r
+ s32i a3, sp, 12\r
+ #else /* Windowed ABI */\r
+ /*\r
+ Save necessary context and spill the register windows.\r
+ PS.EXCM is still set and must remain set until after the spill.\r
+ Reuse context save function though it saves more than necessary.\r
+ For this reason, a full interrupt stack frame is allocated.\r
+ */\r
+ addi sp, sp, -XT_STK_FRMSZ /* allocate interrupt stack frame */\r
+ s32i a12, sp, XT_STK_A12 /* _xt_context_save requires A12- */\r
+ s32i a13, sp, XT_STK_A13 /* A13 to have already been saved */\r
+ call0 _xt_context_save\r
+ #endif\r
+\r
+ /*\r
+ Grab the interruptee's PC and skip over the 'syscall' instruction.\r
+ If it's at the end of a zero-overhead loop and it's not on the last\r
+ iteration, decrement loop counter and skip to beginning of loop.\r
+ */\r
+ rsr a2, EPC_1 /* a2 = PC of 'syscall' */\r
+ addi a3, a2, 3 /* ++PC */\r
+ #if XCHAL_HAVE_LOOPS\r
+ rsr a0, LEND /* if (PC == LEND */\r
+ bne a3, a0, 1f\r
+ rsr a0, LCOUNT /* && LCOUNT != 0) */\r
+ beqz a0, 1f /* { */\r
+ addi a0, a0, -1 /* --LCOUNT */\r
+ rsr a3, LBEG /* PC = LBEG */\r
+ wsr a0, LCOUNT /* } */\r
+ #endif\r
+1: wsr a3, EPC_1 /* update PC */\r
+\r
+ /* Restore interruptee's context and return from exception. */\r
+ #ifdef __XTENSA_CALL0_ABI__\r
+ l32i a2, sp, 8\r
+ l32i a3, sp, 12\r
+ addi sp, sp, 16\r
+ #else\r
+ call0 _xt_context_restore\r
+ addi sp, sp, XT_STK_FRMSZ\r
+ #endif\r
+ movi a0, -1\r
+ movnez a2, a0, a2 /* return -1 if not syscall 0 */\r
+ rsr a0, EXCSAVE_1\r
+ rfe\r
+\r
+/*\r
+--------------------------------------------------------------------------------\r
+Co-Processor Exception Handler (jumped to from User Exception Handler).\r
+These exceptions are generated by co-processor instructions, which are only\r
+allowed in thread code (not in interrupts or kernel code). This restriction is \r
+deliberately imposed to reduce the burden of state-save/restore in interrupts.\r
+--------------------------------------------------------------------------------\r
+*/\r
+#if XCHAL_CP_NUM > 0\r
+\r
+ .section .rodata, "a"\r
+\r
+/* Offset to CP n save area in thread's CP save area. */\r
+ .global _xt_coproc_sa_offset\r
+ .type _xt_coproc_sa_offset,@object\r
+ .align 16 /* minimize crossing cache boundaries */\r
+_xt_coproc_sa_offset:\r
+ .word XT_CP0_SA, XT_CP1_SA, XT_CP2_SA, XT_CP3_SA\r
+ .word XT_CP4_SA, XT_CP5_SA, XT_CP6_SA, XT_CP7_SA\r
+\r
+/* Bitmask for CP n's CPENABLE bit. */\r
+ .type _xt_coproc_mask,@object\r
+ .align 16,,8 /* try to keep it all in one cache line */\r
+ .set i, 0\r
+_xt_coproc_mask:\r
+ .rept XCHAL_CP_MAX\r
+ .long (i<<16) | (1<<i) // upper 16-bits = i, lower = bitmask\r
+ .set i, i+1\r
+ .endr\r
+\r
+ .data\r
+\r
+/* Owner thread of CP n, identified by thread's CP save area (0 = unowned). */\r
+ .global _xt_coproc_owner_sa\r
+ .type _xt_coproc_owner_sa,@object\r
+ .align 16,,XCHAL_CP_MAX<<2 /* minimize crossing cache boundaries */\r
+_xt_coproc_owner_sa:\r
+ .space XCHAL_CP_MAX << 2\r
+\r
+ .text\r
+\r
+\r
+ .align 4\r
+.L_goto_invalid:\r
+ j .L_xt_coproc_invalid /* not in a thread (invalid) */\r
+ .align 4\r
+.L_goto_done:\r
+ j .L_xt_coproc_done\r
+\r
+\r
+/*\r
+--------------------------------------------------------------------------------\r
+ Coprocessor exception handler.\r
+ At entry, only a0 has been saved (in EXCSAVE_1).\r
+--------------------------------------------------------------------------------\r
+*/\r
+\r
+ .type _xt_coproc_exc,@function\r
+ .align 4\r
+\r
+_xt_coproc_exc:\r
+\r
+ /* Allocate interrupt stack frame and save minimal context. */\r
+ mov a0, sp /* sp == a1 */\r
+ addi sp, sp, -XT_STK_FRMSZ /* allocate interrupt stack frame */\r
+ s32i a0, sp, XT_STK_A1 /* save pre-interrupt SP */\r
+ #if XCHAL_HAVE_WINDOWED\r
+ s32e a0, sp, -12 /* for debug backtrace */\r
+ #endif\r
+ rsr a0, PS /* save interruptee's PS */\r
+ s32i a0, sp, XT_STK_PS\r
+ rsr a0, EPC_1 /* save interruptee's PC */\r
+ s32i a0, sp, XT_STK_PC\r
+ rsr a0, EXCSAVE_1 /* save interruptee's a0 */\r
+ s32i a0, sp, XT_STK_A0\r
+ #if XCHAL_HAVE_WINDOWED\r
+ s32e a0, sp, -16 /* for debug backtrace */\r
+ #endif\r
+ movi a0, _xt_user_exit /* save exit point for dispatch */\r
+ s32i a0, sp, XT_STK_EXIT\r
+\r
+ rsr a0, EXCCAUSE\r
+ s32i a5, sp, XT_STK_A5 /* save a5 */\r
+ addi a5, a0, -EXCCAUSE_CP0_DISABLED /* a5 = CP index */\r
+\r
+ /* Save a few more of interruptee's registers (a5 was already saved). */\r
+ s32i a2, sp, XT_STK_A2\r
+ s32i a3, sp, XT_STK_A3\r
+ s32i a4, sp, XT_STK_A4\r
+ s32i a15, sp, XT_STK_A15\r
+\r
+ /* Get co-processor state save area of new owner thread. */\r
+ call0 XT_RTOS_CP_STATE /* a15 = new owner's save area */\r
+ beqz a15, .L_goto_invalid /* not in a thread (invalid) */\r
+\r
+ /* Enable the co-processor's bit in CPENABLE. */\r
+ movi a0, _xt_coproc_mask\r
+ rsr a4, CPENABLE /* a4 = CPENABLE */\r
+ addx4 a0, a5, a0 /* a0 = &_xt_coproc_mask[n] */\r
+ l32i a0, a0, 0 /* a0 = (n << 16) | (1 << n) */\r
+ movi a3, _xt_coproc_owner_sa /* (placed here for load slot) */\r
+ extui a2, a0, 0, 16 /* coprocessor bitmask portion */\r
+ or a4, a4, a2 /* a4 = CPENABLE | (1 << n) */\r
+ wsr a4, CPENABLE\r
+\r
+ /* Get old coprocessor owner thread (save area ptr) and assign new one. */\r
+ addx4 a3, a5, a3 /* a3 = &_xt_coproc_owner_sa[n] */\r
+ l32i a2, a3, 0 /* a2 = old owner's save area */\r
+ s32i a15, a3, 0 /* _xt_coproc_owner_sa[n] = new */\r
+ rsync /* ensure wsr.CPENABLE is complete */\r
+\r
+ /* Only need to context switch if new owner != old owner. */\r
+ beq a15, a2, .L_goto_done /* new owner == old, we're done */\r
+\r
+ /* If no old owner then nothing to save. */\r
+ beqz a2, .L_check_new\r
+\r
+ /* If old owner not actively using CP then nothing to save. */\r
+ l16ui a4, a2, XT_CPENABLE /* a4 = old owner's CPENABLE */\r
+ bnone a4, a0, .L_check_new /* old owner not using CP */\r
+\r
+.L_save_old:\r
+ /* Save old owner's coprocessor state. */\r
+\r
+ movi a5, _xt_coproc_sa_offset\r
+\r
+ /* Mark old owner state as no longer active (CPENABLE bit n clear). */\r
+ xor a4, a4, a0 /* clear CP bit in CPENABLE */\r
+ s16i a4, a2, XT_CPENABLE /* update old owner's CPENABLE */\r
+\r
+ extui a4, a0, 16, 5 /* a4 = CP index = n */\r
+ addx4 a5, a4, a5 /* a5 = &_xt_coproc_sa_offset[n] */\r
+\r
+ /* Mark old owner state as saved (CPSTORED bit n set). */\r
+ l16ui a4, a2, XT_CPSTORED /* a4 = old owner's CPSTORED */\r
+ l32i a5, a5, 0 /* a5 = XT_CP[n]_SA offset */\r
+ or a4, a4, a0 /* set CP in old owner's CPSTORED */\r
+ s16i a4, a2, XT_CPSTORED /* update old owner's CPSTORED */\r
+ l32i a2, a2, XT_CP_ASA /* ptr to actual (aligned) save area */\r
+ extui a3, a0, 16, 5 /* a3 = CP index = n */\r
+ add a2, a2, a5 /* a2 = old owner's area for CP n */\r
+\r
+ /*\r
+ The config-specific HAL macro invoked below destroys a2-5, preserves a0-1.\r
+ It is theoretically possible for Xtensa processor designers to write TIE \r
+ that causes more address registers to be affected, but it is generally \r
+ unlikely. If that ever happens, more registers needs to be saved/restored\r
+ around this macro invocation, and the value in a15 needs to be recomputed.\r
+ */\r
+ xchal_cpi_store_funcbody\r
+\r
+.L_check_new:\r
+ /* Check if any state has to be restored for new owner. */\r
+ /* NOTE: a15 = new owner's save area, cannot be zero when we get here. */\r
+\r
+ l16ui a3, a15, XT_CPSTORED /* a3 = new owner's CPSTORED */\r
+ movi a4, _xt_coproc_sa_offset\r
+ bnone a3, a0, .L_check_cs /* full CP not saved, check callee-saved */\r
+ xor a3, a3, a0 /* CPSTORED bit is set, clear it */\r
+ s16i a3, a15, XT_CPSTORED /* update new owner's CPSTORED */\r
+\r
+ /* Adjust new owner's save area pointers to area for CP n. */\r
+ extui a3, a0, 16, 5 /* a3 = CP index = n */\r
+ addx4 a4, a3, a4 /* a4 = &_xt_coproc_sa_offset[n] */\r
+ l32i a4, a4, 0 /* a4 = XT_CP[n]_SA */\r
+ l32i a5, a15, XT_CP_ASA /* ptr to actual (aligned) save area */\r
+ add a2, a4, a5 /* a2 = new owner's area for CP */\r
+\r
+ /*\r
+ The config-specific HAL macro invoked below destroys a2-5, preserves a0-1.\r
+ It is theoretically possible for Xtensa processor designers to write TIE \r
+ that causes more address registers to be affected, but it is generally \r
+ unlikely. If that ever happens, more registers needs to be saved/restored\r
+ around this macro invocation.\r
+ */\r
+ xchal_cpi_load_funcbody\r
+\r
+ /* Restore interruptee's saved registers. */\r
+ /* Can omit rsync for wsr.CPENABLE here because _xt_user_exit does it. */\r
+.L_xt_coproc_done:\r
+ l32i a15, sp, XT_STK_A15\r
+ l32i a5, sp, XT_STK_A5\r
+ l32i a4, sp, XT_STK_A4\r
+ l32i a3, sp, XT_STK_A3\r
+ l32i a2, sp, XT_STK_A2\r
+ call0 _xt_user_exit /* return via exit dispatcher */\r
+ /* Never returns here - call0 is used as a jump (see note at top) */\r
+\r
+.L_check_cs:\r
+ /* a0 = CP mask in low bits, a15 = new owner's save area */\r
+ l16ui a2, a15, XT_CP_CS_ST /* a2 = mask of CPs saved */\r
+ bnone a2, a0, .L_xt_coproc_done /* if no match then done */\r
+ and a2, a2, a0 /* a2 = which CPs to restore */\r
+ extui a2, a2, 0, 8 /* extract low 8 bits */\r
+ s32i a6, sp, XT_STK_A6 /* save extra needed regs */\r
+ s32i a7, sp, XT_STK_A7\r
+ s32i a13, sp, XT_STK_A13\r
+ s32i a14, sp, XT_STK_A14\r
+ call0 _xt_coproc_restorecs /* restore CP registers */\r
+ l32i a6, sp, XT_STK_A6 /* restore saved registers */\r
+ l32i a7, sp, XT_STK_A7\r
+ l32i a13, sp, XT_STK_A13\r
+ l32i a14, sp, XT_STK_A14\r
+ j .L_xt_coproc_done\r
+\r
+ /* Co-processor exception occurred outside a thread (not supported). */\r
+.L_xt_coproc_invalid:\r
+ #if XCHAL_HAVE_DEBUG\r
+ break 1, 1 /* unhandled user exception */\r
+ #endif\r
+ call0 _xt_panic /* not in a thread (invalid) */\r
+ /* never returns */\r
+\r
+\r
+#endif /* XCHAL_CP_NUM */\r
+\r
+\r
+/*\r
+-------------------------------------------------------------------------------\r
+ Level 1 interrupt dispatch. Assumes stack frame has not been allocated yet.\r
+-------------------------------------------------------------------------------\r
+*/\r
+\r
+ .text \r
+ .type _xt_lowint1,@function\r
+ .align 4\r
+\r
+_xt_lowint1:\r
+ mov a0, sp /* sp == a1 */\r
+ addi sp, sp, -XT_STK_FRMSZ /* allocate interrupt stack frame */\r
+ s32i a0, sp, XT_STK_A1 /* save pre-interrupt SP */\r
+ rsr a0, PS /* save interruptee's PS */\r
+ s32i a0, sp, XT_STK_PS\r
+ rsr a0, EPC_1 /* save interruptee's PC */\r
+ s32i a0, sp, XT_STK_PC\r
+ rsr a0, EXCSAVE_1 /* save interruptee's a0 */\r
+ s32i a0, sp, XT_STK_A0\r
+ movi a0, _xt_user_exit /* save exit point for dispatch */\r
+ s32i a0, sp, XT_STK_EXIT\r
+\r
+ /* Save rest of interrupt context and enter RTOS. */\r
+ call0 XT_RTOS_INT_ENTER /* common RTOS interrupt entry */\r
+\r
+ /* !! We are now on the RTOS system stack !! */ \r
+\r
+ /* Set up PS for C, enable interrupts above this level and clear EXCM. */\r
+ #ifdef __XTENSA_CALL0_ABI__\r
+ movi a0, PS_INTLEVEL(1) | PS_UM\r
+ #else \r
+ movi a0, PS_INTLEVEL(1) | PS_UM | PS_WOE\r
+ #endif\r
+ wsr a0, PS\r
+ rsync\r
+\r
+ /* OK to call C code at this point, dispatch user ISRs */\r
+\r
+ dispatch_c_isr 1 XCHAL_INTLEVEL1_MASK\r
+\r
+ /* Done handling interrupts, transfer control to OS */\r
+ call0 XT_RTOS_INT_EXIT /* does not return directly here */\r
+\r
+\r
+/*\r
+-------------------------------------------------------------------------------\r
+ MEDIUM PRIORITY (LEVEL 2+) INTERRUPT VECTORS AND LOW LEVEL HANDLERS.\r
+\r
+ Medium priority interrupts are by definition those with priority greater\r
+ than 1 and not greater than XCHAL_EXCM_LEVEL. These are disabled by\r
+ setting PS.EXCM and therefore can easily support a C environment for\r
+ handlers in C, and interact safely with an RTOS.\r
+\r
+ Each vector goes at a predetermined location according to the Xtensa\r
+ hardware configuration, which is ensured by its placement in a special\r
+ section known to the Xtensa linker support package (LSP). It performs\r
+ the minimum necessary before jumping to the handler in the .text section.\r
+\r
+ The corresponding handler goes in the normal .text section. It sets up\r
+ the appropriate stack frame, saves a few vector-specific registers and\r
+ calls XT_RTOS_INT_ENTER to save the rest of the interrupted context\r
+ and enter the RTOS, then sets up a C environment. It then calls the\r
+ user's interrupt handler code (which may be coded in C) and finally \r
+ calls XT_RTOS_INT_EXIT to transfer control to the RTOS for scheduling.\r
+\r
+ While XT_RTOS_INT_EXIT does not return directly to the interruptee,\r
+ eventually the RTOS scheduler will want to dispatch the interrupted\r
+ task or handler. The scheduler will return to the exit point that was\r
+ saved in the interrupt stack frame at XT_STK_EXIT.\r
+-------------------------------------------------------------------------------\r
+*/\r
+\r
+#if XCHAL_EXCM_LEVEL >= 2\r
+\r
+ .begin literal_prefix .Level2InterruptVector\r
+ .section .Level2InterruptVector.text, "ax"\r
+ .global _Level2Vector\r
+ .type _Level2Vector,@function\r
+ .align 4\r
+_Level2Vector:\r
+ wsr a0, EXCSAVE_2 /* preserve a0 */\r
+ call0 _xt_medint2 /* load interrupt handler */\r
+ /* never returns here - call0 is used as a jump (see note at top) */\r
+\r
+ .end literal_prefix\r
+\r
+ .text\r
+ .type _xt_medint2,@function\r
+ .align 4\r
+_xt_medint2:\r
+ mov a0, sp /* sp == a1 */\r
+ addi sp, sp, -XT_STK_FRMSZ /* allocate interrupt stack frame */\r
+ s32i a0, sp, XT_STK_A1 /* save pre-interrupt SP */\r
+ rsr a0, EPS_2 /* save interruptee's PS */\r
+ s32i a0, sp, XT_STK_PS\r
+ rsr a0, EPC_2 /* save interruptee's PC */\r
+ s32i a0, sp, XT_STK_PC\r
+ rsr a0, EXCSAVE_2 /* save interruptee's a0 */\r
+ s32i a0, sp, XT_STK_A0\r
+ movi a0, _xt_medint2_exit /* save exit point for dispatch */\r
+ s32i a0, sp, XT_STK_EXIT\r
+\r
+ /* Save rest of interrupt context and enter RTOS. */\r
+ call0 XT_RTOS_INT_ENTER /* common RTOS interrupt entry */\r
+\r
+ /* !! We are now on the RTOS system stack !! */\r
+\r
+ /* Set up PS for C, enable interrupts above this level and clear EXCM. */\r
+ #ifdef __XTENSA_CALL0_ABI__\r
+ movi a0, PS_INTLEVEL(2) | PS_UM\r
+ #else\r
+ movi a0, PS_INTLEVEL(2) | PS_UM | PS_WOE\r
+ #endif\r
+ wsr a0, PS\r
+ rsync\r
+\r
+ /* OK to call C code at this point, dispatch user ISRs */\r
+\r
+ dispatch_c_isr 2 XCHAL_INTLEVEL2_MASK\r
+\r
+ /* Done handling interrupts, transfer control to OS */\r
+ call0 XT_RTOS_INT_EXIT /* does not return directly here */\r
+\r
+ /*\r
+ Exit point for dispatch. Saved in interrupt stack frame at XT_STK_EXIT\r
+ on entry and used to return to a thread or interrupted interrupt handler.\r
+ */\r
+ .global _xt_medint2_exit\r
+ .type _xt_medint2_exit,@function\r
+ .align 4\r
+_xt_medint2_exit:\r
+ /* Restore only level-specific regs (the rest were already restored) */\r
+ l32i a0, sp, XT_STK_PS /* retrieve interruptee's PS */\r
+ wsr a0, EPS_2\r
+ l32i a0, sp, XT_STK_PC /* retrieve interruptee's PC */\r
+ wsr a0, EPC_2\r
+ l32i a0, sp, XT_STK_A0 /* retrieve interruptee's A0 */\r
+ l32i sp, sp, XT_STK_A1 /* remove interrupt stack frame */\r
+ rsync /* ensure EPS and EPC written */\r
+ rfi 2\r
+\r
+#endif /* Level 2 */\r
+\r
+#if XCHAL_EXCM_LEVEL >= 3\r
+\r
+ .begin literal_prefix .Level3InterruptVector\r
+ .section .Level3InterruptVector.text, "ax"\r
+ .global _Level3Vector\r
+ .type _Level3Vector,@function\r
+ .align 4\r
+_Level3Vector:\r
+ wsr a0, EXCSAVE_3 /* preserve a0 */\r
+ call0 _xt_medint3 /* load interrupt handler */\r
+ /* never returns here - call0 is used as a jump (see note at top) */\r
+\r
+ .end literal_prefix\r
+\r
+ .text\r
+ .type _xt_medint3,@function\r
+ .align 4\r
+_xt_medint3:\r
+ mov a0, sp /* sp == a1 */\r
+ addi sp, sp, -XT_STK_FRMSZ /* allocate interrupt stack frame */\r
+ s32i a0, sp, XT_STK_A1 /* save pre-interrupt SP */\r
+ rsr a0, EPS_3 /* save interruptee's PS */\r
+ s32i a0, sp, XT_STK_PS\r
+ rsr a0, EPC_3 /* save interruptee's PC */\r
+ s32i a0, sp, XT_STK_PC\r
+ rsr a0, EXCSAVE_3 /* save interruptee's a0 */\r
+ s32i a0, sp, XT_STK_A0\r
+ movi a0, _xt_medint3_exit /* save exit point for dispatch */\r
+ s32i a0, sp, XT_STK_EXIT\r
+\r
+ /* Save rest of interrupt context and enter RTOS. */\r
+ call0 XT_RTOS_INT_ENTER /* common RTOS interrupt entry */\r
+\r
+ /* !! We are now on the RTOS system stack !! */\r
+\r
+ /* Set up PS for C, enable interrupts above this level and clear EXCM. */\r
+ #ifdef __XTENSA_CALL0_ABI__\r
+ movi a0, PS_INTLEVEL(3) | PS_UM\r
+ #else\r
+ movi a0, PS_INTLEVEL(3) | PS_UM | PS_WOE\r
+ #endif\r
+ wsr a0, PS\r
+ rsync\r
+\r
+ /* OK to call C code at this point, dispatch user ISRs */\r
+\r
+ dispatch_c_isr 3 XCHAL_INTLEVEL3_MASK\r
+\r
+ /* Done handling interrupts, transfer control to OS */\r
+ call0 XT_RTOS_INT_EXIT /* does not return directly here */\r
+\r
+ /*\r
+ Exit point for dispatch. Saved in interrupt stack frame at XT_STK_EXIT\r
+ on entry and used to return to a thread or interrupted interrupt handler.\r
+ */\r
+ .global _xt_medint3_exit\r
+ .type _xt_medint3_exit,@function\r
+ .align 4\r
+_xt_medint3_exit:\r
+ /* Restore only level-specific regs (the rest were already restored) */\r
+ l32i a0, sp, XT_STK_PS /* retrieve interruptee's PS */\r
+ wsr a0, EPS_3\r
+ l32i a0, sp, XT_STK_PC /* retrieve interruptee's PC */\r
+ wsr a0, EPC_3\r
+ l32i a0, sp, XT_STK_A0 /* retrieve interruptee's A0 */\r
+ l32i sp, sp, XT_STK_A1 /* remove interrupt stack frame */\r
+ rsync /* ensure EPS and EPC written */\r
+ rfi 3\r
+\r
+#endif /* Level 3 */\r
+\r
+#if XCHAL_EXCM_LEVEL >= 4\r
+\r
+ .begin literal_prefix .Level4InterruptVector\r
+ .section .Level4InterruptVector.text, "ax"\r
+ .global _Level4Vector\r
+ .type _Level4Vector,@function\r
+ .align 4\r
+_Level4Vector:\r
+ wsr a0, EXCSAVE_4 /* preserve a0 */\r
+ call0 _xt_medint4 /* load interrupt handler */\r
+\r
+ .end literal_prefix\r
+\r
+ .text\r
+ .type _xt_medint4,@function\r
+ .align 4\r
+_xt_medint4:\r
+ mov a0, sp /* sp == a1 */\r
+ addi sp, sp, -XT_STK_FRMSZ /* allocate interrupt stack frame */\r
+ s32i a0, sp, XT_STK_A1 /* save pre-interrupt SP */\r
+ rsr a0, EPS_4 /* save interruptee's PS */\r
+ s32i a0, sp, XT_STK_PS\r
+ rsr a0, EPC_4 /* save interruptee's PC */\r
+ s32i a0, sp, XT_STK_PC\r
+ rsr a0, EXCSAVE_4 /* save interruptee's a0 */\r
+ s32i a0, sp, XT_STK_A0\r
+ movi a0, _xt_medint4_exit /* save exit point for dispatch */\r
+ s32i a0, sp, XT_STK_EXIT\r
+\r
+ /* Save rest of interrupt context and enter RTOS. */\r
+ call0 XT_RTOS_INT_ENTER /* common RTOS interrupt entry */\r
+\r
+ /* !! We are now on the RTOS system stack !! */\r
+\r
+ /* Set up PS for C, enable interrupts above this level and clear EXCM. */\r
+ #ifdef __XTENSA_CALL0_ABI__\r
+ movi a0, PS_INTLEVEL(4) | PS_UM\r
+ #else\r
+ movi a0, PS_INTLEVEL(4) | PS_UM | PS_WOE\r
+ #endif\r
+ wsr a0, PS\r
+ rsync\r
+\r
+ /* OK to call C code at this point, dispatch user ISRs */\r
+\r
+ dispatch_c_isr 4 XCHAL_INTLEVEL4_MASK\r
+\r
+ /* Done handling interrupts, transfer control to OS */\r
+ call0 XT_RTOS_INT_EXIT /* does not return directly here */\r
+\r
+ /*\r
+ Exit point for dispatch. Saved in interrupt stack frame at XT_STK_EXIT\r
+ on entry and used to return to a thread or interrupted interrupt handler.\r
+ */\r
+ .global _xt_medint4_exit\r
+ .type _xt_medint4_exit,@function\r
+ .align 4\r
+_xt_medint4_exit:\r
+ /* Restore only level-specific regs (the rest were already restored) */\r
+ l32i a0, sp, XT_STK_PS /* retrieve interruptee's PS */\r
+ wsr a0, EPS_4\r
+ l32i a0, sp, XT_STK_PC /* retrieve interruptee's PC */\r
+ wsr a0, EPC_4\r
+ l32i a0, sp, XT_STK_A0 /* retrieve interruptee's A0 */\r
+ l32i sp, sp, XT_STK_A1 /* remove interrupt stack frame */\r
+ rsync /* ensure EPS and EPC written */\r
+ rfi 4\r
+\r
+#endif /* Level 4 */\r
+\r
+#if XCHAL_EXCM_LEVEL >= 5\r
+\r
+ .begin literal_prefix .Level5InterruptVector\r
+ .section .Level5InterruptVector.text, "ax"\r
+ .global _Level5Vector\r
+ .type _Level5Vector,@function\r
+ .align 4\r
+_Level5Vector:\r
+ wsr a0, EXCSAVE_5 /* preserve a0 */\r
+ call0 _xt_medint5 /* load interrupt handler */\r
+\r
+ .end literal_prefix\r
+\r
+ .text\r
+ .type _xt_medint5,@function\r
+ .align 4\r
+_xt_medint5:\r
+ mov a0, sp /* sp == a1 */\r
+ addi sp, sp, -XT_STK_FRMSZ /* allocate interrupt stack frame */\r
+ s32i a0, sp, XT_STK_A1 /* save pre-interrupt SP */\r
+ rsr a0, EPS_5 /* save interruptee's PS */\r
+ s32i a0, sp, XT_STK_PS\r
+ rsr a0, EPC_5 /* save interruptee's PC */\r
+ s32i a0, sp, XT_STK_PC\r
+ rsr a0, EXCSAVE_5 /* save interruptee's a0 */\r
+ s32i a0, sp, XT_STK_A0\r
+ movi a0, _xt_medint5_exit /* save exit point for dispatch */\r
+ s32i a0, sp, XT_STK_EXIT\r
+\r
+ /* Save rest of interrupt context and enter RTOS. */\r
+ call0 XT_RTOS_INT_ENTER /* common RTOS interrupt entry */\r
+\r
+ /* !! We are now on the RTOS system stack !! */\r
+\r
+ /* Set up PS for C, enable interrupts above this level and clear EXCM. */\r
+ #ifdef __XTENSA_CALL0_ABI__\r
+ movi a0, PS_INTLEVEL(5) | PS_UM\r
+ #else\r
+ movi a0, PS_INTLEVEL(5) | PS_UM | PS_WOE\r
+ #endif\r
+ wsr a0, PS\r
+ rsync\r
+\r
+ /* OK to call C code at this point, dispatch user ISRs */\r
+\r
+ dispatch_c_isr 5 XCHAL_INTLEVEL5_MASK\r
+\r
+ /* Done handling interrupts, transfer control to OS */\r
+ call0 XT_RTOS_INT_EXIT /* does not return directly here */\r
+\r
+ /*\r
+ Exit point for dispatch. Saved in interrupt stack frame at XT_STK_EXIT\r
+ on entry and used to return to a thread or interrupted interrupt handler.\r
+ */\r
+ .global _xt_medint5_exit\r
+ .type _xt_medint5_exit,@function\r
+ .align 4\r
+_xt_medint5_exit:\r
+ /* Restore only level-specific regs (the rest were already restored) */\r
+ l32i a0, sp, XT_STK_PS /* retrieve interruptee's PS */\r
+ wsr a0, EPS_5\r
+ l32i a0, sp, XT_STK_PC /* retrieve interruptee's PC */\r
+ wsr a0, EPC_5\r
+ l32i a0, sp, XT_STK_A0 /* retrieve interruptee's A0 */\r
+ l32i sp, sp, XT_STK_A1 /* remove interrupt stack frame */\r
+ rsync /* ensure EPS and EPC written */\r
+ rfi 5\r
+\r
+#endif /* Level 5 */\r
+\r
+#if XCHAL_EXCM_LEVEL >= 6\r
+\r
+ .begin literal_prefix .Level6InterruptVector\r
+ .section .Level6InterruptVector.text, "ax"\r
+ .global _Level6Vector\r
+ .type _Level6Vector,@function\r
+ .align 4\r
+_Level6Vector:\r
+ wsr a0, EXCSAVE_6 /* preserve a0 */\r
+ call0 _xt_medint6 /* load interrupt handler */\r
+\r
+ .end literal_prefix\r
+\r
+ .text\r
+ .type _xt_medint6,@function\r
+ .align 4\r
+_xt_medint6:\r
+ mov a0, sp /* sp == a1 */\r
+ addi sp, sp, -XT_STK_FRMSZ /* allocate interrupt stack frame */\r
+ s32i a0, sp, XT_STK_A1 /* save pre-interrupt SP */\r
+ rsr a0, EPS_6 /* save interruptee's PS */\r
+ s32i a0, sp, XT_STK_PS\r
+ rsr a0, EPC_6 /* save interruptee's PC */\r
+ s32i a0, sp, XT_STK_PC\r
+ rsr a0, EXCSAVE_6 /* save interruptee's a0 */\r
+ s32i a0, sp, XT_STK_A0\r
+ movi a0, _xt_medint6_exit /* save exit point for dispatch */\r
+ s32i a0, sp, XT_STK_EXIT\r
+\r
+ /* Save rest of interrupt context and enter RTOS. */\r
+ call0 XT_RTOS_INT_ENTER /* common RTOS interrupt entry */\r
+\r
+ /* !! We are now on the RTOS system stack !! */\r
+\r
+ /* Set up PS for C, enable interrupts above this level and clear EXCM. */\r
+ #ifdef __XTENSA_CALL0_ABI__\r
+ movi a0, PS_INTLEVEL(6) | PS_UM\r
+ #else\r
+ movi a0, PS_INTLEVEL(6) | PS_UM | PS_WOE\r
+ #endif\r
+ wsr a0, PS\r
+ rsync\r
+\r
+ /* OK to call C code at this point, dispatch user ISRs */\r
+\r
+ dispatch_c_isr 6 XCHAL_INTLEVEL6_MASK\r
+\r
+ /* Done handling interrupts, transfer control to OS */\r
+ call0 XT_RTOS_INT_EXIT /* does not return directly here */\r
+\r
+ /*\r
+ Exit point for dispatch. Saved in interrupt stack frame at XT_STK_EXIT\r
+ on entry and used to return to a thread or interrupted interrupt handler.\r
+ */\r
+ .global _xt_medint6_exit\r
+ .type _xt_medint6_exit,@function\r
+ .align 4\r
+_xt_medint6_exit:\r
+ /* Restore only level-specific regs (the rest were already restored) */\r
+ l32i a0, sp, XT_STK_PS /* retrieve interruptee's PS */\r
+ wsr a0, EPS_6\r
+ l32i a0, sp, XT_STK_PC /* retrieve interruptee's PC */\r
+ wsr a0, EPC_6\r
+ l32i a0, sp, XT_STK_A0 /* retrieve interruptee's A0 */\r
+ l32i sp, sp, XT_STK_A1 /* remove interrupt stack frame */\r
+ rsync /* ensure EPS and EPC written */\r
+ rfi 6\r
+\r
+#endif /* Level 6 */\r
+\r
+\r
+/*******************************************************************************\r
+\r
+HIGH PRIORITY (LEVEL > XCHAL_EXCM_LEVEL) INTERRUPT VECTORS AND HANDLERS\r
+\r
+High priority interrupts are by definition those with priorities greater\r
+than XCHAL_EXCM_LEVEL. This includes non-maskable (NMI). High priority\r
+interrupts cannot interact with the RTOS, that is they must save all regs\r
+they use and not call any RTOS function.\r
+\r
+A further restriction imposed by the Xtensa windowed architecture is that\r
+high priority interrupts must not modify the stack area even logically\r
+"above" the top of the interrupted stack (they need to provide their\r
+own stack or static save area).\r
+\r
+Cadence Design Systems recommends high priority interrupt handlers be coded in assembly\r
+and used for purposes requiring very short service times.\r
+\r
+Here are templates for high priority (level 2+) interrupt vectors.\r
+They assume only one interrupt per level to avoid the burden of identifying\r
+which interrupts at this level are pending and enabled. This allows for \r
+minimum latency and avoids having to save/restore a2 in addition to a0.\r
+If more than one interrupt per high priority level is configured, this burden\r
+is on the handler which in any case must provide a way to save and restore\r
+registers it uses without touching the interrupted stack.\r
+\r
+Each vector goes at a predetermined location according to the Xtensa\r
+hardware configuration, which is ensured by its placement in a special\r
+section known to the Xtensa linker support package (LSP). It performs\r
+the minimum necessary before jumping to the handler in the .text section.\r
+\r
+*******************************************************************************/\r
+\r
+/*\r
+Currently only shells for high priority interrupt handlers are provided\r
+here. However a template and example can be found in the Cadence Design Systems tools\r
+documentation: "Microprocessor Programmer's Guide".\r
+*/\r
+\r
+#if XCHAL_NUM_INTLEVELS >=2 && XCHAL_EXCM_LEVEL <2 && XCHAL_DEBUGLEVEL !=2\r
+\r
+ .begin literal_prefix .Level2InterruptVector\r
+ .section .Level2InterruptVector.text, "ax"\r
+ .global _Level2Vector\r
+ .type _Level2Vector,@function\r
+ .align 4\r
+_Level2Vector:\r
+ wsr a0, EXCSAVE_2 /* preserve a0 */\r
+ call0 _xt_highint2 /* load interrupt handler */\r
+\r
+ .end literal_prefix\r
+\r
+ .text\r
+ .type _xt_highint2,@function\r
+ .align 4\r
+_xt_highint2:\r
+\r
+ #ifdef XT_INTEXC_HOOKS\r
+ /* Call interrupt hook if present to (pre)handle interrupts. */\r
+ movi a0, _xt_intexc_hooks\r
+ l32i a0, a0, 2<<2\r
+ beqz a0, 1f\r
+.Ln_xt_highint2_call_hook:\r
+ callx0 a0 /* must NOT disturb stack! */\r
+1:\r
+ #endif\r
+\r
+ /* USER_EDIT:\r
+ ADD HIGH PRIORITY LEVEL 2 INTERRUPT HANDLER CODE HERE.\r
+ */\r
+\r
+ .align 4\r
+.L_xt_highint2_exit:\r
+ rsr a0, EXCSAVE_2 /* restore a0 */\r
+ rfi 2\r
+\r
+#endif /* Level 2 */\r
+\r
+#if XCHAL_NUM_INTLEVELS >=3 && XCHAL_EXCM_LEVEL <3 && XCHAL_DEBUGLEVEL !=3\r
+\r
+ .begin literal_prefix .Level3InterruptVector\r
+ .section .Level3InterruptVector.text, "ax"\r
+ .global _Level3Vector\r
+ .type _Level3Vector,@function\r
+ .align 4\r
+_Level3Vector:\r
+ wsr a0, EXCSAVE_3 /* preserve a0 */\r
+ call0 _xt_highint3 /* load interrupt handler */\r
+ /* never returns here - call0 is used as a jump (see note at top) */\r
+\r
+ .end literal_prefix\r
+\r
+ .text\r
+ .type _xt_highint3,@function\r
+ .align 4\r
+_xt_highint3:\r
+\r
+ #ifdef XT_INTEXC_HOOKS\r
+ /* Call interrupt hook if present to (pre)handle interrupts. */\r
+ movi a0, _xt_intexc_hooks\r
+ l32i a0, a0, 3<<2\r
+ beqz a0, 1f\r
+.Ln_xt_highint3_call_hook:\r
+ callx0 a0 /* must NOT disturb stack! */\r
+1:\r
+ #endif\r
+\r
+ /* USER_EDIT:\r
+ ADD HIGH PRIORITY LEVEL 3 INTERRUPT HANDLER CODE HERE.\r
+ */\r
+\r
+ .align 4\r
+.L_xt_highint3_exit:\r
+ rsr a0, EXCSAVE_3 /* restore a0 */\r
+ rfi 3\r
+\r
+#endif /* Level 3 */\r
+\r
+#if XCHAL_NUM_INTLEVELS >=4 && XCHAL_EXCM_LEVEL <4 && XCHAL_DEBUGLEVEL !=4\r
+\r
+ .begin literal_prefix .Level4InterruptVector\r
+ .section .Level4InterruptVector.text, "ax"\r
+ .global _Level4Vector\r
+ .type _Level4Vector,@function\r
+ .align 4\r
+_Level4Vector:\r
+ wsr a0, EXCSAVE_4 /* preserve a0 */\r
+ call0 _xt_highint4 /* load interrupt handler */\r
+ /* never returns here - call0 is used as a jump (see note at top) */\r
+\r
+ .end literal_prefix\r
+\r
+ .text\r
+ .type _xt_highint4,@function\r
+ .align 4\r
+_xt_highint4:\r
+\r
+ #ifdef XT_INTEXC_HOOKS\r
+ /* Call interrupt hook if present to (pre)handle interrupts. */\r
+ movi a0, _xt_intexc_hooks\r
+ l32i a0, a0, 4<<2\r
+ beqz a0, 1f\r
+.Ln_xt_highint4_call_hook:\r
+ callx0 a0 /* must NOT disturb stack! */\r
+1:\r
+ #endif\r
+\r
+ /* USER_EDIT:\r
+ ADD HIGH PRIORITY LEVEL 4 INTERRUPT HANDLER CODE HERE.\r
+ */\r
+\r
+ .align 4\r
+.L_xt_highint4_exit:\r
+ rsr a0, EXCSAVE_4 /* restore a0 */\r
+ rfi 4\r
+\r
+#endif /* Level 4 */\r
+\r
+#if XCHAL_NUM_INTLEVELS >=5 && XCHAL_EXCM_LEVEL <5 && XCHAL_DEBUGLEVEL !=5\r
+\r
+ .begin literal_prefix .Level5InterruptVector\r
+ .section .Level5InterruptVector.text, "ax"\r
+ .global _Level5Vector\r
+ .type _Level5Vector,@function\r
+ .align 4\r
+_Level5Vector:\r
+ wsr a0, EXCSAVE_5 /* preserve a0 */\r
+ call0 _xt_highint5 /* load interrupt handler */\r
+ /* never returns here - call0 is used as a jump (see note at top) */\r
+\r
+ .end literal_prefix\r
+\r
+ .text\r
+ .type _xt_highint5,@function\r
+ .align 4\r
+_xt_highint5:\r
+\r
+ #ifdef XT_INTEXC_HOOKS\r
+ /* Call interrupt hook if present to (pre)handle interrupts. */\r
+ movi a0, _xt_intexc_hooks\r
+ l32i a0, a0, 5<<2\r
+ beqz a0, 1f\r
+.Ln_xt_highint5_call_hook:\r
+ callx0 a0 /* must NOT disturb stack! */\r
+1:\r
+ #endif\r
+\r
+ /* USER_EDIT:\r
+ ADD HIGH PRIORITY LEVEL 5 INTERRUPT HANDLER CODE HERE.\r
+ */\r
+\r
+ .align 4\r
+.L_xt_highint5_exit:\r
+ rsr a0, EXCSAVE_5 /* restore a0 */\r
+ rfi 5\r
+\r
+#endif /* Level 5 */\r
+\r
+#if XCHAL_NUM_INTLEVELS >=6 && XCHAL_EXCM_LEVEL <6 && XCHAL_DEBUGLEVEL !=6\r
+\r
+ .begin literal_prefix .Level6InterruptVector\r
+ .section .Level6InterruptVector.text, "ax"\r
+ .global _Level6Vector\r
+ .type _Level6Vector,@function\r
+ .align 4\r
+_Level6Vector:\r
+ wsr a0, EXCSAVE_6 /* preserve a0 */\r
+ call0 _xt_highint6 /* load interrupt handler */\r
+ /* never returns here - call0 is used as a jump (see note at top) */\r
+\r
+ .end literal_prefix\r
+\r
+ .text\r
+ .type _xt_highint6,@function\r
+ .align 4\r
+_xt_highint6:\r
+\r
+ #ifdef XT_INTEXC_HOOKS\r
+ /* Call interrupt hook if present to (pre)handle interrupts. */\r
+ movi a0, _xt_intexc_hooks\r
+ l32i a0, a0, 6<<2\r
+ beqz a0, 1f\r
+.Ln_xt_highint6_call_hook:\r
+ callx0 a0 /* must NOT disturb stack! */\r
+1:\r
+ #endif\r
+\r
+ /* USER_EDIT:\r
+ ADD HIGH PRIORITY LEVEL 6 INTERRUPT HANDLER CODE HERE.\r
+ */\r
+\r
+ .align 4\r
+.L_xt_highint6_exit:\r
+ rsr a0, EXCSAVE_6 /* restore a0 */\r
+ rfi 6\r
+\r
+#endif /* Level 6 */\r
+\r
+#if XCHAL_HAVE_NMI\r
+\r
+ .begin literal_prefix .NMIExceptionVector\r
+ .section .NMIExceptionVector.text, "ax"\r
+ .global _NMIExceptionVector\r
+ .type _NMIExceptionVector,@function\r
+ .align 4\r
+_NMIExceptionVector:\r
+ wsr a0, EXCSAVE + XCHAL_NMILEVEL _ /* preserve a0 */\r
+ call0 _xt_nmi /* load interrupt handler */\r
+ /* never returns here - call0 is used as a jump (see note at top) */\r
+\r
+ .end literal_prefix\r
+\r
+ .text\r
+ .type _xt_nmi,@function\r
+ .align 4\r
+_xt_nmi:\r
+\r
+ #ifdef XT_INTEXC_HOOKS\r
+ /* Call interrupt hook if present to (pre)handle interrupts. */\r
+ movi a0, _xt_intexc_hooks\r
+ l32i a0, a0, XCHAL_NMILEVEL<<2\r
+ beqz a0, 1f\r
+.Ln_xt_nmi_call_hook:\r
+ callx0 a0 /* must NOT disturb stack! */\r
+1:\r
+ #endif\r
+\r
+ /* USER_EDIT:\r
+ ADD HIGH PRIORITY NON-MASKABLE INTERRUPT (NMI) HANDLER CODE HERE.\r
+ */\r
+\r
+ .align 4\r
+.L_xt_nmi_exit:\r
+ rsr a0, EXCSAVE + XCHAL_NMILEVEL /* restore a0 */\r
+ rfi XCHAL_NMILEVEL\r
+\r
+#endif /* NMI */\r
+\r
+\r
+/*******************************************************************************\r
+\r
+WINDOW OVERFLOW AND UNDERFLOW EXCEPTION VECTORS AND ALLOCA EXCEPTION HANDLER\r
+\r
+Here is the code for each window overflow/underflow exception vector and \r
+(interspersed) efficient code for handling the alloca exception cause.\r
+Window exceptions are handled entirely in the vector area and are very\r
+tight for performance. The alloca exception is also handled entirely in \r
+the window vector area so comes at essentially no cost in code size.\r
+Users should never need to modify them and Cadence Design Systems recommends \r
+they do not.\r
+\r
+Window handlers go at predetermined vector locations according to the\r
+Xtensa hardware configuration, which is ensured by their placement in a\r
+special section known to the Xtensa linker support package (LSP). Since\r
+their offsets in that section are always the same, the LSPs do not define\r
+a section per vector.\r
+\r
+These things are coded for XEA2 only (XEA1 is not supported).\r
+\r
+Note on Underflow Handlers:\r
+The underflow handler for returning from call[i+1] to call[i]\r
+must preserve all the registers from call[i+1]'s window.\r
+In particular, a0 and a1 must be preserved because the RETW instruction\r
+will be reexecuted (and may even underflow if an intervening exception\r
+has flushed call[i]'s registers).\r
+Registers a2 and up may contain return values.\r
+\r
+*******************************************************************************/\r
+\r
+#if XCHAL_HAVE_WINDOWED\r
+\r
+ .section .WindowVectors.text, "ax"\r
+\r
+/*\r
+--------------------------------------------------------------------------------\r
+Window Overflow Exception for Call4.\r
+\r
+Invoked if a call[i] referenced a register (a4-a15)\r
+that contains data from ancestor call[j];\r
+call[j] had done a call4 to call[j+1].\r
+On entry here:\r
+ window rotated to call[j] start point;\r
+ a0-a3 are registers to be saved;\r
+ a4-a15 must be preserved;\r
+ a5 is call[j+1]'s stack pointer.\r
+--------------------------------------------------------------------------------\r
+*/\r
+\r
+ .org 0x0\r
+ .global _WindowOverflow4\r
+_WindowOverflow4:\r
+\r
+ s32e a0, a5, -16 /* save a0 to call[j+1]'s stack frame */\r
+ s32e a1, a5, -12 /* save a1 to call[j+1]'s stack frame */\r
+ s32e a2, a5, -8 /* save a2 to call[j+1]'s stack frame */\r
+ s32e a3, a5, -4 /* save a3 to call[j+1]'s stack frame */\r
+ rfwo /* rotates back to call[i] position */\r
+\r
+/*\r
+--------------------------------------------------------------------------------\r
+Window Underflow Exception for Call4\r
+\r
+Invoked by RETW returning from call[i+1] to call[i]\r
+where call[i]'s registers must be reloaded (not live in ARs);\r
+where call[i] had done a call4 to call[i+1].\r
+On entry here:\r
+ window rotated to call[i] start point;\r
+ a0-a3 are undefined, must be reloaded with call[i].reg[0..3];\r
+ a4-a15 must be preserved (they are call[i+1].reg[0..11]);\r
+ a5 is call[i+1]'s stack pointer.\r
+--------------------------------------------------------------------------------\r
+*/\r
+\r
+ .org 0x40\r
+ .global _WindowUnderflow4\r
+_WindowUnderflow4:\r
+\r
+ l32e a0, a5, -16 /* restore a0 from call[i+1]'s stack frame */\r
+ l32e a1, a5, -12 /* restore a1 from call[i+1]'s stack frame */\r
+ l32e a2, a5, -8 /* restore a2 from call[i+1]'s stack frame */\r
+ l32e a3, a5, -4 /* restore a3 from call[i+1]'s stack frame */\r
+ rfwu\r
+\r
+/*\r
+--------------------------------------------------------------------------------\r
+Handle alloca exception generated by interruptee executing 'movsp'.\r
+This uses space between the window vectors, so is essentially "free".\r
+All interruptee's regs are intact except a0 which is saved in EXCSAVE_1,\r
+and PS.EXCM has been set by the exception hardware (can't be interrupted).\r
+The fact the alloca exception was taken means the registers associated with\r
+the base-save area have been spilled and will be restored by the underflow\r
+handler, so those 4 registers are available for scratch.\r
+The code is optimized to avoid unaligned branches and minimize cache misses.\r
+--------------------------------------------------------------------------------\r
+*/\r
+\r
+ .align 4\r
+ .global _xt_alloca_exc\r
+_xt_alloca_exc:\r
+\r
+ rsr a0, WINDOWBASE /* grab WINDOWBASE before rotw changes it */\r
+ rotw -1 /* WINDOWBASE goes to a4, new a0-a3 are scratch */\r
+ rsr a2, PS\r
+ extui a3, a2, XCHAL_PS_OWB_SHIFT, XCHAL_PS_OWB_BITS\r
+ xor a3, a3, a4 /* bits changed from old to current windowbase */\r
+ rsr a4, EXCSAVE_1 /* restore original a0 (now in a4) */\r
+ slli a3, a3, XCHAL_PS_OWB_SHIFT\r
+ xor a2, a2, a3 /* flip changed bits in old window base */\r
+ wsr a2, PS /* update PS.OWB to new window base */\r
+ rsync\r
+\r
+ _bbci.l a4, 31, _WindowUnderflow4\r
+ rotw -1 /* original a0 goes to a8 */\r
+ _bbci.l a8, 30, _WindowUnderflow8\r
+ rotw -1\r
+ j _WindowUnderflow12\r
+\r
+/*\r
+--------------------------------------------------------------------------------\r
+Window Overflow Exception for Call8\r
+\r
+Invoked if a call[i] referenced a register (a4-a15)\r
+that contains data from ancestor call[j];\r
+call[j] had done a call8 to call[j+1].\r
+On entry here:\r
+ window rotated to call[j] start point;\r
+ a0-a7 are registers to be saved;\r
+ a8-a15 must be preserved;\r
+ a9 is call[j+1]'s stack pointer.\r
+--------------------------------------------------------------------------------\r
+*/\r
+\r
+ .org 0x80\r
+ .global _WindowOverflow8\r
+_WindowOverflow8:\r
+\r
+ s32e a0, a9, -16 /* save a0 to call[j+1]'s stack frame */\r
+ l32e a0, a1, -12 /* a0 <- call[j-1]'s sp\r
+ (used to find end of call[j]'s frame) */\r
+ s32e a1, a9, -12 /* save a1 to call[j+1]'s stack frame */\r
+ s32e a2, a9, -8 /* save a2 to call[j+1]'s stack frame */\r
+ s32e a3, a9, -4 /* save a3 to call[j+1]'s stack frame */\r
+ s32e a4, a0, -32 /* save a4 to call[j]'s stack frame */\r
+ s32e a5, a0, -28 /* save a5 to call[j]'s stack frame */\r
+ s32e a6, a0, -24 /* save a6 to call[j]'s stack frame */\r
+ s32e a7, a0, -20 /* save a7 to call[j]'s stack frame */\r
+ rfwo /* rotates back to call[i] position */\r
+\r
+/*\r
+--------------------------------------------------------------------------------\r
+Window Underflow Exception for Call8\r
+\r
+Invoked by RETW returning from call[i+1] to call[i]\r
+where call[i]'s registers must be reloaded (not live in ARs);\r
+where call[i] had done a call8 to call[i+1].\r
+On entry here:\r
+ window rotated to call[i] start point;\r
+ a0-a7 are undefined, must be reloaded with call[i].reg[0..7];\r
+ a8-a15 must be preserved (they are call[i+1].reg[0..7]);\r
+ a9 is call[i+1]'s stack pointer.\r
+--------------------------------------------------------------------------------\r
+*/\r
+\r
+ .org 0xC0\r
+ .global _WindowUnderflow8\r
+_WindowUnderflow8:\r
+\r
+ l32e a0, a9, -16 /* restore a0 from call[i+1]'s stack frame */\r
+ l32e a1, a9, -12 /* restore a1 from call[i+1]'s stack frame */\r
+ l32e a2, a9, -8 /* restore a2 from call[i+1]'s stack frame */\r
+ l32e a7, a1, -12 /* a7 <- call[i-1]'s sp\r
+ (used to find end of call[i]'s frame) */\r
+ l32e a3, a9, -4 /* restore a3 from call[i+1]'s stack frame */\r
+ l32e a4, a7, -32 /* restore a4 from call[i]'s stack frame */\r
+ l32e a5, a7, -28 /* restore a5 from call[i]'s stack frame */\r
+ l32e a6, a7, -24 /* restore a6 from call[i]'s stack frame */\r
+ l32e a7, a7, -20 /* restore a7 from call[i]'s stack frame */\r
+ rfwu\r
+\r
+/*\r
+--------------------------------------------------------------------------------\r
+Window Overflow Exception for Call12\r
+\r
+Invoked if a call[i] referenced a register (a4-a15)\r
+that contains data from ancestor call[j];\r
+call[j] had done a call12 to call[j+1].\r
+On entry here:\r
+ window rotated to call[j] start point;\r
+ a0-a11 are registers to be saved;\r
+ a12-a15 must be preserved;\r
+ a13 is call[j+1]'s stack pointer.\r
+--------------------------------------------------------------------------------\r
+*/\r
+\r
+ .org 0x100\r
+ .global _WindowOverflow12\r
+_WindowOverflow12:\r
+\r
+ s32e a0, a13, -16 /* save a0 to call[j+1]'s stack frame */\r
+ l32e a0, a1, -12 /* a0 <- call[j-1]'s sp\r
+ (used to find end of call[j]'s frame) */\r
+ s32e a1, a13, -12 /* save a1 to call[j+1]'s stack frame */\r
+ s32e a2, a13, -8 /* save a2 to call[j+1]'s stack frame */\r
+ s32e a3, a13, -4 /* save a3 to call[j+1]'s stack frame */\r
+ s32e a4, a0, -48 /* save a4 to end of call[j]'s stack frame */\r
+ s32e a5, a0, -44 /* save a5 to end of call[j]'s stack frame */\r
+ s32e a6, a0, -40 /* save a6 to end of call[j]'s stack frame */\r
+ s32e a7, a0, -36 /* save a7 to end of call[j]'s stack frame */\r
+ s32e a8, a0, -32 /* save a8 to end of call[j]'s stack frame */\r
+ s32e a9, a0, -28 /* save a9 to end of call[j]'s stack frame */\r
+ s32e a10, a0, -24 /* save a10 to end of call[j]'s stack frame */\r
+ s32e a11, a0, -20 /* save a11 to end of call[j]'s stack frame */\r
+ rfwo /* rotates back to call[i] position */\r
+\r
+/*\r
+--------------------------------------------------------------------------------\r
+Window Underflow Exception for Call12\r
+\r
+Invoked by RETW returning from call[i+1] to call[i]\r
+where call[i]'s registers must be reloaded (not live in ARs);\r
+where call[i] had done a call12 to call[i+1].\r
+On entry here:\r
+ window rotated to call[i] start point;\r
+ a0-a11 are undefined, must be reloaded with call[i].reg[0..11];\r
+ a12-a15 must be preserved (they are call[i+1].reg[0..3]);\r
+ a13 is call[i+1]'s stack pointer.\r
+--------------------------------------------------------------------------------\r
+*/\r
+\r
+ .org 0x140\r
+ .global _WindowUnderflow12\r
+_WindowUnderflow12:\r
+\r
+ l32e a0, a13, -16 /* restore a0 from call[i+1]'s stack frame */\r
+ l32e a1, a13, -12 /* restore a1 from call[i+1]'s stack frame */\r
+ l32e a2, a13, -8 /* restore a2 from call[i+1]'s stack frame */\r
+ l32e a11, a1, -12 /* a11 <- call[i-1]'s sp\r
+ (used to find end of call[i]'s frame) */\r
+ l32e a3, a13, -4 /* restore a3 from call[i+1]'s stack frame */\r
+ l32e a4, a11, -48 /* restore a4 from end of call[i]'s stack frame */\r
+ l32e a5, a11, -44 /* restore a5 from end of call[i]'s stack frame */\r
+ l32e a6, a11, -40 /* restore a6 from end of call[i]'s stack frame */\r
+ l32e a7, a11, -36 /* restore a7 from end of call[i]'s stack frame */\r
+ l32e a8, a11, -32 /* restore a8 from end of call[i]'s stack frame */\r
+ l32e a9, a11, -28 /* restore a9 from end of call[i]'s stack frame */\r
+ l32e a10, a11, -24 /* restore a10 from end of call[i]'s stack frame */\r
+ l32e a11, a11, -20 /* restore a11 from end of call[i]'s stack frame */\r
+ rfwu\r
+\r
+#endif /* XCHAL_HAVE_WINDOWED */\r
+\r