1 // High-Priority Interrupt Dispatcher Template
2 // $Id: //depot/rel/Cottonwood/Xtensa/OS/xtos/int-highpri-dispatcher.S#4 $
4 // Copyright (c) 2004-2010 Tensilica Inc.
6 // Permission is hereby granted, free of charge, to any person obtaining
7 // a copy of this software and associated documentation files (the
8 // "Software"), to deal in the Software without restriction, including
9 // without limitation the rights to use, copy, modify, merge, publish,
10 // distribute, sublicense, and/or sell copies of the Software, and to
11 // permit persons to whom the Software is furnished to do so, subject to
12 // the following conditions:
14 // The above copyright notice and this permission notice shall be included
15 // in all copies or substantial portions of the Software.
17 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
20 // IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
21 // CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
22 // TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
23 // SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
26 // This file allows writing high-priority interrupt handlers in C,
27 // providing convenience at a significant cost in performance.
29 // By default, this file is included by inth-template.S .
30 // The default Makefile defines _INTERRUPT_LEVEL when assembling
31 // inth-template.S for each medium and high priority interrupt level.
33 // To use this template file, define a macro called _INTERRUPT_LEVEL
34 // to be the interrupt priority level of the vector, then include this file.
37 #include <xtensa/coreasm.h>
38 #include "xtos-internal.h"
41 #if XCHAL_HAVE_INTERRUPTS
43 #define INTERRUPT_MASK XCHAL_INTLEVEL_MASK(_INTERRUPT_LEVEL)
44 #define SINGLE_INTERRUPT ((INTERRUPT_MASK & (INTERRUPT_MASK - 1)) == 0)
45 #define SINGLE_INT_NUM XCHAL_INTLEVEL_NUM(_INTERRUPT_LEVEL)
48 #define INTLEVEL_N_MASK INTERRUPT_MASK // mask of interrupts at this priority
49 #define INTLEVEL_N_NUM SINGLE_INT_NUM // interrupt number if there is only one
50 #define INTLEVEL_N_BELOW_MASK XCHAL_INTLEVEL_ANDBELOW_MASK(_INTERRUPT_LEVEL)
52 /* Indicates whether there are multiple interrupts at this interrupt
53 * priority, ie. mapped to this interrupt vector.
54 * If there is only one, its number is INTLEVEL_N_NUM
56 #define MULTIPLE_INTERRUPTS (!SINGLE_INTERRUPT)
59 * High priority interrupt stack frame:
62 STRUCT_FIELD (long,4,HESF_,SAR)
63 STRUCT_FIELD (long,4,HESF_,WINDOWSTART)
64 STRUCT_FIELD (long,4,HESF_,WINDOWBASE)
65 STRUCT_FIELD (long,4,HESF_,EPC1)
66 STRUCT_FIELD (long,4,HESF_,EXCCAUSE)
67 STRUCT_FIELD (long,4,HESF_,EXCVADDR)
68 STRUCT_FIELD (long,4,HESF_,EXCSAVE1)
69 STRUCT_FIELD (long,4,HESF_,VPRI) /* (XEA1 only) */
71 STRUCT_FIELD (long,4,HESF_,ACCLO)
72 STRUCT_FIELD (long,4,HESF_,ACCHI)
73 /*STRUCT_AFIELD(long,4,HESF_,MR, 4)*/
76 STRUCT_FIELD (long,4,HESF_,LCOUNT)
77 STRUCT_FIELD (long,4,HESF_,LBEG)
78 STRUCT_FIELD (long,4,HESF_,LEND)
80 STRUCT_AFIELD(long,4,HESF_,AREG, 64) /* address registers ar0..ar63 */
81 #define HESF_AR(n) HESF_AREG+((n)*4)
82 STRUCT_END(HighPriFrame)
83 #define HESF_TOTALSIZE HighPriFrameSize+32 /* 32 bytes for interrupted code's save areas under SP */
86 #if XCHAL_HAVE_XEA1 && HAVE_XSR /* could be made true for T1040 and T1050 */
87 # error "high-priority interrupt stack frame needs adjustment if HAVE_XSR is allowed with XEA1"
91 #define PRI_N_STACK_SIZE 1024 /* default to 1 kB stack for each level-N handling */
94 // Allocate save area and stack:
95 // (must use .bss, not .comm, because the subsequent .set does not work otherwise)
98 LABEL(_Pri_,_Stack): .space PRI_N_STACK_SIZE + HESF_TOTALSIZE
102 .global LABEL(_Pri_,_HandlerAddress)
103 LABEL(_Pri_,_HandlerAddress): .space 4
109 .global LABEL(_Level,FromVector)
110 LABEL(_Level,FromVector):
111 movi a2, LABEL(_Pri_,_Stack) + PRI_N_STACK_SIZE // get ptr to save area
114 // Save a few registers so we can do some work:
115 s32i a0, a2, HESF_AR(0)
117 //movi a0, LABEL(_Level,FromVector) // this dispatcher's address
118 movi a0, LABEL(_Pri_,_HandlerAddress) // dispatcher address var.
119 s32i a1, a2, HESF_AR(1)
120 l32i a0, a0, 0 // get dispatcher address
121 s32i a3, a2, HESF_AR(3)
122 xsr a0, EXCSAVE_LEVEL // get saved a2, restore dispatcher address
124 rsr a0, EXCSAVE_LEVEL // get saved a2
125 s32i a1, a2, HESF_AR(1)
126 s32i a3, a2, HESF_AR(3)
128 s32i a4, a2, HESF_AR(4)
129 s32i a0, a2, HESF_AR(2)
131 // Save/restore all exception state
132 // (IMPORTANT: this code assumes no general exceptions occur
133 // during the execution of this dispatcher until this state
134 // is completely saved and from the point it is restored.)
136 // Exceptions that may normally occur within the C handler
137 // include window exceptions (affecting EPC1), alloca exceptions
138 // (affecting EPC1/EXCCAUSE and its handling uses EXCSAVE1),
139 // and possibly others depending on the particular C handler
140 // (possibly needing save/restore of EXCVADDR; and EXCVADDR
141 // is also possibly corrupted by any access thru an auto-refill
142 // way on a processor with a full MMU).
146 s32i a3, a2, HESF_EPC1
147 s32i a4, a2, HESF_EXCCAUSE
150 s32i a3, a2, HESF_EXCVADDR
153 s32i a4, a2, HESF_EXCSAVE1
155 #ifdef __XTENSA_WINDOWED_ABI__
156 // Save remainder of entire address register file (!):
157 movi a0, XCHAL_NUM_AREGS - 8 // how many saved so far
160 s32i a5, a2, HESF_AR(5)
161 s32i a6, a2, HESF_AR(6)
162 s32i a7, a2, HESF_AR(7)
164 1: s32i a8, a2, HESF_AR(8)
165 s32i a9, a2, HESF_AR(9)
166 s32i a10, a2, HESF_AR(10)
167 s32i a11, a2, HESF_AR(11)
168 s32i a12, a2, HESF_AR(12)
169 s32i a13, a2, HESF_AR(13)
170 s32i a14, a2, HESF_AR(14)
171 s32i a15, a2, HESF_AR(15)
173 #ifdef __XTENSA_WINDOWED_ABI__
177 bnez a0, 1b // loop until done
180 // back to original a2 ...
182 // Save a few other registers required for C:
185 s32i a3, a2, HESF_WINDOWSTART
186 s32i a4, a2, HESF_WINDOWBASE
188 // Setup window registers for first caller:
195 // Note: register window has rotated, ie. a0..a15 clobbered.
197 #endif /* __XTENSA_WINDOWED_ABI__ */
199 movi a1, LABEL(_Pri_,_Stack) + PRI_N_STACK_SIZE // get ptr to save area (is also initial stack ptr)
200 movi a0, 0 // mark start of call frames in stack
202 // Critical state saved, a bit more to do to allow window exceptions...
204 // We now have a C-coherent stack and window state.
205 // Still have to fix PS while making sure interrupts stay disabled
206 // at the appropriate level (ie. level 2 and below are disabled in this case).
209 movi a7, _xtos_intstruct // address of interrupt management globals
210 rsilft a3, _INTERRUPT_LEVEL, XTOS_LOCKLEVEL // lockout
211 movi a4, ~INTLEVEL_N_BELOW_MASK // mask out all interrupts at this level or lower
212 l32i a3, a7, XTOS_VPRI_ENABLED_OFS // read previous _xtos_vpri_enabled
213 l32i a5, a7, XTOS_ENABLED_OFS // read _xtos_enabled
214 s32i a4, a7, XTOS_VPRI_ENABLED_OFS // set new _xtos_vpri_enabled (mask interrupts as if at _INTERRUPT_LEVEL)
215 s32i a3, a1, HESF_VPRI // save previous vpri
216 movi a2, 0x50020 // WOE=1, UM=1, INTLEVEL=0
217 and a3, a5, a4 // mask out selected interrupts
218 wsr a3, INTENABLE // disable all low-priority interrupts
220 // Load PS for C code, clear EXCM (NOTE: this step is different for XEA1):
221 # ifdef __XTENSA_CALL0_ABI__
222 movi a2, 0x00020 + _INTERRUPT_LEVEL // WOE=0, CALLINC=0, UM=1, INTLEVEL=N, EXCM=0, RING=0
224 movi a2, 0x50020 + _INTERRUPT_LEVEL // WOE=1, CALLINC=1, UM=1, INTLEVEL=N, EXCM=0, RING=0
228 wsr a2, PS // update PS to enable window exceptions, etc as per above
231 // Okay, window exceptions can now happen (although we have to call
232 // deep before any will happen because we've reset WINDOWSTART).
234 // Save other state that might get clobbered by C code:
236 ////////////////// COMMON DISPATCH CODE BEGIN
239 s32i a14, a1, HESF_SAR
242 s32i a14, a1, HESF_LCOUNT
244 s32i a14, a1, HESF_LBEG
246 s32i a14, a1, HESF_LEND
250 s32i a14, a1, HESF_ACCLO
252 s32i a14, a1, HESF_ACCHI
255 #if MULTIPLE_INTERRUPTS /* > 1 interrupts at this priority */ // _split_ multi_setup
258 rsr a15, INTERRUPT // mask of pending interrupts
260 l32i a12, a7, XTOS_ENABLED_OFS // mask of enabled interrupts
262 rsr a12, INTENABLE // mask of enabled interrupts
264 movi a13, INTLEVEL_N_MASK // mask of interrupts at this priority level
266 and a15, a15, a13 // enabled & pending interrupts at this priority
267 _beqz a15, LABEL(Pri_,_spurious) // handle spurious interrupts (eg. level-trig.)
268 LABEL(Pri_,_loop): // handle all enabled & pending interrupts
270 and a14, a14, a15 // single-out least-significant bit set in mask
271 wsr a14, INTCLEAR // clear if edge-trig. or s/w or wr/err (else no effect)
273 // Compute pointer to interrupt table entry, given mask a14 with single bit set:
276 movi a12, _xtos_interrupt_table - (32-XCHAL_NUM_INTERRUPTS)*8
277 nsau a14, a14 // get index of bit in a14, numbered from msbit
279 # else /* XCHAL_HAVE_NSA */
280 movi a12, _xtos_interrupt_table // pointer to interrupt table
281 bltui a14, 0x10000, 1f // in 16 lsbits? (if so, check them)
282 addi a12, a12, 16*8 // no, index is at least 16 entries further
283 // (the above ADDI expands to an ADDI+ADDMI sequence, +128 is outside its range)
284 extui a14, a14, 16,16 // shift right upper 16 bits
285 1: bltui a14, 0x100, 1f // in 8 lsbits? (if so, check them)
286 addi a12, a12, 8*8 // no, index is at least 8 entries further
287 srli a14, a14, 8 // shift right upper 8 bits
288 1: bltui a14, 0x10, 1f // in 4 lsbits? (if so, check them)
289 addi a12, a12, 4*8 // no, index is at least 4 entries further
290 srli a14, a14, 4 // shift right 4 bits
291 1: bltui a14, 0x4, 1f // in 2 lsbits? (if so, check them)
292 addi a12, a12, 2*8 // no, index is at least 2 entries further
293 srli a14, a14, 2 // shift right 2 bits
294 1: bltui a14, 0x2, 1f // is it the lsbit?
295 addi a12, a12, 1*8 // no, index is one entry further
296 1: // done! a12 points to interrupt's table entry
297 # endif /* XCHAL_HAVE_NSA */
299 #else /* !MULTIPLE_INTERRUPTS */
302 # define TABLE_OFS 8 * (XCHAL_NUM_INTERRUPTS - 1 - INTLEVEL_N_NUM)
304 # define TABLE_OFS 8 * INTLEVEL_N_NUM
307 movi a13, INTLEVEL_N_MASK // (if interrupt is s/w or edge-triggered or write/err only)
308 movi a12, _xtos_interrupt_table // get pointer to its interrupt table entry
309 wsr a13, INTCLEAR // clear the interrupt (if s/w or edge or wr/err only)
311 #endif /* ifdef MULTIPLE_INTERRUPTS */
313 l32i a13, a12, TABLE_OFS + 0 // get pointer to handler from table entry
314 #ifdef __XTENSA_CALL0_ABI__
315 l32i a2, a12, TABLE_OFS + 4 // pass single argument to C handler
316 callx0 a13 // call interrupt's C handler
318 l32i a6, a12, TABLE_OFS + 4 // pass single argument to C handler
319 callx4 a13 // call interrupt's C handler
323 movi a7, _xtos_intstruct // address of interrupt management globals
325 #if MULTIPLE_INTERRUPTS /* > 1 interrupts at this priority */
326 rsr a15, INTERRUPT // get pending interrupts
328 l32i a12, a7, XTOS_ENABLED_OFS // get enabled interrupts
330 rsr a12, INTENABLE // get enabled interrupts
332 movi a13, INTLEVEL_N_MASK // get mask of interrupts at this priority level
334 and a15, a15, a13 // pending+enabled interrupts at this priority
335 _bnez a15, LABEL(Pri_,_loop) // if any remain, dispatch one
336 LABEL(Pri_,_spurious):
337 #endif /* MULTIPLE_INTERRUPTS */
339 // Restore everything, and return.
341 // Three temp registers are required for this code to be optimal (no interlocks) in
342 // T2xxx microarchitectures with 7-stage pipe; otherwise only two
343 // registers would be needed.
346 l32i a13, a1, HESF_LCOUNT
347 l32i a14, a1, HESF_LBEG
348 l32i a15, a1, HESF_LEND
355 l32i a13, a1, HESF_ACCLO
356 l32i a14, a1, HESF_ACCHI
360 l32i a15, a1, HESF_SAR
363 ////////////////// COMMON DISPATCH CODE END
366 // Here, a7 = address of interrupt management globals
367 l32i a4, a1, HESF_VPRI // restore previous vpri
368 rsil a3, XTOS_LOCKLEVEL // lockout
369 l32i a5, a7, XTOS_ENABLED_OFS // read _xtos_enabled
370 s32i a4, a7, XTOS_VPRI_ENABLED_OFS // set new _xtos_vpri_enabled
371 movi a2, 0x00020 + _INTERRUPT_LEVEL // WOE=0, UM=1, INTLEVEL=N
372 and a3, a5, a4 // mask out selected interrupts
373 wsr a3, INTENABLE // disable all low-priority interrupts
375 // Load PS for interrupt exit, set EXCM:
376 movi a2, 0x00030 + _INTERRUPT_LEVEL // WOE=0, CALLINC=0, UM=1, INTLEVEL=N, EXCM=1, RING=0
378 wsr a2, PS // update PS to disable window exceptions, etc as per above
381 // NOTE: here for XEA1, restore INTENABLE etc...
383 #ifdef __XTENSA_WINDOWED_ABI__
384 // Restore window registers:
385 l32i a2, a1, HESF_WINDOWSTART
386 l32i a3, a1, HESF_WINDOWBASE
390 // Note: register window has rotated, ie. a0..a15 clobbered.
392 // Reload initial stack pointer:
393 movi a1, LABEL(_Pri_,_Stack) + PRI_N_STACK_SIZE // - 16
394 movi a6, XCHAL_NUM_AREGS - 8 // how many saved so far
397 // Restore entire register file (!):
402 l32i a4, a15, HESF_AR(4)
403 l32i a5, a15, HESF_AR(5)
404 l32i a6, a15, HESF_AR(6)
405 l32i a7, a15, HESF_AR(7)
406 l32i a8, a15, HESF_AR(8)
407 l32i a9, a15, HESF_AR(9)
408 l32i a10,a15, HESF_AR(10)
409 l32i a11,a15, HESF_AR(11)
411 bnez a6, 1b // loop until done
413 l32i a4, a7, HESF_AR(12)
414 l32i a5, a7, HESF_AR(13)
415 l32i a6, a7, HESF_AR(14)
416 l32i a7, a7, HESF_AR(15)
419 // back to original a1 ...
421 #else /* Call0 ABI: */
423 l32i a4, a1, HESF_AR(4) // restore general registers
424 l32i a5, a1, HESF_AR(5)
425 l32i a6, a1, HESF_AR(6)
426 l32i a7, a1, HESF_AR(7)
427 l32i a8, a1, HESF_AR(8)
428 l32i a9, a1, HESF_AR(9)
429 l32i a10, a1, HESF_AR(10)
430 l32i a11, a1, HESF_AR(11)
431 l32i a12, a1, HESF_AR(12)
432 l32i a13, a1, HESF_AR(13)
433 l32i a14, a1, HESF_AR(14)
434 l32i a15, a1, HESF_AR(15)
436 #endif /* __XTENSA_WINDOWED_ABI__ */
438 // Restore exception state:
439 l32i a2, a1, HESF_EPC1
440 l32i a3, a1, HESF_EXCCAUSE
444 l32i a2, a1, HESF_EXCVADDR
447 l32i a3, a1, HESF_EXCSAVE1
450 l32i a0, a1, HESF_AR(0)
451 l32i a2, a1, HESF_AR(2)
452 l32i a3, a1, HESF_AR(3)
453 l32i a1, a1, HESF_AR(1)
456 .size LABEL(_Level,FromVector), . - LABEL(_Level,FromVector)
458 // This symbol exists solely for the purpose of being able to pull-in this
459 // dispatcher using _xtos_dispatch_level<n>() routines with the tiny-rt LSP:
460 .global LABEL(_Level,HandlerLabel)
461 .set LABEL(_Level,HandlerLabel), 0
463 #endif /* XCHAL_HAVE_INTERRUPTS */