// High-Priority Interrupt Dispatcher Template // $Id: //depot/rel/Cottonwood/Xtensa/OS/xtos/int-highpri-dispatcher.S#4 $ // Copyright (c) 2004-2010 Tensilica 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. // // This file allows writing high-priority interrupt handlers in C, // providing convenience at a significant cost in performance. // // By default, this file is included by inth-template.S . // The default Makefile defines _INTERRUPT_LEVEL when assembling // inth-template.S for each medium and high priority interrupt level. // // To use this template file, define a macro called _INTERRUPT_LEVEL // to be the interrupt priority level of the vector, then include this file. #include #include "xtos-internal.h" #if XCHAL_HAVE_INTERRUPTS #define INTERRUPT_MASK XCHAL_INTLEVEL_MASK(_INTERRUPT_LEVEL) #define SINGLE_INTERRUPT ((INTERRUPT_MASK & (INTERRUPT_MASK - 1)) == 0) #define SINGLE_INT_NUM XCHAL_INTLEVEL_NUM(_INTERRUPT_LEVEL) #define INTLEVEL_N_MASK INTERRUPT_MASK // mask of interrupts at this priority #define INTLEVEL_N_NUM SINGLE_INT_NUM // interrupt number if there is only one #define INTLEVEL_N_BELOW_MASK XCHAL_INTLEVEL_ANDBELOW_MASK(_INTERRUPT_LEVEL) /* Indicates whether there are multiple interrupts at this interrupt * priority, ie. mapped to this interrupt vector. * If there is only one, its number is INTLEVEL_N_NUM */ #define MULTIPLE_INTERRUPTS (!SINGLE_INTERRUPT) /* * High priority interrupt stack frame: */ STRUCT_BEGIN STRUCT_FIELD (long,4,HESF_,SAR) STRUCT_FIELD (long,4,HESF_,WINDOWSTART) STRUCT_FIELD (long,4,HESF_,WINDOWBASE) STRUCT_FIELD (long,4,HESF_,EPC1) STRUCT_FIELD (long,4,HESF_,EXCCAUSE) STRUCT_FIELD (long,4,HESF_,EXCVADDR) STRUCT_FIELD (long,4,HESF_,EXCSAVE1) STRUCT_FIELD (long,4,HESF_,VPRI) /* (XEA1 only) */ #if XCHAL_HAVE_MAC16 STRUCT_FIELD (long,4,HESF_,ACCLO) STRUCT_FIELD (long,4,HESF_,ACCHI) /*STRUCT_AFIELD(long,4,HESF_,MR, 4)*/ #endif #if XCHAL_HAVE_LOOPS STRUCT_FIELD (long,4,HESF_,LCOUNT) STRUCT_FIELD (long,4,HESF_,LBEG) STRUCT_FIELD (long,4,HESF_,LEND) #endif STRUCT_AFIELD(long,4,HESF_,AREG, 64) /* address registers ar0..ar63 */ #define HESF_AR(n) HESF_AREG+((n)*4) STRUCT_END(HighPriFrame) #define HESF_TOTALSIZE HighPriFrameSize+32 /* 32 bytes for interrupted code's save areas under SP */ #if XCHAL_HAVE_XEA1 && HAVE_XSR /* could be made true for T1040 and T1050 */ # error "high-priority interrupt stack frame needs adjustment if HAVE_XSR is allowed with XEA1" #endif #define PRI_N_STACK_SIZE 1024 /* default to 1 kB stack for each level-N handling */ // Allocate save area and stack: // (must use .bss, not .comm, because the subsequent .set does not work otherwise) .section .bss, "aw" .align 16 LABEL(_Pri_,_Stack): .space PRI_N_STACK_SIZE + HESF_TOTALSIZE #if HAVE_XSR .data .global LABEL(_Pri_,_HandlerAddress) LABEL(_Pri_,_HandlerAddress): .space 4 #endif .text .align 4 .global LABEL(_Level,FromVector) LABEL(_Level,FromVector): movi a2, LABEL(_Pri_,_Stack) + PRI_N_STACK_SIZE // get ptr to save area // interlock // Save a few registers so we can do some work: s32i a0, a2, HESF_AR(0) #if HAVE_XSR //movi a0, LABEL(_Level,FromVector) // this dispatcher's address movi a0, LABEL(_Pri_,_HandlerAddress) // dispatcher address var. s32i a1, a2, HESF_AR(1) l32i a0, a0, 0 // get dispatcher address s32i a3, a2, HESF_AR(3) xsr a0, EXCSAVE_LEVEL // get saved a2, restore dispatcher address #else rsr a0, EXCSAVE_LEVEL // get saved a2 s32i a1, a2, HESF_AR(1) s32i a3, a2, HESF_AR(3) #endif s32i a4, a2, HESF_AR(4) s32i a0, a2, HESF_AR(2) // Save/restore all exception state // (IMPORTANT: this code assumes no general exceptions occur // during the execution of this dispatcher until this state // is completely saved and from the point it is restored.) // // Exceptions that may normally occur within the C handler // include window exceptions (affecting EPC1), alloca exceptions // (affecting EPC1/EXCCAUSE and its handling uses EXCSAVE1), // and possibly others depending on the particular C handler // (possibly needing save/restore of EXCVADDR; and EXCVADDR // is also possibly corrupted by any access thru an auto-refill // way on a processor with a full MMU). // rsr a3, EPC1 rsr a4, EXCCAUSE s32i a3, a2, HESF_EPC1 s32i a4, a2, HESF_EXCCAUSE #if !XCHAL_HAVE_XEA1 rsr a3, EXCVADDR s32i a3, a2, HESF_EXCVADDR #endif rsr a4, EXCSAVE1 s32i a4, a2, HESF_EXCSAVE1 #ifdef __XTENSA_WINDOWED_ABI__ // Save remainder of entire address register file (!): movi a0, XCHAL_NUM_AREGS - 8 // how many saved so far #endif s32i a5, a2, HESF_AR(5) s32i a6, a2, HESF_AR(6) s32i a7, a2, HESF_AR(7) 1: s32i a8, a2, HESF_AR(8) s32i a9, a2, HESF_AR(9) s32i a10, a2, HESF_AR(10) s32i a11, a2, HESF_AR(11) s32i a12, a2, HESF_AR(12) s32i a13, a2, HESF_AR(13) s32i a14, a2, HESF_AR(14) s32i a15, a2, HESF_AR(15) #ifdef __XTENSA_WINDOWED_ABI__ addi a8, a0, -8 addi a10, a2, 8*4 rotw 2 bnez a0, 1b // loop until done rotw 2 // back to original a2 ... // Save a few other registers required for C: rsr a3, WINDOWSTART rsr a4, WINDOWBASE s32i a3, a2, HESF_WINDOWSTART s32i a4, a2, HESF_WINDOWBASE // Setup window registers for first caller: movi a3, 1 movi a4, 0 wsr a3, WINDOWSTART wsr a4, WINDOWBASE rsync // Note: register window has rotated, ie. a0..a15 clobbered. #endif /* __XTENSA_WINDOWED_ABI__ */ movi a1, LABEL(_Pri_,_Stack) + PRI_N_STACK_SIZE // get ptr to save area (is also initial stack ptr) movi a0, 0 // mark start of call frames in stack // Critical state saved, a bit more to do to allow window exceptions... // We now have a C-coherent stack and window state. // Still have to fix PS while making sure interrupts stay disabled // at the appropriate level (ie. level 2 and below are disabled in this case). #if XCHAL_HAVE_XEA1 movi a7, _xtos_intstruct // address of interrupt management globals rsilft a3, _INTERRUPT_LEVEL, XTOS_LOCKLEVEL // lockout movi a4, ~INTLEVEL_N_BELOW_MASK // mask out all interrupts at this level or lower l32i a3, a7, XTOS_VPRI_ENABLED_OFS // read previous _xtos_vpri_enabled l32i a5, a7, XTOS_ENABLED_OFS // read _xtos_enabled s32i a4, a7, XTOS_VPRI_ENABLED_OFS // set new _xtos_vpri_enabled (mask interrupts as if at _INTERRUPT_LEVEL) s32i a3, a1, HESF_VPRI // save previous vpri movi a2, 0x50020 // WOE=1, UM=1, INTLEVEL=0 and a3, a5, a4 // mask out selected interrupts wsr a3, INTENABLE // disable all low-priority interrupts #else // Load PS for C code, clear EXCM (NOTE: this step is different for XEA1): # ifdef __XTENSA_CALL0_ABI__ movi a2, 0x00020 + _INTERRUPT_LEVEL // WOE=0, CALLINC=0, UM=1, INTLEVEL=N, EXCM=0, RING=0 # else movi a2, 0x50020 + _INTERRUPT_LEVEL // WOE=1, CALLINC=1, UM=1, INTLEVEL=N, EXCM=0, RING=0 # endif #endif wsr a2, PS // update PS to enable window exceptions, etc as per above rsync // Okay, window exceptions can now happen (although we have to call // deep before any will happen because we've reset WINDOWSTART). // Save other state that might get clobbered by C code: ////////////////// COMMON DISPATCH CODE BEGIN rsr a14, SAR s32i a14, a1, HESF_SAR #if XCHAL_HAVE_LOOPS rsr a14, LCOUNT s32i a14, a1, HESF_LCOUNT rsr a14, LBEG s32i a14, a1, HESF_LBEG rsr a14, LEND s32i a14, a1, HESF_LEND #endif #if XCHAL_HAVE_MAC16 rsr a14, ACCLO s32i a14, a1, HESF_ACCLO rsr a14, ACCHI s32i a14, a1, HESF_ACCHI #endif #if MULTIPLE_INTERRUPTS /* > 1 interrupts at this priority */ // _split_ multi_setup #define TABLE_OFS 0 rsr a15, INTERRUPT // mask of pending interrupts # if XCHAL_HAVE_XEA1 l32i a12, a7, XTOS_ENABLED_OFS // mask of enabled interrupts # else rsr a12, INTENABLE // mask of enabled interrupts # endif movi a13, INTLEVEL_N_MASK // mask of interrupts at this priority level and a15, a15, a12 and a15, a15, a13 // enabled & pending interrupts at this priority _beqz a15, LABEL(Pri_,_spurious) // handle spurious interrupts (eg. level-trig.) LABEL(Pri_,_loop): // handle all enabled & pending interrupts neg a14, a15 and a14, a14, a15 // single-out least-significant bit set in mask wsr a14, INTCLEAR // clear if edge-trig. or s/w or wr/err (else no effect) // Compute pointer to interrupt table entry, given mask a14 with single bit set: # if XCHAL_HAVE_NSA movi a12, _xtos_interrupt_table - (32-XCHAL_NUM_INTERRUPTS)*8 nsau a14, a14 // get index of bit in a14, numbered from msbit addx8 a12, a14, a12 # else /* XCHAL_HAVE_NSA */ movi a12, _xtos_interrupt_table // pointer to interrupt table bltui a14, 0x10000, 1f // in 16 lsbits? (if so, check them) addi a12, a12, 16*8 // no, index is at least 16 entries further // (the above ADDI expands to an ADDI+ADDMI sequence, +128 is outside its range) extui a14, a14, 16,16 // shift right upper 16 bits 1: bltui a14, 0x100, 1f // in 8 lsbits? (if so, check them) addi a12, a12, 8*8 // no, index is at least 8 entries further srli a14, a14, 8 // shift right upper 8 bits 1: bltui a14, 0x10, 1f // in 4 lsbits? (if so, check them) addi a12, a12, 4*8 // no, index is at least 4 entries further srli a14, a14, 4 // shift right 4 bits 1: bltui a14, 0x4, 1f // in 2 lsbits? (if so, check them) addi a12, a12, 2*8 // no, index is at least 2 entries further srli a14, a14, 2 // shift right 2 bits 1: bltui a14, 0x2, 1f // is it the lsbit? addi a12, a12, 1*8 // no, index is one entry further 1: // done! a12 points to interrupt's table entry # endif /* XCHAL_HAVE_NSA */ #else /* !MULTIPLE_INTERRUPTS */ # if XCHAL_HAVE_NSA # define TABLE_OFS 8 * (XCHAL_NUM_INTERRUPTS - 1 - INTLEVEL_N_NUM) # else # define TABLE_OFS 8 * INTLEVEL_N_NUM # endif movi a13, INTLEVEL_N_MASK // (if interrupt is s/w or edge-triggered or write/err only) movi a12, _xtos_interrupt_table // get pointer to its interrupt table entry wsr a13, INTCLEAR // clear the interrupt (if s/w or edge or wr/err only) #endif /* ifdef MULTIPLE_INTERRUPTS */ l32i a13, a12, TABLE_OFS + 0 // get pointer to handler from table entry #ifdef __XTENSA_CALL0_ABI__ l32i a2, a12, TABLE_OFS + 4 // pass single argument to C handler callx0 a13 // call interrupt's C handler #else l32i a6, a12, TABLE_OFS + 4 // pass single argument to C handler callx4 a13 // call interrupt's C handler #endif #if XCHAL_HAVE_XEA1 movi a7, _xtos_intstruct // address of interrupt management globals #endif #if MULTIPLE_INTERRUPTS /* > 1 interrupts at this priority */ rsr a15, INTERRUPT // get pending interrupts # if XCHAL_HAVE_XEA1 l32i a12, a7, XTOS_ENABLED_OFS // get enabled interrupts # else rsr a12, INTENABLE // get enabled interrupts # endif movi a13, INTLEVEL_N_MASK // get mask of interrupts at this priority level and a15, a15, a12 and a15, a15, a13 // pending+enabled interrupts at this priority _bnez a15, LABEL(Pri_,_loop) // if any remain, dispatch one LABEL(Pri_,_spurious): #endif /* MULTIPLE_INTERRUPTS */ // Restore everything, and return. // Three temp registers are required for this code to be optimal (no interlocks) in // T2xxx microarchitectures with 7-stage pipe; otherwise only two // registers would be needed. // #if XCHAL_HAVE_LOOPS l32i a13, a1, HESF_LCOUNT l32i a14, a1, HESF_LBEG l32i a15, a1, HESF_LEND wsr a13, LCOUNT wsr a14, LBEG wsr a15, LEND #endif #if XCHAL_HAVE_MAC16 l32i a13, a1, HESF_ACCLO l32i a14, a1, HESF_ACCHI wsr a13, ACCLO wsr a14, ACCHI #endif l32i a15, a1, HESF_SAR wsr a15, SAR ////////////////// COMMON DISPATCH CODE END #if XCHAL_HAVE_XEA1 // Here, a7 = address of interrupt management globals l32i a4, a1, HESF_VPRI // restore previous vpri rsil a3, XTOS_LOCKLEVEL // lockout l32i a5, a7, XTOS_ENABLED_OFS // read _xtos_enabled s32i a4, a7, XTOS_VPRI_ENABLED_OFS // set new _xtos_vpri_enabled movi a2, 0x00020 + _INTERRUPT_LEVEL // WOE=0, UM=1, INTLEVEL=N and a3, a5, a4 // mask out selected interrupts wsr a3, INTENABLE // disable all low-priority interrupts #else // Load PS for interrupt exit, set EXCM: movi a2, 0x00030 + _INTERRUPT_LEVEL // WOE=0, CALLINC=0, UM=1, INTLEVEL=N, EXCM=1, RING=0 #endif wsr a2, PS // update PS to disable window exceptions, etc as per above rsync // NOTE: here for XEA1, restore INTENABLE etc... #ifdef __XTENSA_WINDOWED_ABI__ // Restore window registers: l32i a2, a1, HESF_WINDOWSTART l32i a3, a1, HESF_WINDOWBASE wsr a2, WINDOWSTART wsr a3, WINDOWBASE rsync // Note: register window has rotated, ie. a0..a15 clobbered. // Reload initial stack pointer: movi a1, LABEL(_Pri_,_Stack) + PRI_N_STACK_SIZE // - 16 movi a6, XCHAL_NUM_AREGS - 8 // how many saved so far addi a7, a1, -8*4 // Restore entire register file (!): 1: addi a14, a6, -8 addi a15, a7, 8*4 l32i a4, a15, HESF_AR(4) l32i a5, a15, HESF_AR(5) l32i a6, a15, HESF_AR(6) l32i a7, a15, HESF_AR(7) l32i a8, a15, HESF_AR(8) l32i a9, a15, HESF_AR(9) l32i a10,a15, HESF_AR(10) l32i a11,a15, HESF_AR(11) rotw 2 bnez a6, 1b // loop until done l32i a4, a7, HESF_AR(12) l32i a5, a7, HESF_AR(13) l32i a6, a7, HESF_AR(14) l32i a7, a7, HESF_AR(15) rotw 2 // back to original a1 ... #else /* Call0 ABI: */ l32i a4, a1, HESF_AR(4) // restore general registers l32i a5, a1, HESF_AR(5) l32i a6, a1, HESF_AR(6) l32i a7, a1, HESF_AR(7) l32i a8, a1, HESF_AR(8) l32i a9, a1, HESF_AR(9) l32i a10, a1, HESF_AR(10) l32i a11, a1, HESF_AR(11) l32i a12, a1, HESF_AR(12) l32i a13, a1, HESF_AR(13) l32i a14, a1, HESF_AR(14) l32i a15, a1, HESF_AR(15) #endif /* __XTENSA_WINDOWED_ABI__ */ // Restore exception state: l32i a2, a1, HESF_EPC1 l32i a3, a1, HESF_EXCCAUSE wsr a2, EPC1 wsr a3, EXCCAUSE #if !XCHAL_HAVE_XEA1 l32i a2, a1, HESF_EXCVADDR wsr a2, EXCVADDR #endif l32i a3, a1, HESF_EXCSAVE1 wsr a3, EXCSAVE1 l32i a0, a1, HESF_AR(0) l32i a2, a1, HESF_AR(2) l32i a3, a1, HESF_AR(3) l32i a1, a1, HESF_AR(1) rfi _INTERRUPT_LEVEL .size LABEL(_Level,FromVector), . - LABEL(_Level,FromVector) // This symbol exists solely for the purpose of being able to pull-in this // dispatcher using _xtos_dispatch_level() routines with the tiny-rt LSP: .global LABEL(_Level,HandlerLabel) .set LABEL(_Level,HandlerLabel), 0 #endif /* XCHAL_HAVE_INTERRUPTS */