/**************************************************************************/ /* Copyright (c) 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. */ /**************************************************************************/ /**************************************************************************/ /* */ /* DESCRIPTION */ /* */ /* Xtensa exception and interrupt dispatch for XEA3. */ /* */ /* Interrupt handlers and user exception handlers support interaction */ /* with the RTOS by calling XT_RTOS_INT_ENTER and XT_RTOS_INT_EXIT */ /* before and after calling the user's specific interrupt handlers. */ /* */ /* Users can install application-specific interrupt handlers by calling */ /* xt_set_interrupt_handler(). These handlers can be written in C and */ /* must follow the C calling convention. The handler table is indexed by */ /* the interrupt number. Each handler may be provided with an argument. */ /* */ /* Users can install application-specific exception handlers in the */ /* same way, by calling xt_set_exception_handler(). One handler slot is */ /* provided for each exception type. Note that some exceptions are */ /* handled by the porting layer itself, and cannot be taken over by */ /* application code. These are the alloca, syscall, and coprocessor */ /* exceptions. */ /* */ /* Exception handlers can be written in C, and must follow C calling */ /* convention. Each handler is passed a pointer to an exception frame as */ /* its single argument. The exception frame is created on the stack and */ /* holds the saved context of the thread that took the exception. If the */ /* handler returns, the context will be restored and the instruction */ /* that caused the exception will be retried. If the handler makes any */ /* changes to the saved state in the exception frame, the changes will */ /* be applied when restoring the context. */ /* */ /* RELEASE HISTORY */ /* */ /* DATE NAME DESCRIPTION */ /* */ /* 12-31-2020 Cadence Design Systems Initial Version 6.1.3 */ /* */ /**************************************************************************/ #include #include #if XCHAL_HAVE_XEA3 #include "xtensa_context.h" #if (XCHAL_HW_VERSION < XTENSA_HWVERSION_RH_2016_2) #error Xtensa HW earlier than RH_2016.2 not supported. #endif //----------------------------------------------------------------------------- // The entry point vectors are common for call0 and windowed configurations. //----------------------------------------------------------------------------- .extern _DoubleExceptionHandler .extern _xtos_exc_dispatch .section .DispatchVector.text, "ax" #if XCHAL_HAVE_VECBASE .align 64 // 64-byte alignment needed when vecbase #else // is relocatable .align 4 #endif .org 0 // Fixed offset for Reset Vector .global _DispatchVector .weak _DispatchVector _DispatchVector: j _JumpToResetHandler .org 3 // Reserved .local _Reserved1 _Reserved1: ill .org 6 // Fixed offset for Double Exception Vector .global _DoubleExceptionVector .weak _DoubleExceptionVector _DoubleExceptionVector: j _DoubleExceptionHandler .org 9 // Reserved .local _Reserved2 _Reserved2: ill //----------------------------------------------------------------------------- // Start of dispatch code. //----------------------------------------------------------------------------- .org 12 // Fixed offset for Tailchain entry point .global _xt_dispatch _xt_dispatch: #ifdef __XTENSA_CALL0_ABI__ // NOTE: for call0, a15 is expected to be holding the previous stack pointer // when we get to the Tailchain segment. s32si.x4 a2, a15 // Select interrupt, a2 <- (intnum << 2) movi a0, 0 l32dis.it a0, a0 // a0 <- wrapper addr (handler_table[0]) s32stk a9, a15, 96 // Set new stack pointer #if XT_STK_XTRA_SZ addi a1, a1, -XT_STK_XTRA_SZ // Adjust for extra save area #endif s32dis.h a0, a0 // Jump to handler if interrupt else fall through // Note this also clears local exclusive monitor #else // windowed s32si.x4 a10, a1 // Select interrupt, a10 <- (intnum << 2) movi a8, 0 l32dis.it a8, a8 // a8 <- wrapper addr (handler_table[0]) s32stk a9, a1, 96 // Set new stack pointer #if XT_STK_XTRA_SZ addi a9, a9, -XT_STK_XTRA_SZ // Adjust for extra save area #endif s32dis.h a8, a8 // Jump to handler if interrupt else fall through // Note this also clears local exclusive monitor #endif // __XTENSA_CALL0_ABI__ .Lexit: j _xt_exit #ifndef __XTENSA_CALL0_ABI__ .org 36 // Fixed offset for Underflow segment .global _xt_underflow _xt_underflow: l32e a8, a1, -64 // a8 <- [a1-32] l32e a9, a1, -64 // a9 <- [a1-28] l32e a10, a1, -64 // a10 <- [a1-24] l32e a11, a1, -64 // a11 <- [a1-20] l32e a12, a1, -64 // a12 <- [a1-16] l32e a13, a1, -64 // a13 <- [a1-12] l32e a14, a1, -64 // a14 <- [a1-8] l32e a15, a1, -64 // a15 <- [a1-4] ; Return (branch to EPC) #endif .org 60 // Fixed offset for Save/Overflow segment .global _xt_save _xt_save: #ifdef __XTENSA_CALL0_ABI__ s32e a0, a1, -64 // [a1-64] <- a0 s32e a2, a1, -48 // [a1-56] <- a2 ; a2 <- EPC s32e a3, a1, -64 // [a1-52] <- a3 s32e a4, a1, -64 // [a1-48] <- a4 s32e a5, a1, -64 // [a1-44] <- a5 s32e a6, a1, -64 // [a1-40] <- a6 s32e a7, a1, -64 // [a1-36] <- a7 #else .global _xt_overflow _xt_overflow: #endif s32e a8, a1, -52 // [a1-32] <- a8 ; a8 <- ExcVAddr s32e a9, a1, -28 // [a1-28] <- a9 ; a9 <- PS/SAR s32e a10, a1, -48 // [a1-24] <- a10 ; a10 <- EPC s32e a11, a1, -24 // [a1-20] <- a11 ; a11 <- ExcCause s32e a12, a1, -44 // [a1-16] <- a12 ; a12 <- LBEG s32e a13, a1, -40 // [a1-12] <- a13 ; a13 <- LEND s32e a14, a1, -36 // [a1-8] <- a14 ; a14 <- LCOUNT s32e a15, a1, -32 // [a1-4] <- a15 ; a15 <- a1 // If Overflow then return (branch to EPC) _xt_entry: s32e a8, a1, -4 // [a1-68] <- a8 (ExcVAddr) s32e a11, a1, -8 // [a1-72] <- a11 (ExcCause) #if XCHAL_HAVE_LOOPS s32e a12, a1, -20 // [a1-84] <- a12 (LBEG) s32e a13, a1, -24 // [a1-88] <- a13 (LEND) s32e a14, a1, -28 // [a1-92] <- a14 (LCOUNT) #endif #if XCHAL_HAVE_EXCLUSIVE movi a12, 0 getex a12 s32e a12, a1, -32 // [a1-96] <- a12 (ATOMCTL) #endif j 1f // make room for literals .align 4 .literal_position .Le1: .word _xt_exception_table 1: // Call OS-specific code for additional work to be done. Stay on interruptee's // stack in case more saves are required into stack frame. // NOTE: OS-specific code can use a8, a12-a14, (+a2-a7: call0, a15: windowed). // ALL other registers must be preserved. XT_RTOS_INT_ENTER // This sequence checks the interrupt controller and loads the interrupt // number if available, and also loads the wrapper handler address. // If there is an interrupt, execution will branch to the wrapper which // will then forward to the correct handler. // All this happens only if there is a pending interrupt. If not, execution // falls through to exception handling. #ifdef __XTENSA_CALL0_ABI__ s32si.x4 a2, a1 // [a1-80] <- a2 (EPC) ; a2 <- (intnum << 2) movi a0, 0 l32dis.it a0, a0 // a0 <- wrapper addr (handler_table[0]) s32stk a9, a1, 96 // [a1-76] <- a9 (PS/SAR) ; a1 = a1-96 #if XT_STK_XTRA_SZ addi a1, a1, -XT_STK_XTRA_SZ // Adjust for extra save area #endif s32dis.h a0, a0 // Jump to handler if interrupt else fall through #else // windowed s32si.x4 a10, a1 // [a1-80] <- a10 (EPC) ; a10 <- (intnum << 2) movi a8, 0 l32dis.it a8, a8 // a8 <- wrapper addr (handler_table[0]) s32stk a9, a1, 96 // [a1-76] <- a9 (PS/SAR) ; a9 = a1-96 #if XT_STK_XTRA_SZ addi a9, a9, -XT_STK_XTRA_SZ // Adjust for extra save area #endif s32dis.h a8, a8 // Jump to handler if interrupt else fall through #endif // __XTENSA_CALL0_ABI__ // At this point we have: // (note window has rotated for windowed ABI) // a0 holds return address (Tailchain+3) // For call0: // a11 holds ExcCause, also saved in [oldsp - 72] // a15 holds exception SP, a1 points to exception frame // For windowed: // a3 holds ExcCause, also saved in [oldsp - 72] // a1 points to exception frame .global _xt_exception _xt_exception: l32r a2, .Le1 // Load exc table address #ifdef __XTENSA_CALL0_ABI__ mov a3, a11 // Copy exception cause to a3 #endif extui a4, a3, 0, 4 // Extract exception cause addx4 a2, a4, a2 // Index into exc table l32i a4, a2, 0 // Load handler address #if XT_STK_XTRA_SZ addi a2, a1, XT_STK_XTRA_SZ // Argument = Exception frame ptr #else mov a2, a1 // Argument = Exception frame ptr #endif jx a4 // Return directly from handler // Exit/restore sequence .global _xt_exit _xt_exit: #ifdef __XTENSA_CALL0_ABI__ mov a1, a15 // Restore stack pointer #endif // Run OS-specific code to determine what to restore. // Interrupts will remain disabled through this sequence. // WARNING: stack pointer may change within this macro // so all restores off the stack must happen afterwards. XT_RTOS_INT_EXIT .global _xt_restore _xt_restore: // Some loads must happen before DISPST = Restore, as these // will not be accessible via L32E once DISPST = Restore. #if XCHAL_HAVE_EXCLUSIVE l32e a12, a1, -32 // a12 <- [a1-96] (ATOMCTL) getex a12 #endif l32e a10, a1, -12 // a10 <- [a1-76] (PS/SAR) l32e a12, a1, -20 // a12 <- [a1-84] (LBEG) l32e a13, a1, -24 // a13 <- [a1-88] (LEND) l32e a14, a1, -28 // a14 <- [a1-92] (LCOUNT) l32dis.epc a11, a1 // a11 <- [a1-80] (EPC) // If interrupt goto tailchain else fall through #ifdef __XTENSA_CALL0_ABI__ l32e a0, a1, -64 // a0 <- [a1-64] l32e a2, a1, -64 // a2 <- [a1-56] l32e a3, a1, -64 // a3 <- [a1-52] l32e a4, a1, -64 // a4 <- [a1-48] l32e a5, a1, -64 // a5 <- [a1-44] l32e a6, a1, -64 // a6 <- [a1-40] l32e a7, a1, -64 // a7 <- [a1-36] #endif // Important: the following restrictions must be observed: // 1) The LCOUNT register must be restored after LBEG/LEND. // 2) There must be at least 3 instructions between the LCOUNT // restore and the last L32E (the one that branches). l32e a12, a1, -44 // LBEG <- a12, a12 <- [a1-16] l32e a13, a1, -40 // LEND <- a13, a13 <- [a1-12] l32e a14, a1, -36 // LCOUNT <- a14, a14 <- [a1-8] l32e a8, a1, -64 // a8 <- [a1-32] l32e a9, a1, -64 // a9 <- [a1-28] l32e a10, a1, -60 // PS/SAR <- a10, a10 <- [a1-24] l32e a11, a1, -48 // EPC <- a11, a11 <- [a1-20] l32e a15, a1, -64 // a15 <- [a1-4], Branch to EPC if no interrupt // If interrupt, branch to Tailchain //----------------------------------------------------------------------------- // Branch to reset handler code from here. Use CALL0 as a branch, will expand // to CALLX0 if needed when built with the -mlongcalls option. //----------------------------------------------------------------------------- .align 4 .local _JumpToResetHandler _JumpToResetHandler: call0 _ResetHandler //----------------------------------------------------------------------------- // Idle loop. On interrupt, no state needs saving. //----------------------------------------------------------------------------- .align 4 .global _xt_idle _xt_idle: movi a14, _xt_interrupt_stack_top mov a1, a14 // a1 <- Top of interrupt stack movi a14, 0 // 0 = Normal wsr.ms a14 // Set DISPST = Normal rsync waiti 0 // Wait for interrupt memw // HW erratum 569 //----------------------------------------------------------------------------- // Scheduler interrupt handler. Triggered by context switch. At this time only // useful for windowed ABI to spill register windows. //----------------------------------------------------------------------------- .align 4 .global xt_sched_handler xt_sched_handler: #ifdef __XTENSA_WINDOWED_ABI__ entry a1, 32 ssai 1 spillw retw #else ret #endif #endif // XCHAL_HAVE_XEA3