2 * FreeRTOS Kernel V11.1.0
3 * Copyright (C) 2015-2019 Cadence Design Systems, Inc.
4 * Copyright (C) 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
6 * SPDX-License-Identifier: MIT
8 * Permission is hereby granted, free of charge, to any person obtaining a copy of
9 * this software and associated documentation files (the "Software"), to deal in
10 * the Software without restriction, including without limitation the rights to
11 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
12 * the Software, and to permit persons to whom the Software is furnished to do so,
13 * subject to the following conditions:
15 * The above copyright notice and this permission notice shall be included in all
16 * copies or substantial portions of the Software.
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
20 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
21 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
22 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
23 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
25 * https://www.FreeRTOS.org
26 * https://github.com/FreeRTOS
31 XTENSA VECTORS AND LOW LEVEL HANDLERS FOR AN RTOS
33 Xtensa low level exception and interrupt vectors and handlers for an RTOS.
35 Interrupt handlers and user exception handlers support interaction with
36 the RTOS by calling XT_RTOS_INT_ENTER and XT_RTOS_INT_EXIT before and
37 after user's specific interrupt handlers. These macros are defined in
38 xtensa_<rtos>.h to call suitable functions in a specific RTOS.
40 Users can install application-specific interrupt handlers for low and
41 medium level interrupts, by calling xt_set_interrupt_handler(). These
42 handlers can be written in C, and must obey C calling convention. The
43 handler table is indexed by the interrupt number. Each handler may be
44 provided with an argument.
46 Note that the system timer interrupt is handled specially, and is
47 dispatched to the RTOS-specific handler. This timer cannot be hooked
50 Optional hooks are also provided to install a handler per level at
51 run-time, made available by compiling this source file with
52 '-DXT_INTEXC_HOOKS' (useful for automated testing).
54 !! This file is a template that usually needs to be modified to handle !!
55 !! application specific interrupts. Search USER_EDIT for helpful comments !!
56 !! on where to insert handlers and how to write them. !!
58 Users can also install application-specific exception handlers in the
59 same way, by calling xt_set_exception_handler(). One handler slot is
60 provided for each exception type. Note that some exceptions are handled
61 by the porting layer itself, and cannot be taken over by application
62 code in this manner. These are the alloca, syscall, and coprocessor
65 The exception handlers can be written in C, and must follow C calling
66 convention. Each handler is passed a pointer to an exception frame as
67 its single argument. The exception frame is created on the stack, and
68 holds the saved context of the thread that took the exception. If the
69 handler returns, the context will be restored and the instruction that
70 caused the exception will be retried. If the handler makes any changes
71 to the saved state in the exception frame, the changes will be applied
72 when restoring the context.
74 Because Xtensa is a configurable architecture, this port supports all user
75 generated configurations (except restrictions stated in the release notes).
76 This is accomplished by conditional compilation using macros and functions
77 defined in the Xtensa HAL (hardware adaptation layer) for your configuration.
78 Only the relevant parts of this file will be included in your RTOS build.
79 For example, this file provides interrupt vector templates for all types and
80 all priority levels, but only the ones in your configuration are built.
82 NOTES on the use of 'call0' for long jumps instead of 'j':
83 1. This file should be assembled with the -mlongcalls option to xt-xcc.
84 2. The -mlongcalls compiler option causes 'call0 dest' to be expanded to
85 a sequence 'l32r a0, dest' 'callx0 a0' which works regardless of the
86 distance from the call to the destination. The linker then relaxes
87 it back to 'call0 dest' if it determines that dest is within range.
88 This allows more flexibility in locating code without the performance
89 overhead of the 'l32r' literal data load in cases where the destination
90 is in range of 'call0'. There is an additional benefit in that 'call0'
91 has a longer range than 'j' due to the target being word-aligned, so
92 the 'l32r' sequence is less likely needed.
93 3. The use of 'call0' with -mlongcalls requires that register a0 not be
94 live at the time of the call, which is always the case for a function
95 call but needs to be ensured if 'call0' is used as a jump in lieu of 'j'.
96 4. This use of 'call0' is independent of the C function call ABI.
100 #include "xtensa_rtos.h"
103 /* Enable stack backtrace across exception/interrupt - see below */
104 #define XT_DEBUG_BACKTRACE 1
108 --------------------------------------------------------------------------------
109 Defines used to access _xtos_interrupt_table.
110 --------------------------------------------------------------------------------
112 #define XIE_HANDLER 0
117 --------------------------------------------------------------------------------
118 Macro extract_msb - return the input with only the highest bit set.
120 Input : "ain" - Input value, clobbered.
121 Output : "aout" - Output value, has only one bit set, MSB of "ain".
122 The two arguments must be different AR registers.
123 --------------------------------------------------------------------------------
126 .macro extract_msb aout ain
128 addi \aout, \ain, -1 /* aout = ain - 1 */
129 and \ain, \ain, \aout /* ain = ain & aout */
130 bnez \ain, 1b /* repeat until ain == 0 */
131 addi \aout, \aout, 1 /* return aout + 1 */
135 --------------------------------------------------------------------------------
136 Macro dispatch_c_isr - dispatch interrupts to user ISRs.
137 This will dispatch to user handlers (if any) that are registered in the
138 XTOS dispatch table (_xtos_interrupt_table). These handlers would have
139 been registered by calling _xtos_set_interrupt_handler(). There is one
140 exception - the timer interrupt used by the OS will not be dispatched
141 to a user handler - this must be handled by the caller of this macro.
143 Level triggered and software interrupts are automatically deasserted by
147 -- PS.INTLEVEL is set to "level" at entry
148 -- PS.EXCM = 0, C calling enabled
150 NOTE: For CALL0 ABI, a12-a15 have not yet been saved.
152 NOTE: This macro will use registers a0 and a2-a6. The arguments are:
153 level -- interrupt level
154 mask -- interrupt bitmask for this level
155 --------------------------------------------------------------------------------
158 .macro dispatch_c_isr level mask
160 /* Get mask of pending, enabled interrupts at this level into a2. */
162 .L_xt_user_int_&level&:
168 beqz a2, 9f /* nothing to do */
170 /* This bit of code provides a nice debug backtrace in the debugger.
171 It does take a few more instructions, so undef XT_DEBUG_BACKTRACE
172 if you want to save the cycles.
174 #if XT_DEBUG_BACKTRACE
175 #ifndef __XTENSA_CALL0_ABI__
176 rsr a0, EPC_1 + \level - 1 /* return address */
177 movi a4, 0xC0000000 /* constant with top 2 bits set (call size) */
178 or a0, a0, a4 /* set top 2 bits */
179 addx2 a0, a4, a0 /* clear top bit -- simulating call4 size */
183 #ifdef XT_INTEXC_HOOKS
184 /* Call interrupt hook if present to (pre)handle interrupts. */
185 movi a4, _xt_intexc_hooks
186 l32i a4, a4, \level << 2
188 #ifdef __XTENSA_CALL0_ABI__
200 /* Now look up in the dispatch table and call user ISR if any. */
201 /* If multiple bits are set then MSB has highest priority. */
203 extract_msb a4, a2 /* a4 = MSB of a2, a2 trashed */
206 /* Enable all interrupts at this level that are numerically higher
207 than the one we just selected, since they are treated as higher
210 movi a3, \mask /* a3 = all interrupts at this level */
211 add a2, a4, a4 /* a2 = a4 << 1 */
212 addi a2, a2, -1 /* a2 = mask of 1's <= a4 bit */
213 and a2, a2, a3 /* a2 = mask of all bits <= a4 at this level */
215 l32i a6, a3, 4 /* a6 = _xt_vpri_mask */
217 addi a2, a2, -1 /* a2 = mask to apply */
218 and a5, a6, a2 /* mask off all bits <= a4 bit */
219 s32i a5, a3, 4 /* update _xt_vpri_mask */
221 and a3, a3, a2 /* mask off all bits <= a4 bit */
223 rsil a3, \level - 1 /* lower interrupt level by 1 */
226 movi a3, XT_TIMER_INTEN /* a3 = timer interrupt bit */
227 wsr a4, INTCLEAR /* clear sw or edge-triggered interrupt */
228 beq a3, a4, 7f /* if timer interrupt then skip table */
230 find_ms_setbit a3, a4, a3, 0 /* a3 = interrupt number */
232 movi a4, _xt_interrupt_table
233 addx8 a3, a3, a4 /* a3 = address of interrupt table entry */
234 l32i a4, a3, XIE_HANDLER /* a4 = handler address */
235 #ifdef __XTENSA_CALL0_ABI__
236 mov a12, a6 /* save in callee-saved reg */
237 l32i a2, a3, XIE_ARG /* a2 = handler arg */
238 callx0 a4 /* call handler */
241 mov a2, a6 /* save in windowed reg */
242 l32i a6, a3, XIE_ARG /* a6 = handler arg */
243 callx4 a4 /* call handler */
249 j .L_xt_user_int_&level& /* check for more interrupts */
254 .ifeq XT_TIMER_INTPRI - \level
255 .L_xt_user_int_timer_&level&:
257 Interrupt handler for the RTOS tick timer if at this level.
258 We'll be reading the interrupt state again after this call
259 so no need to preserve any registers except a6 (vpri_mask).
262 #ifdef __XTENSA_CALL0_ABI__
264 call0 XT_RTOS_TIMER_INT
268 call4 XT_RTOS_TIMER_INT
275 j .L_xt_user_int_&level& /* check for more interrupts */
280 /* Restore old value of _xt_vpri_mask from a2. Also update INTENABLE from
281 virtual _xt_intenable which _could_ have changed during interrupt
285 l32i a4, a3, 0 /* a4 = _xt_intenable */
286 s32i a2, a3, 4 /* update _xt_vpri_mask */
287 and a4, a4, a2 /* a4 = masked intenable */
288 wsr a4, INTENABLE /* update INTENABLE */
298 --------------------------------------------------------------------------------
300 Should be reached by call0 (preferable) or jump only. If call0, a0 says where
301 from. If on simulator, display panic message and abort, else loop indefinitely.
302 --------------------------------------------------------------------------------
307 .type _xt_panic,@function
313 addi a4, a0, -3 /* point to call0 */
314 movi a3, _xt_panic_message
317 movi a2, SYS_gdb_abort
320 rsil a2, XCHAL_EXCM_LEVEL /* disable all low & med ints */
321 1: j 1b /* loop infinitely */
324 .section .rodata, "a"
328 .string "\n*** _xt_panic() was called from 0x%08x or jumped to. ***\n"
332 --------------------------------------------------------------------------------
333 Hooks to dynamically install handlers for exceptions and interrupts.
334 Allows automated regression frameworks to install handlers per test.
335 Consists of an array of function pointers indexed by interrupt level,
336 with index 0 containing the entry for user exceptions.
337 Initialized with all 0s, meaning no handler is installed at each level.
338 See comment in xtensa_rtos.h for more details.
339 --------------------------------------------------------------------------------
342 #ifdef XT_INTEXC_HOOKS
344 .global _xt_intexc_hooks
345 .type _xt_intexc_hooks,@object
349 .fill XT_INTEXC_HOOK_NUM, 4, 0
354 --------------------------------------------------------------------------------
355 EXCEPTION AND LEVEL 1 INTERRUPT VECTORS AND LOW LEVEL HANDLERS
356 (except window exception vectors).
358 Each vector goes at a predetermined location according to the Xtensa
359 hardware configuration, which is ensured by its placement in a special
360 section known to the Xtensa linker support package (LSP). It performs
361 the minimum necessary before jumping to the handler in the .text section.
363 The corresponding handler goes in the normal .text section. It sets up
364 the appropriate stack frame, saves a few vector-specific registers and
365 calls XT_RTOS_INT_ENTER to save the rest of the interrupted context
366 and enter the RTOS, then sets up a C environment. It then calls the
367 user's interrupt handler code (which may be coded in C) and finally
368 calls XT_RTOS_INT_EXIT to transfer control to the RTOS for scheduling.
370 While XT_RTOS_INT_EXIT does not return directly to the interruptee,
371 eventually the RTOS scheduler will want to dispatch the interrupted
372 task or handler. The scheduler will return to the exit point that was
373 saved in the interrupt stack frame at XT_STK_EXIT.
374 --------------------------------------------------------------------------------
379 --------------------------------------------------------------------------------
381 --------------------------------------------------------------------------------
386 .begin literal_prefix .DebugExceptionVector
387 .section .DebugExceptionVector.text, "ax"
388 .global _DebugExceptionVector
392 _DebugExceptionVector:
396 In the simulator, let the debugger (if any) handle the debug exception,
397 or simply stop the simulation:
399 wsr a2, EXCSAVE+XCHAL_DEBUGLEVEL /* save a2 where sim expects it */
400 movi a2, SYS_gdb_enter_sktloop
401 simcall /* have ISS handle debug exc. */
402 #elif 0 /* change condition to 1 to use the HAL minimal debug handler */
403 wsr a3, EXCSAVE+XCHAL_DEBUGLEVEL
404 movi a3, xthal_debugexc_defhndlr_nw /* use default debug handler */
407 wsr a0, EXCSAVE+XCHAL_DEBUGLEVEL /* save original a0 somewhere */
408 call0 _xt_panic /* does not return */
409 rfi XCHAL_DEBUGLEVEL /* make a0 point here not later */
417 --------------------------------------------------------------------------------
419 Double exceptions are not a normal occurrence. They indicate a bug of some kind.
420 --------------------------------------------------------------------------------
423 #ifdef XCHAL_DOUBLEEXC_VECTOR_VADDR
425 .begin literal_prefix .DoubleExceptionVector
426 .section .DoubleExceptionVector.text, "ax"
427 .global _DoubleExceptionVector
431 _DoubleExceptionVector:
434 break 1, 4 /* unhandled double exception */
436 call0 _xt_panic /* does not return */
437 rfde /* make a0 point here not later */
441 #endif /* XCHAL_DOUBLEEXC_VECTOR_VADDR */
444 --------------------------------------------------------------------------------
445 Kernel Exception (including Level 1 Interrupt from kernel mode).
446 --------------------------------------------------------------------------------
449 .begin literal_prefix .KernelExceptionVector
450 .section .KernelExceptionVector.text, "ax"
451 .global _KernelExceptionVector
455 _KernelExceptionVector:
457 wsr a0, EXCSAVE_1 /* preserve a0 */
458 call0 _xt_kernel_exc /* kernel exception handler */
459 /* never returns here - call0 is used as a jump (see note at top) */
468 break 1, 0 /* unhandled kernel exception */
470 call0 _xt_panic /* does not return */
471 rfe /* make a0 point here not there */
475 --------------------------------------------------------------------------------
476 User Exception (including Level 1 Interrupt from user mode).
477 --------------------------------------------------------------------------------
480 .begin literal_prefix .UserExceptionVector
481 .section .UserExceptionVector.text, "ax"
482 .global _UserExceptionVector
483 .type _UserExceptionVector,@function
487 _UserExceptionVector:
489 wsr a0, EXCSAVE_1 /* preserve a0 */
490 call0 _xt_user_exc /* user exception handler */
491 /* never returns here - call0 is used as a jump (see note at top) */
496 --------------------------------------------------------------------------------
497 Insert some waypoints for jumping beyond the signed 8-bit range of
498 conditional branch instructions, so the conditional branchces to specific
499 exception handlers are not taken in the mainline. Saves some cycles in the
501 --------------------------------------------------------------------------------
506 #if XCHAL_HAVE_WINDOWED
509 call0 _xt_alloca_exc /* in window vectors section */
510 /* never returns here - call0 is used as a jump (see note at top) */
515 call0 _xt_syscall_exc
516 /* never returns here - call0 is used as a jump (see note at top) */
522 /* never returns here - call0 is used as a jump (see note at top) */
527 --------------------------------------------------------------------------------
528 User exception handler.
529 --------------------------------------------------------------------------------
532 .type _xt_user_exc,@function
537 /* If level 1 interrupt then jump to the dispatcher */
539 beqi a0, EXCCAUSE_LEVEL1INTERRUPT, _xt_lowint1
541 /* Handle any coprocessor exceptions. Rely on the fact that exception
542 numbers above EXCCAUSE_CP0_DISABLED all relate to the coprocessors.
545 bgeui a0, EXCCAUSE_CP0_DISABLED, _xt_to_coproc_exc
548 /* Handle alloca and syscall exceptions */
549 #if XCHAL_HAVE_WINDOWED
550 beqi a0, EXCCAUSE_ALLOCA, _xt_to_alloca_exc
552 beqi a0, EXCCAUSE_SYSCALL, _xt_to_syscall_exc
554 /* Handle all other exceptions. All can have user-defined handlers. */
555 /* NOTE: we'll stay on the user stack for exception handling. */
557 /* Allocate exception frame and save minimal context. */
559 addi sp, sp, -XT_STK_FRMSZ
560 s32i a0, sp, XT_STK_A1
561 #if XCHAL_HAVE_WINDOWED
562 s32e a0, sp, -12 /* for debug backtrace */
564 rsr a0, PS /* save interruptee's PS */
565 s32i a0, sp, XT_STK_PS
566 rsr a0, EPC_1 /* save interruptee's PC */
567 s32i a0, sp, XT_STK_PC
568 rsr a0, EXCSAVE_1 /* save interruptee's a0 */
569 s32i a0, sp, XT_STK_A0
570 #if XCHAL_HAVE_WINDOWED
571 s32e a0, sp, -16 /* for debug backtrace */
573 s32i a12, sp, XT_STK_A12 /* _xt_context_save requires A12- */
574 s32i a13, sp, XT_STK_A13 /* A13 to have already been saved */
575 call0 _xt_context_save
577 /* Save exc cause and vaddr into exception frame */
579 s32i a0, sp, XT_STK_EXCCAUSE
581 s32i a0, sp, XT_STK_EXCVADDR
583 /* Set up PS for C, reenable hi-pri interrupts, and clear EXCM. */
584 #ifdef __XTENSA_CALL0_ABI__
585 movi a0, PS_INTLEVEL(XCHAL_EXCM_LEVEL) | PS_UM
587 movi a0, PS_INTLEVEL(XCHAL_EXCM_LEVEL) | PS_UM | PS_WOE
591 #ifdef XT_DEBUG_BACKTRACE
592 #ifndef __XTENSA_CALL0_ABI__
593 rsr a0, EPC_1 /* return address for debug backtrace */
594 movi a5, 0xC0000000 /* constant with top 2 bits set (call size) */
595 rsync /* wait for WSR.PS to complete */
596 or a0, a0, a5 /* set top 2 bits */
597 addx2 a0, a5, a0 /* clear top bit -- thus simulating call4 size */
599 rsync /* wait for WSR.PS to complete */
603 rsr a2, EXCCAUSE /* recover exc cause */
605 #ifdef XT_INTEXC_HOOKS
607 Call exception hook to pre-handle exceptions (if installed).
608 Pass EXCCAUSE in a2, and check result in a2 (if -1, skip default handling).
610 movi a4, _xt_intexc_hooks
611 l32i a4, a4, 0 /* user exception hook index 0 */
613 .Ln_xt_user_exc_call_hook:
614 #ifdef __XTENSA_CALL0_ABI__
616 beqi a2, -1, .L_xt_user_done
620 beqi a6, -1, .L_xt_user_done
626 rsr a2, EXCCAUSE /* recover exc cause */
627 movi a3, _xt_exception_table
628 addx4 a4, a2, a3 /* a4 = address of exception table entry */
629 l32i a4, a4, 0 /* a4 = handler address */
630 #ifdef __XTENSA_CALL0_ABI__
631 mov a2, sp /* a2 = pointer to exc frame */
632 callx0 a4 /* call handler */
634 mov a6, sp /* a6 = pointer to exc frame */
635 callx4 a4 /* call handler */
640 /* Restore context and return */
641 call0 _xt_context_restore
642 l32i a0, sp, XT_STK_PS /* retrieve interruptee's PS */
644 l32i a0, sp, XT_STK_PC /* retrieve interruptee's PC */
646 l32i a0, sp, XT_STK_A0 /* retrieve interruptee's A0 */
647 l32i sp, sp, XT_STK_A1 /* remove exception frame */
648 rsync /* ensure PS and EPC written */
649 rfe /* PS.EXCM is cleared */
653 --------------------------------------------------------------------------------
654 Exit point for dispatch. Saved in interrupt stack frame at XT_STK_EXIT
655 on entry and used to return to a thread or interrupted interrupt handler.
656 --------------------------------------------------------------------------------
659 .global _xt_user_exit
660 .type _xt_user_exit,@function
663 l32i a0, sp, XT_STK_PS /* retrieve interruptee's PS */
665 l32i a0, sp, XT_STK_PC /* retrieve interruptee's PC */
667 l32i a0, sp, XT_STK_A0 /* retrieve interruptee's A0 */
668 l32i sp, sp, XT_STK_A1 /* remove interrupt stack frame */
669 rsync /* ensure PS and EPC written */
670 rfe /* PS.EXCM is cleared */
674 --------------------------------------------------------------------------------
675 Syscall Exception Handler (jumped to from User Exception Handler).
676 Syscall 0 is required to spill the register windows (no-op in Call 0 ABI).
677 Only syscall 0 is handled here. Other syscalls return -1 to caller in a2.
678 --------------------------------------------------------------------------------
682 .type _xt_syscall_exc,@function
686 #ifdef __XTENSA_CALL0_ABI__
688 Save minimal regs for scratch. Syscall 0 does nothing in Call0 ABI.
689 Use a minimal stack frame (16B) to save A2 & A3 for scratch.
690 PS.EXCM could be cleared here, but unlikely to improve worst-case latency.
692 addi a0, a0, -PS_EXCM_MASK
698 #else /* Windowed ABI */
700 Save necessary context and spill the register windows.
701 PS.EXCM is still set and must remain set until after the spill.
702 Reuse context save function though it saves more than necessary.
703 For this reason, a full interrupt stack frame is allocated.
705 addi sp, sp, -XT_STK_FRMSZ /* allocate interrupt stack frame */
706 s32i a12, sp, XT_STK_A12 /* _xt_context_save requires A12- */
707 s32i a13, sp, XT_STK_A13 /* A13 to have already been saved */
708 call0 _xt_context_save
712 Grab the interruptee's PC and skip over the 'syscall' instruction.
713 If it's at the end of a zero-overhead loop and it's not on the last
714 iteration, decrement loop counter and skip to beginning of loop.
716 rsr a2, EPC_1 /* a2 = PC of 'syscall' */
717 addi a3, a2, 3 /* ++PC */
719 rsr a0, LEND /* if (PC == LEND */
721 rsr a0, LCOUNT /* && LCOUNT != 0) */
723 addi a0, a0, -1 /* --LCOUNT */
724 rsr a3, LBEG /* PC = LBEG */
725 wsr a0, LCOUNT /* } */
727 1: wsr a3, EPC_1 /* update PC */
729 /* Restore interruptee's context and return from exception. */
730 #ifdef __XTENSA_CALL0_ABI__
735 call0 _xt_context_restore
736 addi sp, sp, XT_STK_FRMSZ
739 movnez a2, a0, a2 /* return -1 if not syscall 0 */
744 --------------------------------------------------------------------------------
745 Co-Processor Exception Handler (jumped to from User Exception Handler).
746 These exceptions are generated by co-processor instructions, which are only
747 allowed in thread code (not in interrupts or kernel code). This restriction is
748 deliberately imposed to reduce the burden of state-save/restore in interrupts.
749 --------------------------------------------------------------------------------
753 .section .rodata, "a"
755 /* Offset to CP n save area in thread's CP save area. */
756 .global _xt_coproc_sa_offset
757 .type _xt_coproc_sa_offset,@object
758 .align 16 /* minimize crossing cache boundaries */
759 _xt_coproc_sa_offset:
760 .word XT_CP0_SA, XT_CP1_SA, XT_CP2_SA, XT_CP3_SA
761 .word XT_CP4_SA, XT_CP5_SA, XT_CP6_SA, XT_CP7_SA
763 /* Bitmask for CP n's CPENABLE bit. */
764 .type _xt_coproc_mask,@object
765 .align 16,,8 /* try to keep it all in one cache line */
769 .long (i<<16) | (1<<i) // upper 16-bits = i, lower = bitmask
775 /* Owner thread of CP n, identified by thread's CP save area (0 = unowned). */
776 .global _xt_coproc_owner_sa
777 .type _xt_coproc_owner_sa,@object
778 .align 16,,XCHAL_CP_MAX<<2 /* minimize crossing cache boundaries */
780 .space XCHAL_CP_MAX << 2
787 j .L_xt_coproc_invalid /* not in a thread (invalid) */
794 --------------------------------------------------------------------------------
795 Coprocessor exception handler.
796 At entry, only a0 has been saved (in EXCSAVE_1).
797 --------------------------------------------------------------------------------
800 .type _xt_coproc_exc,@function
805 /* Allocate interrupt stack frame and save minimal context. */
806 mov a0, sp /* sp == a1 */
807 addi sp, sp, -XT_STK_FRMSZ /* allocate interrupt stack frame */
808 s32i a0, sp, XT_STK_A1 /* save pre-interrupt SP */
809 #if XCHAL_HAVE_WINDOWED
810 s32e a0, sp, -12 /* for debug backtrace */
812 rsr a0, PS /* save interruptee's PS */
813 s32i a0, sp, XT_STK_PS
814 rsr a0, EPC_1 /* save interruptee's PC */
815 s32i a0, sp, XT_STK_PC
816 rsr a0, EXCSAVE_1 /* save interruptee's a0 */
817 s32i a0, sp, XT_STK_A0
818 #if XCHAL_HAVE_WINDOWED
819 s32e a0, sp, -16 /* for debug backtrace */
821 movi a0, _xt_user_exit /* save exit point for dispatch */
822 s32i a0, sp, XT_STK_EXIT
825 s32i a5, sp, XT_STK_A5 /* save a5 */
826 addi a5, a0, -EXCCAUSE_CP0_DISABLED /* a5 = CP index */
828 /* Save a few more of interruptee's registers (a5 was already saved). */
829 s32i a2, sp, XT_STK_A2
830 s32i a3, sp, XT_STK_A3
831 s32i a4, sp, XT_STK_A4
832 s32i a15, sp, XT_STK_A15
834 /* Get co-processor state save area of new owner thread. */
835 call0 XT_RTOS_CP_STATE /* a15 = new owner's save area */
836 beqz a15, .L_goto_invalid /* not in a thread (invalid) */
838 /* Enable the co-processor's bit in CPENABLE. */
839 movi a0, _xt_coproc_mask
840 rsr a4, CPENABLE /* a4 = CPENABLE */
841 addx4 a0, a5, a0 /* a0 = &_xt_coproc_mask[n] */
842 l32i a0, a0, 0 /* a0 = (n << 16) | (1 << n) */
843 movi a3, _xt_coproc_owner_sa /* (placed here for load slot) */
844 extui a2, a0, 0, 16 /* coprocessor bitmask portion */
845 or a4, a4, a2 /* a4 = CPENABLE | (1 << n) */
848 /* Get old coprocessor owner thread (save area ptr) and assign new one. */
849 addx4 a3, a5, a3 /* a3 = &_xt_coproc_owner_sa[n] */
850 l32i a2, a3, 0 /* a2 = old owner's save area */
851 s32i a15, a3, 0 /* _xt_coproc_owner_sa[n] = new */
852 rsync /* ensure wsr.CPENABLE is complete */
854 /* Only need to context switch if new owner != old owner. */
855 beq a15, a2, .L_goto_done /* new owner == old, we're done */
857 /* If no old owner then nothing to save. */
858 beqz a2, .L_check_new
860 /* If old owner not actively using CP then nothing to save. */
861 l16ui a4, a2, XT_CPENABLE /* a4 = old owner's CPENABLE */
862 bnone a4, a0, .L_check_new /* old owner not using CP */
865 /* Save old owner's coprocessor state. */
867 movi a5, _xt_coproc_sa_offset
869 /* Mark old owner state as no longer active (CPENABLE bit n clear). */
870 xor a4, a4, a0 /* clear CP bit in CPENABLE */
871 s16i a4, a2, XT_CPENABLE /* update old owner's CPENABLE */
873 extui a4, a0, 16, 5 /* a4 = CP index = n */
874 addx4 a5, a4, a5 /* a5 = &_xt_coproc_sa_offset[n] */
876 /* Mark old owner state as saved (CPSTORED bit n set). */
877 l16ui a4, a2, XT_CPSTORED /* a4 = old owner's CPSTORED */
878 l32i a5, a5, 0 /* a5 = XT_CP[n]_SA offset */
879 or a4, a4, a0 /* set CP in old owner's CPSTORED */
880 s16i a4, a2, XT_CPSTORED /* update old owner's CPSTORED */
881 l32i a2, a2, XT_CP_ASA /* ptr to actual (aligned) save area */
882 extui a3, a0, 16, 5 /* a3 = CP index = n */
883 add a2, a2, a5 /* a2 = old owner's area for CP n */
886 The config-specific HAL macro invoked below destroys a2-5, preserves a0-1.
887 It is theoretically possible for Xtensa processor designers to write TIE
888 that causes more address registers to be affected, but it is generally
889 unlikely. If that ever happens, more registers needs to be saved/restored
890 around this macro invocation, and the value in a15 needs to be recomputed.
892 xchal_cpi_store_funcbody
895 /* Check if any state has to be restored for new owner. */
896 /* NOTE: a15 = new owner's save area, cannot be zero when we get here. */
898 l16ui a3, a15, XT_CPSTORED /* a3 = new owner's CPSTORED */
899 movi a4, _xt_coproc_sa_offset
900 bnone a3, a0, .L_check_cs /* full CP not saved, check callee-saved */
901 xor a3, a3, a0 /* CPSTORED bit is set, clear it */
902 s16i a3, a15, XT_CPSTORED /* update new owner's CPSTORED */
904 /* Adjust new owner's save area pointers to area for CP n. */
905 extui a3, a0, 16, 5 /* a3 = CP index = n */
906 addx4 a4, a3, a4 /* a4 = &_xt_coproc_sa_offset[n] */
907 l32i a4, a4, 0 /* a4 = XT_CP[n]_SA */
908 l32i a5, a15, XT_CP_ASA /* ptr to actual (aligned) save area */
909 add a2, a4, a5 /* a2 = new owner's area for CP */
912 The config-specific HAL macro invoked below destroys a2-5, preserves a0-1.
913 It is theoretically possible for Xtensa processor designers to write TIE
914 that causes more address registers to be affected, but it is generally
915 unlikely. If that ever happens, more registers needs to be saved/restored
916 around this macro invocation.
918 xchal_cpi_load_funcbody
920 /* Restore interruptee's saved registers. */
921 /* Can omit rsync for wsr.CPENABLE here because _xt_user_exit does it. */
923 l32i a15, sp, XT_STK_A15
924 l32i a5, sp, XT_STK_A5
925 l32i a4, sp, XT_STK_A4
926 l32i a3, sp, XT_STK_A3
927 l32i a2, sp, XT_STK_A2
928 call0 _xt_user_exit /* return via exit dispatcher */
929 /* Never returns here - call0 is used as a jump (see note at top) */
932 /* a0 = CP mask in low bits, a15 = new owner's save area */
933 l16ui a2, a15, XT_CP_CS_ST /* a2 = mask of CPs saved */
934 bnone a2, a0, .L_xt_coproc_done /* if no match then done */
935 and a2, a2, a0 /* a2 = which CPs to restore */
936 extui a2, a2, 0, 8 /* extract low 8 bits */
937 s32i a6, sp, XT_STK_A6 /* save extra needed regs */
938 s32i a7, sp, XT_STK_A7
939 s32i a13, sp, XT_STK_A13
940 s32i a14, sp, XT_STK_A14
941 call0 _xt_coproc_restorecs /* restore CP registers */
942 l32i a6, sp, XT_STK_A6 /* restore saved registers */
943 l32i a7, sp, XT_STK_A7
944 l32i a13, sp, XT_STK_A13
945 l32i a14, sp, XT_STK_A14
948 /* Co-processor exception occurred outside a thread (not supported). */
949 .L_xt_coproc_invalid:
951 break 1, 1 /* unhandled user exception */
953 call0 _xt_panic /* not in a thread (invalid) */
957 #endif /* XCHAL_CP_NUM */
961 -------------------------------------------------------------------------------
962 Level 1 interrupt dispatch. Assumes stack frame has not been allocated yet.
963 -------------------------------------------------------------------------------
967 .type _xt_lowint1,@function
971 mov a0, sp /* sp == a1 */
972 addi sp, sp, -XT_STK_FRMSZ /* allocate interrupt stack frame */
973 s32i a0, sp, XT_STK_A1 /* save pre-interrupt SP */
974 rsr a0, PS /* save interruptee's PS */
975 s32i a0, sp, XT_STK_PS
976 rsr a0, EPC_1 /* save interruptee's PC */
977 s32i a0, sp, XT_STK_PC
978 rsr a0, EXCSAVE_1 /* save interruptee's a0 */
979 s32i a0, sp, XT_STK_A0
980 movi a0, _xt_user_exit /* save exit point for dispatch */
981 s32i a0, sp, XT_STK_EXIT
983 /* Save rest of interrupt context and enter RTOS. */
984 call0 XT_RTOS_INT_ENTER /* common RTOS interrupt entry */
986 /* !! We are now on the RTOS system stack !! */
988 /* Set up PS for C, enable interrupts above this level and clear EXCM. */
989 #ifdef __XTENSA_CALL0_ABI__
990 movi a0, PS_INTLEVEL(1) | PS_UM
992 movi a0, PS_INTLEVEL(1) | PS_UM | PS_WOE
997 /* OK to call C code at this point, dispatch user ISRs */
999 dispatch_c_isr 1 XCHAL_INTLEVEL1_MASK
1001 /* Done handling interrupts, transfer control to OS */
1002 call0 XT_RTOS_INT_EXIT /* does not return directly here */
1006 -------------------------------------------------------------------------------
1007 MEDIUM PRIORITY (LEVEL 2+) INTERRUPT VECTORS AND LOW LEVEL HANDLERS.
1009 Medium priority interrupts are by definition those with priority greater
1010 than 1 and not greater than XCHAL_EXCM_LEVEL. These are disabled by
1011 setting PS.EXCM and therefore can easily support a C environment for
1012 handlers in C, and interact safely with an RTOS.
1014 Each vector goes at a predetermined location according to the Xtensa
1015 hardware configuration, which is ensured by its placement in a special
1016 section known to the Xtensa linker support package (LSP). It performs
1017 the minimum necessary before jumping to the handler in the .text section.
1019 The corresponding handler goes in the normal .text section. It sets up
1020 the appropriate stack frame, saves a few vector-specific registers and
1021 calls XT_RTOS_INT_ENTER to save the rest of the interrupted context
1022 and enter the RTOS, then sets up a C environment. It then calls the
1023 user's interrupt handler code (which may be coded in C) and finally
1024 calls XT_RTOS_INT_EXIT to transfer control to the RTOS for scheduling.
1026 While XT_RTOS_INT_EXIT does not return directly to the interruptee,
1027 eventually the RTOS scheduler will want to dispatch the interrupted
1028 task or handler. The scheduler will return to the exit point that was
1029 saved in the interrupt stack frame at XT_STK_EXIT.
1030 -------------------------------------------------------------------------------
1033 #if XCHAL_EXCM_LEVEL >= 2
1035 .begin literal_prefix .Level2InterruptVector
1036 .section .Level2InterruptVector.text, "ax"
1037 .global _Level2Vector
1038 .type _Level2Vector,@function
1043 wsr a0, EXCSAVE_2 /* preserve a0 */
1044 call0 _xt_medint2 /* load interrupt handler */
1045 /* never returns here - call0 is used as a jump (see note at top) */
1050 .type _xt_medint2,@function
1053 mov a0, sp /* sp == a1 */
1054 addi sp, sp, -XT_STK_FRMSZ /* allocate interrupt stack frame */
1055 s32i a0, sp, XT_STK_A1 /* save pre-interrupt SP */
1056 rsr a0, EPS_2 /* save interruptee's PS */
1057 s32i a0, sp, XT_STK_PS
1058 rsr a0, EPC_2 /* save interruptee's PC */
1059 s32i a0, sp, XT_STK_PC
1060 rsr a0, EXCSAVE_2 /* save interruptee's a0 */
1061 s32i a0, sp, XT_STK_A0
1062 movi a0, _xt_medint2_exit /* save exit point for dispatch */
1063 s32i a0, sp, XT_STK_EXIT
1065 /* Save rest of interrupt context and enter RTOS. */
1066 call0 XT_RTOS_INT_ENTER /* common RTOS interrupt entry */
1068 /* !! We are now on the RTOS system stack !! */
1070 /* Set up PS for C, enable interrupts above this level and clear EXCM. */
1071 #ifdef __XTENSA_CALL0_ABI__
1072 movi a0, PS_INTLEVEL(2) | PS_UM
1074 movi a0, PS_INTLEVEL(2) | PS_UM | PS_WOE
1079 /* OK to call C code at this point, dispatch user ISRs */
1081 dispatch_c_isr 2 XCHAL_INTLEVEL2_MASK
1083 /* Done handling interrupts, transfer control to OS */
1084 call0 XT_RTOS_INT_EXIT /* does not return directly here */
1087 Exit point for dispatch. Saved in interrupt stack frame at XT_STK_EXIT
1088 on entry and used to return to a thread or interrupted interrupt handler.
1090 .global _xt_medint2_exit
1091 .type _xt_medint2_exit,@function
1094 /* Restore only level-specific regs (the rest were already restored) */
1095 l32i a0, sp, XT_STK_PS /* retrieve interruptee's PS */
1097 l32i a0, sp, XT_STK_PC /* retrieve interruptee's PC */
1099 l32i a0, sp, XT_STK_A0 /* retrieve interruptee's A0 */
1100 l32i sp, sp, XT_STK_A1 /* remove interrupt stack frame */
1101 rsync /* ensure EPS and EPC written */
1104 #endif /* Level 2 */
1106 #if XCHAL_EXCM_LEVEL >= 3
1108 .begin literal_prefix .Level3InterruptVector
1109 .section .Level3InterruptVector.text, "ax"
1110 .global _Level3Vector
1111 .type _Level3Vector,@function
1116 wsr a0, EXCSAVE_3 /* preserve a0 */
1117 call0 _xt_medint3 /* load interrupt handler */
1118 /* never returns here - call0 is used as a jump (see note at top) */
1123 .type _xt_medint3,@function
1126 mov a0, sp /* sp == a1 */
1127 addi sp, sp, -XT_STK_FRMSZ /* allocate interrupt stack frame */
1128 s32i a0, sp, XT_STK_A1 /* save pre-interrupt SP */
1129 rsr a0, EPS_3 /* save interruptee's PS */
1130 s32i a0, sp, XT_STK_PS
1131 rsr a0, EPC_3 /* save interruptee's PC */
1132 s32i a0, sp, XT_STK_PC
1133 rsr a0, EXCSAVE_3 /* save interruptee's a0 */
1134 s32i a0, sp, XT_STK_A0
1135 movi a0, _xt_medint3_exit /* save exit point for dispatch */
1136 s32i a0, sp, XT_STK_EXIT
1138 /* Save rest of interrupt context and enter RTOS. */
1139 call0 XT_RTOS_INT_ENTER /* common RTOS interrupt entry */
1141 /* !! We are now on the RTOS system stack !! */
1143 /* Set up PS for C, enable interrupts above this level and clear EXCM. */
1144 #ifdef __XTENSA_CALL0_ABI__
1145 movi a0, PS_INTLEVEL(3) | PS_UM
1147 movi a0, PS_INTLEVEL(3) | PS_UM | PS_WOE
1152 /* OK to call C code at this point, dispatch user ISRs */
1154 dispatch_c_isr 3 XCHAL_INTLEVEL3_MASK
1156 /* Done handling interrupts, transfer control to OS */
1157 call0 XT_RTOS_INT_EXIT /* does not return directly here */
1160 Exit point for dispatch. Saved in interrupt stack frame at XT_STK_EXIT
1161 on entry and used to return to a thread or interrupted interrupt handler.
1163 .global _xt_medint3_exit
1164 .type _xt_medint3_exit,@function
1167 /* Restore only level-specific regs (the rest were already restored) */
1168 l32i a0, sp, XT_STK_PS /* retrieve interruptee's PS */
1170 l32i a0, sp, XT_STK_PC /* retrieve interruptee's PC */
1172 l32i a0, sp, XT_STK_A0 /* retrieve interruptee's A0 */
1173 l32i sp, sp, XT_STK_A1 /* remove interrupt stack frame */
1174 rsync /* ensure EPS and EPC written */
1177 #endif /* Level 3 */
1179 #if XCHAL_EXCM_LEVEL >= 4
1181 .begin literal_prefix .Level4InterruptVector
1182 .section .Level4InterruptVector.text, "ax"
1183 .global _Level4Vector
1184 .type _Level4Vector,@function
1189 wsr a0, EXCSAVE_4 /* preserve a0 */
1190 call0 _xt_medint4 /* load interrupt handler */
1195 .type _xt_medint4,@function
1198 mov a0, sp /* sp == a1 */
1199 addi sp, sp, -XT_STK_FRMSZ /* allocate interrupt stack frame */
1200 s32i a0, sp, XT_STK_A1 /* save pre-interrupt SP */
1201 rsr a0, EPS_4 /* save interruptee's PS */
1202 s32i a0, sp, XT_STK_PS
1203 rsr a0, EPC_4 /* save interruptee's PC */
1204 s32i a0, sp, XT_STK_PC
1205 rsr a0, EXCSAVE_4 /* save interruptee's a0 */
1206 s32i a0, sp, XT_STK_A0
1207 movi a0, _xt_medint4_exit /* save exit point for dispatch */
1208 s32i a0, sp, XT_STK_EXIT
1210 /* Save rest of interrupt context and enter RTOS. */
1211 call0 XT_RTOS_INT_ENTER /* common RTOS interrupt entry */
1213 /* !! We are now on the RTOS system stack !! */
1215 /* Set up PS for C, enable interrupts above this level and clear EXCM. */
1216 #ifdef __XTENSA_CALL0_ABI__
1217 movi a0, PS_INTLEVEL(4) | PS_UM
1219 movi a0, PS_INTLEVEL(4) | PS_UM | PS_WOE
1224 /* OK to call C code at this point, dispatch user ISRs */
1226 dispatch_c_isr 4 XCHAL_INTLEVEL4_MASK
1228 /* Done handling interrupts, transfer control to OS */
1229 call0 XT_RTOS_INT_EXIT /* does not return directly here */
1232 Exit point for dispatch. Saved in interrupt stack frame at XT_STK_EXIT
1233 on entry and used to return to a thread or interrupted interrupt handler.
1235 .global _xt_medint4_exit
1236 .type _xt_medint4_exit,@function
1239 /* Restore only level-specific regs (the rest were already restored) */
1240 l32i a0, sp, XT_STK_PS /* retrieve interruptee's PS */
1242 l32i a0, sp, XT_STK_PC /* retrieve interruptee's PC */
1244 l32i a0, sp, XT_STK_A0 /* retrieve interruptee's A0 */
1245 l32i sp, sp, XT_STK_A1 /* remove interrupt stack frame */
1246 rsync /* ensure EPS and EPC written */
1249 #endif /* Level 4 */
1251 #if XCHAL_EXCM_LEVEL >= 5
1253 .begin literal_prefix .Level5InterruptVector
1254 .section .Level5InterruptVector.text, "ax"
1255 .global _Level5Vector
1256 .type _Level5Vector,@function
1261 wsr a0, EXCSAVE_5 /* preserve a0 */
1262 call0 _xt_medint5 /* load interrupt handler */
1267 .type _xt_medint5,@function
1270 mov a0, sp /* sp == a1 */
1271 addi sp, sp, -XT_STK_FRMSZ /* allocate interrupt stack frame */
1272 s32i a0, sp, XT_STK_A1 /* save pre-interrupt SP */
1273 rsr a0, EPS_5 /* save interruptee's PS */
1274 s32i a0, sp, XT_STK_PS
1275 rsr a0, EPC_5 /* save interruptee's PC */
1276 s32i a0, sp, XT_STK_PC
1277 rsr a0, EXCSAVE_5 /* save interruptee's a0 */
1278 s32i a0, sp, XT_STK_A0
1279 movi a0, _xt_medint5_exit /* save exit point for dispatch */
1280 s32i a0, sp, XT_STK_EXIT
1282 /* Save rest of interrupt context and enter RTOS. */
1283 call0 XT_RTOS_INT_ENTER /* common RTOS interrupt entry */
1285 /* !! We are now on the RTOS system stack !! */
1287 /* Set up PS for C, enable interrupts above this level and clear EXCM. */
1288 #ifdef __XTENSA_CALL0_ABI__
1289 movi a0, PS_INTLEVEL(5) | PS_UM
1291 movi a0, PS_INTLEVEL(5) | PS_UM | PS_WOE
1296 /* OK to call C code at this point, dispatch user ISRs */
1298 dispatch_c_isr 5 XCHAL_INTLEVEL5_MASK
1300 /* Done handling interrupts, transfer control to OS */
1301 call0 XT_RTOS_INT_EXIT /* does not return directly here */
1304 Exit point for dispatch. Saved in interrupt stack frame at XT_STK_EXIT
1305 on entry and used to return to a thread or interrupted interrupt handler.
1307 .global _xt_medint5_exit
1308 .type _xt_medint5_exit,@function
1311 /* Restore only level-specific regs (the rest were already restored) */
1312 l32i a0, sp, XT_STK_PS /* retrieve interruptee's PS */
1314 l32i a0, sp, XT_STK_PC /* retrieve interruptee's PC */
1316 l32i a0, sp, XT_STK_A0 /* retrieve interruptee's A0 */
1317 l32i sp, sp, XT_STK_A1 /* remove interrupt stack frame */
1318 rsync /* ensure EPS and EPC written */
1321 #endif /* Level 5 */
1323 #if XCHAL_EXCM_LEVEL >= 6
1325 .begin literal_prefix .Level6InterruptVector
1326 .section .Level6InterruptVector.text, "ax"
1327 .global _Level6Vector
1328 .type _Level6Vector,@function
1333 wsr a0, EXCSAVE_6 /* preserve a0 */
1334 call0 _xt_medint6 /* load interrupt handler */
1339 .type _xt_medint6,@function
1342 mov a0, sp /* sp == a1 */
1343 addi sp, sp, -XT_STK_FRMSZ /* allocate interrupt stack frame */
1344 s32i a0, sp, XT_STK_A1 /* save pre-interrupt SP */
1345 rsr a0, EPS_6 /* save interruptee's PS */
1346 s32i a0, sp, XT_STK_PS
1347 rsr a0, EPC_6 /* save interruptee's PC */
1348 s32i a0, sp, XT_STK_PC
1349 rsr a0, EXCSAVE_6 /* save interruptee's a0 */
1350 s32i a0, sp, XT_STK_A0
1351 movi a0, _xt_medint6_exit /* save exit point for dispatch */
1352 s32i a0, sp, XT_STK_EXIT
1354 /* Save rest of interrupt context and enter RTOS. */
1355 call0 XT_RTOS_INT_ENTER /* common RTOS interrupt entry */
1357 /* !! We are now on the RTOS system stack !! */
1359 /* Set up PS for C, enable interrupts above this level and clear EXCM. */
1360 #ifdef __XTENSA_CALL0_ABI__
1361 movi a0, PS_INTLEVEL(6) | PS_UM
1363 movi a0, PS_INTLEVEL(6) | PS_UM | PS_WOE
1368 /* OK to call C code at this point, dispatch user ISRs */
1370 dispatch_c_isr 6 XCHAL_INTLEVEL6_MASK
1372 /* Done handling interrupts, transfer control to OS */
1373 call0 XT_RTOS_INT_EXIT /* does not return directly here */
1376 Exit point for dispatch. Saved in interrupt stack frame at XT_STK_EXIT
1377 on entry and used to return to a thread or interrupted interrupt handler.
1379 .global _xt_medint6_exit
1380 .type _xt_medint6_exit,@function
1383 /* Restore only level-specific regs (the rest were already restored) */
1384 l32i a0, sp, XT_STK_PS /* retrieve interruptee's PS */
1386 l32i a0, sp, XT_STK_PC /* retrieve interruptee's PC */
1388 l32i a0, sp, XT_STK_A0 /* retrieve interruptee's A0 */
1389 l32i sp, sp, XT_STK_A1 /* remove interrupt stack frame */
1390 rsync /* ensure EPS and EPC written */
1393 #endif /* Level 6 */
1396 /*******************************************************************************
1398 HIGH PRIORITY (LEVEL > XCHAL_EXCM_LEVEL) INTERRUPT VECTORS AND HANDLERS
1400 High priority interrupts are by definition those with priorities greater
1401 than XCHAL_EXCM_LEVEL. This includes non-maskable (NMI). High priority
1402 interrupts cannot interact with the RTOS, that is they must save all regs
1403 they use and not call any RTOS function.
1405 A further restriction imposed by the Xtensa windowed architecture is that
1406 high priority interrupts must not modify the stack area even logically
1407 "above" the top of the interrupted stack (they need to provide their
1408 own stack or static save area).
1410 Cadence Design Systems recommends high priority interrupt handlers be coded in assembly
1411 and used for purposes requiring very short service times.
1413 Here are templates for high priority (level 2+) interrupt vectors.
1414 They assume only one interrupt per level to avoid the burden of identifying
1415 which interrupts at this level are pending and enabled. This allows for
1416 minimum latency and avoids having to save/restore a2 in addition to a0.
1417 If more than one interrupt per high priority level is configured, this burden
1418 is on the handler which in any case must provide a way to save and restore
1419 registers it uses without touching the interrupted stack.
1421 Each vector goes at a predetermined location according to the Xtensa
1422 hardware configuration, which is ensured by its placement in a special
1423 section known to the Xtensa linker support package (LSP). It performs
1424 the minimum necessary before jumping to the handler in the .text section.
1426 *******************************************************************************/
1429 Currently only shells for high priority interrupt handlers are provided
1430 here. However a template and example can be found in the Cadence Design Systems tools
1431 documentation: "Microprocessor Programmer's Guide".
1434 #if XCHAL_NUM_INTLEVELS >=2 && XCHAL_EXCM_LEVEL <2 && XCHAL_DEBUGLEVEL !=2
1436 .begin literal_prefix .Level2InterruptVector
1437 .section .Level2InterruptVector.text, "ax"
1438 .global _Level2Vector
1439 .type _Level2Vector,@function
1442 wsr a0, EXCSAVE_2 /* preserve a0 */
1443 call0 _xt_highint2 /* load interrupt handler */
1448 .type _xt_highint2,@function
1452 #ifdef XT_INTEXC_HOOKS
1453 /* Call interrupt hook if present to (pre)handle interrupts. */
1454 movi a0, _xt_intexc_hooks
1457 .Ln_xt_highint2_call_hook:
1458 callx0 a0 /* must NOT disturb stack! */
1463 ADD HIGH PRIORITY LEVEL 2 INTERRUPT HANDLER CODE HERE.
1467 .L_xt_highint2_exit:
1468 rsr a0, EXCSAVE_2 /* restore a0 */
1471 #endif /* Level 2 */
1473 #if XCHAL_NUM_INTLEVELS >=3 && XCHAL_EXCM_LEVEL <3 && XCHAL_DEBUGLEVEL !=3
1475 .begin literal_prefix .Level3InterruptVector
1476 .section .Level3InterruptVector.text, "ax"
1477 .global _Level3Vector
1478 .type _Level3Vector,@function
1481 wsr a0, EXCSAVE_3 /* preserve a0 */
1482 call0 _xt_highint3 /* load interrupt handler */
1483 /* never returns here - call0 is used as a jump (see note at top) */
1488 .type _xt_highint3,@function
1492 #ifdef XT_INTEXC_HOOKS
1493 /* Call interrupt hook if present to (pre)handle interrupts. */
1494 movi a0, _xt_intexc_hooks
1497 .Ln_xt_highint3_call_hook:
1498 callx0 a0 /* must NOT disturb stack! */
1503 ADD HIGH PRIORITY LEVEL 3 INTERRUPT HANDLER CODE HERE.
1507 .L_xt_highint3_exit:
1508 rsr a0, EXCSAVE_3 /* restore a0 */
1511 #endif /* Level 3 */
1513 #if XCHAL_NUM_INTLEVELS >=4 && XCHAL_EXCM_LEVEL <4 && XCHAL_DEBUGLEVEL !=4
1515 .begin literal_prefix .Level4InterruptVector
1516 .section .Level4InterruptVector.text, "ax"
1517 .global _Level4Vector
1518 .type _Level4Vector,@function
1521 wsr a0, EXCSAVE_4 /* preserve a0 */
1522 call0 _xt_highint4 /* load interrupt handler */
1523 /* never returns here - call0 is used as a jump (see note at top) */
1528 .type _xt_highint4,@function
1532 #ifdef XT_INTEXC_HOOKS
1533 /* Call interrupt hook if present to (pre)handle interrupts. */
1534 movi a0, _xt_intexc_hooks
1537 .Ln_xt_highint4_call_hook:
1538 callx0 a0 /* must NOT disturb stack! */
1543 ADD HIGH PRIORITY LEVEL 4 INTERRUPT HANDLER CODE HERE.
1547 .L_xt_highint4_exit:
1548 rsr a0, EXCSAVE_4 /* restore a0 */
1551 #endif /* Level 4 */
1553 #if XCHAL_NUM_INTLEVELS >=5 && XCHAL_EXCM_LEVEL <5 && XCHAL_DEBUGLEVEL !=5
1555 .begin literal_prefix .Level5InterruptVector
1556 .section .Level5InterruptVector.text, "ax"
1557 .global _Level5Vector
1558 .type _Level5Vector,@function
1561 wsr a0, EXCSAVE_5 /* preserve a0 */
1562 call0 _xt_highint5 /* load interrupt handler */
1563 /* never returns here - call0 is used as a jump (see note at top) */
1568 .type _xt_highint5,@function
1572 #ifdef XT_INTEXC_HOOKS
1573 /* Call interrupt hook if present to (pre)handle interrupts. */
1574 movi a0, _xt_intexc_hooks
1577 .Ln_xt_highint5_call_hook:
1578 callx0 a0 /* must NOT disturb stack! */
1583 ADD HIGH PRIORITY LEVEL 5 INTERRUPT HANDLER CODE HERE.
1587 .L_xt_highint5_exit:
1588 rsr a0, EXCSAVE_5 /* restore a0 */
1591 #endif /* Level 5 */
1593 #if XCHAL_NUM_INTLEVELS >=6 && XCHAL_EXCM_LEVEL <6 && XCHAL_DEBUGLEVEL !=6
1595 .begin literal_prefix .Level6InterruptVector
1596 .section .Level6InterruptVector.text, "ax"
1597 .global _Level6Vector
1598 .type _Level6Vector,@function
1601 wsr a0, EXCSAVE_6 /* preserve a0 */
1602 call0 _xt_highint6 /* load interrupt handler */
1603 /* never returns here - call0 is used as a jump (see note at top) */
1608 .type _xt_highint6,@function
1612 #ifdef XT_INTEXC_HOOKS
1613 /* Call interrupt hook if present to (pre)handle interrupts. */
1614 movi a0, _xt_intexc_hooks
1617 .Ln_xt_highint6_call_hook:
1618 callx0 a0 /* must NOT disturb stack! */
1623 ADD HIGH PRIORITY LEVEL 6 INTERRUPT HANDLER CODE HERE.
1627 .L_xt_highint6_exit:
1628 rsr a0, EXCSAVE_6 /* restore a0 */
1631 #endif /* Level 6 */
1635 .begin literal_prefix .NMIExceptionVector
1636 .section .NMIExceptionVector.text, "ax"
1637 .global _NMIExceptionVector
1638 .type _NMIExceptionVector,@function
1640 _NMIExceptionVector:
1641 wsr a0, EXCSAVE + XCHAL_NMILEVEL _ /* preserve a0 */
1642 call0 _xt_nmi /* load interrupt handler */
1643 /* never returns here - call0 is used as a jump (see note at top) */
1648 .type _xt_nmi,@function
1652 #ifdef XT_INTEXC_HOOKS
1653 /* Call interrupt hook if present to (pre)handle interrupts. */
1654 movi a0, _xt_intexc_hooks
1655 l32i a0, a0, XCHAL_NMILEVEL<<2
1657 .Ln_xt_nmi_call_hook:
1658 callx0 a0 /* must NOT disturb stack! */
1663 ADD HIGH PRIORITY NON-MASKABLE INTERRUPT (NMI) HANDLER CODE HERE.
1668 rsr a0, EXCSAVE + XCHAL_NMILEVEL /* restore a0 */
1674 /*******************************************************************************
1676 WINDOW OVERFLOW AND UNDERFLOW EXCEPTION VECTORS AND ALLOCA EXCEPTION HANDLER
1678 Here is the code for each window overflow/underflow exception vector and
1679 (interspersed) efficient code for handling the alloca exception cause.
1680 Window exceptions are handled entirely in the vector area and are very
1681 tight for performance. The alloca exception is also handled entirely in
1682 the window vector area so comes at essentially no cost in code size.
1683 Users should never need to modify them and Cadence Design Systems recommends
1686 Window handlers go at predetermined vector locations according to the
1687 Xtensa hardware configuration, which is ensured by their placement in a
1688 special section known to the Xtensa linker support package (LSP). Since
1689 their offsets in that section are always the same, the LSPs do not define
1690 a section per vector.
1692 These things are coded for XEA2 only (XEA1 is not supported).
1694 Note on Underflow Handlers:
1695 The underflow handler for returning from call[i+1] to call[i]
1696 must preserve all the registers from call[i+1]'s window.
1697 In particular, a0 and a1 must be preserved because the RETW instruction
1698 will be reexecuted (and may even underflow if an intervening exception
1699 has flushed call[i]'s registers).
1700 Registers a2 and up may contain return values.
1702 *******************************************************************************/
1704 #if XCHAL_HAVE_WINDOWED
1706 .section .WindowVectors.text, "ax"
1709 --------------------------------------------------------------------------------
1710 Window Overflow Exception for Call4.
1712 Invoked if a call[i] referenced a register (a4-a15)
1713 that contains data from ancestor call[j];
1714 call[j] had done a call4 to call[j+1].
1716 window rotated to call[j] start point;
1717 a0-a3 are registers to be saved;
1718 a4-a15 must be preserved;
1719 a5 is call[j+1]'s stack pointer.
1720 --------------------------------------------------------------------------------
1724 .global _WindowOverflow4
1727 s32e a0, a5, -16 /* save a0 to call[j+1]'s stack frame */
1728 s32e a1, a5, -12 /* save a1 to call[j+1]'s stack frame */
1729 s32e a2, a5, -8 /* save a2 to call[j+1]'s stack frame */
1730 s32e a3, a5, -4 /* save a3 to call[j+1]'s stack frame */
1731 rfwo /* rotates back to call[i] position */
1734 --------------------------------------------------------------------------------
1735 Window Underflow Exception for Call4
1737 Invoked by RETW returning from call[i+1] to call[i]
1738 where call[i]'s registers must be reloaded (not live in ARs);
1739 where call[i] had done a call4 to call[i+1].
1741 window rotated to call[i] start point;
1742 a0-a3 are undefined, must be reloaded with call[i].reg[0..3];
1743 a4-a15 must be preserved (they are call[i+1].reg[0..11]);
1744 a5 is call[i+1]'s stack pointer.
1745 --------------------------------------------------------------------------------
1749 .global _WindowUnderflow4
1752 l32e a0, a5, -16 /* restore a0 from call[i+1]'s stack frame */
1753 l32e a1, a5, -12 /* restore a1 from call[i+1]'s stack frame */
1754 l32e a2, a5, -8 /* restore a2 from call[i+1]'s stack frame */
1755 l32e a3, a5, -4 /* restore a3 from call[i+1]'s stack frame */
1759 --------------------------------------------------------------------------------
1760 Handle alloca exception generated by interruptee executing 'movsp'.
1761 This uses space between the window vectors, so is essentially "free".
1762 All interruptee's regs are intact except a0 which is saved in EXCSAVE_1,
1763 and PS.EXCM has been set by the exception hardware (can't be interrupted).
1764 The fact the alloca exception was taken means the registers associated with
1765 the base-save area have been spilled and will be restored by the underflow
1766 handler, so those 4 registers are available for scratch.
1767 The code is optimized to avoid unaligned branches and minimize cache misses.
1768 --------------------------------------------------------------------------------
1772 .global _xt_alloca_exc
1775 rsr a0, WINDOWBASE /* grab WINDOWBASE before rotw changes it */
1776 rotw -1 /* WINDOWBASE goes to a4, new a0-a3 are scratch */
1778 extui a3, a2, XCHAL_PS_OWB_SHIFT, XCHAL_PS_OWB_BITS
1779 xor a3, a3, a4 /* bits changed from old to current windowbase */
1780 rsr a4, EXCSAVE_1 /* restore original a0 (now in a4) */
1781 slli a3, a3, XCHAL_PS_OWB_SHIFT
1782 xor a2, a2, a3 /* flip changed bits in old window base */
1783 wsr a2, PS /* update PS.OWB to new window base */
1786 _bbci.l a4, 31, _WindowUnderflow4
1787 rotw -1 /* original a0 goes to a8 */
1788 _bbci.l a8, 30, _WindowUnderflow8
1790 j _WindowUnderflow12
1793 --------------------------------------------------------------------------------
1794 Window Overflow Exception for Call8
1796 Invoked if a call[i] referenced a register (a4-a15)
1797 that contains data from ancestor call[j];
1798 call[j] had done a call8 to call[j+1].
1800 window rotated to call[j] start point;
1801 a0-a7 are registers to be saved;
1802 a8-a15 must be preserved;
1803 a9 is call[j+1]'s stack pointer.
1804 --------------------------------------------------------------------------------
1808 .global _WindowOverflow8
1811 s32e a0, a9, -16 /* save a0 to call[j+1]'s stack frame */
1812 l32e a0, a1, -12 /* a0 <- call[j-1]'s sp
1813 (used to find end of call[j]'s frame) */
1814 s32e a1, a9, -12 /* save a1 to call[j+1]'s stack frame */
1815 s32e a2, a9, -8 /* save a2 to call[j+1]'s stack frame */
1816 s32e a3, a9, -4 /* save a3 to call[j+1]'s stack frame */
1817 s32e a4, a0, -32 /* save a4 to call[j]'s stack frame */
1818 s32e a5, a0, -28 /* save a5 to call[j]'s stack frame */
1819 s32e a6, a0, -24 /* save a6 to call[j]'s stack frame */
1820 s32e a7, a0, -20 /* save a7 to call[j]'s stack frame */
1821 rfwo /* rotates back to call[i] position */
1824 --------------------------------------------------------------------------------
1825 Window Underflow Exception for Call8
1827 Invoked by RETW returning from call[i+1] to call[i]
1828 where call[i]'s registers must be reloaded (not live in ARs);
1829 where call[i] had done a call8 to call[i+1].
1831 window rotated to call[i] start point;
1832 a0-a7 are undefined, must be reloaded with call[i].reg[0..7];
1833 a8-a15 must be preserved (they are call[i+1].reg[0..7]);
1834 a9 is call[i+1]'s stack pointer.
1835 --------------------------------------------------------------------------------
1839 .global _WindowUnderflow8
1842 l32e a0, a9, -16 /* restore a0 from call[i+1]'s stack frame */
1843 l32e a1, a9, -12 /* restore a1 from call[i+1]'s stack frame */
1844 l32e a2, a9, -8 /* restore a2 from call[i+1]'s stack frame */
1845 l32e a7, a1, -12 /* a7 <- call[i-1]'s sp
1846 (used to find end of call[i]'s frame) */
1847 l32e a3, a9, -4 /* restore a3 from call[i+1]'s stack frame */
1848 l32e a4, a7, -32 /* restore a4 from call[i]'s stack frame */
1849 l32e a5, a7, -28 /* restore a5 from call[i]'s stack frame */
1850 l32e a6, a7, -24 /* restore a6 from call[i]'s stack frame */
1851 l32e a7, a7, -20 /* restore a7 from call[i]'s stack frame */
1855 --------------------------------------------------------------------------------
1856 Window Overflow Exception for Call12
1858 Invoked if a call[i] referenced a register (a4-a15)
1859 that contains data from ancestor call[j];
1860 call[j] had done a call12 to call[j+1].
1862 window rotated to call[j] start point;
1863 a0-a11 are registers to be saved;
1864 a12-a15 must be preserved;
1865 a13 is call[j+1]'s stack pointer.
1866 --------------------------------------------------------------------------------
1870 .global _WindowOverflow12
1873 s32e a0, a13, -16 /* save a0 to call[j+1]'s stack frame */
1874 l32e a0, a1, -12 /* a0 <- call[j-1]'s sp
1875 (used to find end of call[j]'s frame) */
1876 s32e a1, a13, -12 /* save a1 to call[j+1]'s stack frame */
1877 s32e a2, a13, -8 /* save a2 to call[j+1]'s stack frame */
1878 s32e a3, a13, -4 /* save a3 to call[j+1]'s stack frame */
1879 s32e a4, a0, -48 /* save a4 to end of call[j]'s stack frame */
1880 s32e a5, a0, -44 /* save a5 to end of call[j]'s stack frame */
1881 s32e a6, a0, -40 /* save a6 to end of call[j]'s stack frame */
1882 s32e a7, a0, -36 /* save a7 to end of call[j]'s stack frame */
1883 s32e a8, a0, -32 /* save a8 to end of call[j]'s stack frame */
1884 s32e a9, a0, -28 /* save a9 to end of call[j]'s stack frame */
1885 s32e a10, a0, -24 /* save a10 to end of call[j]'s stack frame */
1886 s32e a11, a0, -20 /* save a11 to end of call[j]'s stack frame */
1887 rfwo /* rotates back to call[i] position */
1890 --------------------------------------------------------------------------------
1891 Window Underflow Exception for Call12
1893 Invoked by RETW returning from call[i+1] to call[i]
1894 where call[i]'s registers must be reloaded (not live in ARs);
1895 where call[i] had done a call12 to call[i+1].
1897 window rotated to call[i] start point;
1898 a0-a11 are undefined, must be reloaded with call[i].reg[0..11];
1899 a12-a15 must be preserved (they are call[i+1].reg[0..3]);
1900 a13 is call[i+1]'s stack pointer.
1901 --------------------------------------------------------------------------------
1905 .global _WindowUnderflow12
1908 l32e a0, a13, -16 /* restore a0 from call[i+1]'s stack frame */
1909 l32e a1, a13, -12 /* restore a1 from call[i+1]'s stack frame */
1910 l32e a2, a13, -8 /* restore a2 from call[i+1]'s stack frame */
1911 l32e a11, a1, -12 /* a11 <- call[i-1]'s sp
1912 (used to find end of call[i]'s frame) */
1913 l32e a3, a13, -4 /* restore a3 from call[i+1]'s stack frame */
1914 l32e a4, a11, -48 /* restore a4 from end of call[i]'s stack frame */
1915 l32e a5, a11, -44 /* restore a5 from end of call[i]'s stack frame */
1916 l32e a6, a11, -40 /* restore a6 from end of call[i]'s stack frame */
1917 l32e a7, a11, -36 /* restore a7 from end of call[i]'s stack frame */
1918 l32e a8, a11, -32 /* restore a8 from end of call[i]'s stack frame */
1919 l32e a9, a11, -28 /* restore a9 from end of call[i]'s stack frame */
1920 l32e a10, a11, -24 /* restore a10 from end of call[i]'s stack frame */
1921 l32e a11, a11, -20 /* restore a11 from end of call[i]'s stack frame */
1924 #endif /* XCHAL_HAVE_WINDOWED */