1 /*
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.
5 *
6 * SPDX-License-Identifier: MIT
7 *
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:
14 *
15 * The above copyright notice and this permission notice shall be included in all
16 * copies or substantial portions of the Software.
17 *
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.
24 *
25 * https://www.FreeRTOS.org
26 * https://github.com/FreeRTOS
27 *
28 */
29
30/*
31        XTENSA VECTORS AND LOW LEVEL HANDLERS FOR AN RTOS
32
33  Xtensa low level exception and interrupt vectors and handlers for an RTOS.
34
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.
39
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.
45
46  Note that the system timer interrupt is handled specially, and is
47  dispatched to the RTOS-specific handler. This timer cannot be hooked
48  by application code.
49
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).
53
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.                        !!
57
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
63  exceptions.
64
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.
73
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.
81
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.
97
98 */
99
100#include "xtensa_rtos.h"
101
102
103/* Enable stack backtrace across exception/interrupt - see below */
104#define XT_DEBUG_BACKTRACE    1
105
106
107/*
108--------------------------------------------------------------------------------
109  Defines used to access _xtos_interrupt_table.
110--------------------------------------------------------------------------------
111*/
112#define XIE_HANDLER     0
113#define XIE_ARG         4
114#define XIE_SIZE        8
115
116/*
117--------------------------------------------------------------------------------
118  Macro extract_msb - return the input with only the highest bit set.
119
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--------------------------------------------------------------------------------
124*/
125
126    .macro  extract_msb     aout ain
1271:
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       */
132    .endm
133
134/*
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.
142
143  Level triggered and software interrupts are automatically deasserted by
144  this code.
145
146  ASSUMPTIONS:
147    -- PS.INTLEVEL is set to "level" at entry
148    -- PS.EXCM = 0, C calling enabled
149
150  NOTE: For CALL0 ABI, a12-a15 have not yet been saved.
151
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--------------------------------------------------------------------------------
156*/
157
158    .macro  dispatch_c_isr    level  mask
159
160    /* Get mask of pending, enabled interrupts at this level into a2. */
161
162.L_xt_user_int_&level&:
163    rsr     a2, INTENABLE
164    rsr     a3, INTERRUPT
165    movi    a4, \mask
166    and     a2, a2, a3
167    and     a2, a2, a4
168    beqz    a2, 9f                          /* nothing to do */
169
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.
173    */
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   */
180    #endif
181    #endif
182
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
187    beqz    a4, 2f
188    #ifdef __XTENSA_CALL0_ABI__
189    callx0  a4
190    beqz    a2, 9f
191    #else
192    mov     a6, a2
193    callx4  a4
194    beqz    a6, 9f
195    mov     a2, a6
196    #endif
1972:
198    #endif
199
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.     */
202
203    extract_msb  a4, a2                     /* a4 = MSB of a2, a2 trashed */
204
205    #ifdef XT_USE_SWPRI
206    /* Enable all interrupts at this level that are numerically higher
207       than the one we just selected, since they are treated as higher
208       priority.
209    */
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 */
214    movi    a3, _xt_intdata
215    l32i    a6, a3, 4                       /* a6 = _xt_vpri_mask */
216    neg     a2, a2
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 */
220    rsr     a3, INTENABLE
221    and     a3, a3, a2                      /* mask off all bits <= a4 bit */
222    wsr     a3, INTENABLE
223    rsil    a3, \level - 1                  /* lower interrupt level by 1 */
224    #endif
225
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 */
229
230    find_ms_setbit a3, a4, a3, 0            /* a3 = interrupt number */
231
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 */
239    mov     a2, a12
240    #else
241    mov     a2, a6                          /* save in windowed reg */
242    l32i    a6, a3, XIE_ARG                 /* a6 = handler arg */
243    callx4  a4                              /* call handler */
244    #endif
245
246    #ifdef XT_USE_SWPRI
247    j       8f
248    #else
249    j       .L_xt_user_int_&level&          /* check for more interrupts */
250    #endif
251
2527:
253
254    .ifeq XT_TIMER_INTPRI - \level
255.L_xt_user_int_timer_&level&:
256    /*
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).
260    */
261
262    #ifdef __XTENSA_CALL0_ABI__
263    mov     a12, a6
264    call0   XT_RTOS_TIMER_INT
265    mov     a2, a12
266    #else
267    mov     a2, a6
268    call4   XT_RTOS_TIMER_INT
269    #endif
270    .endif
271
272    #ifdef XT_USE_SWPRI
273    j       8f
274    #else
275    j       .L_xt_user_int_&level&          /* check for more interrupts */
276    #endif
277
278    #ifdef XT_USE_SWPRI
2798:
280    /* Restore old value of _xt_vpri_mask from a2. Also update INTENABLE from
281       virtual _xt_intenable which _could_ have changed during interrupt
282       processing. */
283
284    movi    a3, _xt_intdata
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      */
289    #endif
290
2919:
292    /* done */
293
294    .endm
295
296
297/*
298--------------------------------------------------------------------------------
299  Panic handler.
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--------------------------------------------------------------------------------
303*/
304
305    .text
306    .global     _xt_panic
307    .type       _xt_panic,@function
308    .align      4
309    .literal_position
310
311_xt_panic:
312    #ifdef XT_SIMULATOR
313    addi    a4, a0, -3                      /* point to call0 */
314    movi    a3, _xt_panic_message
315    movi    a2, SYS_log_msg
316    simcall
317    movi    a2, SYS_gdb_abort
318    simcall
319    #else
320    rsil    a2, XCHAL_EXCM_LEVEL            /* disable all low & med ints */
3211:  j       1b                              /* loop infinitely */
322    #endif
323
324    .section    .rodata, "a"
325    .align      4
326
327_xt_panic_message:
328    .string "\n*** _xt_panic() was called from 0x%08x or jumped to. ***\n"
329
330
331/*
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--------------------------------------------------------------------------------
340*/
341
342    #ifdef XT_INTEXC_HOOKS
343    .data
344    .global     _xt_intexc_hooks
345    .type       _xt_intexc_hooks,@object
346    .align      4
347
348_xt_intexc_hooks:
349    .fill       XT_INTEXC_HOOK_NUM, 4, 0
350    #endif
351
352
353/*
354--------------------------------------------------------------------------------
355  EXCEPTION AND LEVEL 1 INTERRUPT VECTORS AND LOW LEVEL HANDLERS
356  (except window exception vectors).
357
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.
362
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.
369
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--------------------------------------------------------------------------------
375*/
376
377
378/*
379--------------------------------------------------------------------------------
380Debug Exception.
381--------------------------------------------------------------------------------
382*/
383
384#if XCHAL_HAVE_DEBUG
385
386    .begin      literal_prefix .DebugExceptionVector
387    .section    .DebugExceptionVector.text, "ax"
388    .global     _DebugExceptionVector
389    .align      4
390    .literal_position
391
392_DebugExceptionVector:
393
394    #ifdef XT_SIMULATOR
395    /*
396    In the simulator, let the debugger (if any) handle the debug exception,
397    or simply stop the simulation:
398    */
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 */
405    jx      a3
406    #else
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 */
410    #endif
411
412    .end        literal_prefix
413
414#endif
415
416/*
417--------------------------------------------------------------------------------
418Double Exception.
419Double exceptions are not a normal occurrence. They indicate a bug of some kind.
420--------------------------------------------------------------------------------
421*/
422
423#ifdef XCHAL_DOUBLEEXC_VECTOR_VADDR
424
425    .begin      literal_prefix .DoubleExceptionVector
426    .section    .DoubleExceptionVector.text, "ax"
427    .global     _DoubleExceptionVector
428    .align      4
429    .literal_position
430
431_DoubleExceptionVector:
432
433    #if XCHAL_HAVE_DEBUG
434    break   1, 4                            /* unhandled double exception */
435    #endif
436    call0   _xt_panic                       /* does not return */
437    rfde                                    /* make a0 point here not later */
438
439    .end        literal_prefix
440
441#endif /* XCHAL_DOUBLEEXC_VECTOR_VADDR */
442
443/*
444--------------------------------------------------------------------------------
445Kernel Exception (including Level 1 Interrupt from kernel mode).
446--------------------------------------------------------------------------------
447*/
448
449    .begin      literal_prefix .KernelExceptionVector
450    .section    .KernelExceptionVector.text, "ax"
451    .global     _KernelExceptionVector
452    .align      4
453    .literal_position
454
455_KernelExceptionVector:
456
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) */
460
461    .end        literal_prefix
462
463    .text
464    .align      4
465
466_xt_kernel_exc:
467    #if XCHAL_HAVE_DEBUG
468    break   1, 0                            /* unhandled kernel exception */
469    #endif
470    call0   _xt_panic                       /* does not return */
471    rfe                                     /* make a0 point here not there */
472
473
474/*
475--------------------------------------------------------------------------------
476User Exception (including Level 1 Interrupt from user mode).
477--------------------------------------------------------------------------------
478*/
479
480    .begin      literal_prefix .UserExceptionVector
481    .section    .UserExceptionVector.text, "ax"
482    .global     _UserExceptionVector
483    .type       _UserExceptionVector,@function
484    .align      4
485    .literal_position
486
487_UserExceptionVector:
488
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) */
492
493    .end        literal_prefix
494
495/*
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
500  mainline.
501--------------------------------------------------------------------------------
502*/
503
504    .text
505
506    #if XCHAL_HAVE_WINDOWED
507    .align      4
508_xt_to_alloca_exc:
509    call0   _xt_alloca_exc                  /* in window vectors section */
510    /* never returns here - call0 is used as a jump (see note at top) */
511    #endif
512
513    .align      4
514_xt_to_syscall_exc:
515    call0   _xt_syscall_exc
516    /* never returns here - call0 is used as a jump (see note at top) */
517
518    #if XCHAL_CP_NUM > 0
519    .align      4
520_xt_to_coproc_exc:
521    call0   _xt_coproc_exc
522    /* never returns here - call0 is used as a jump (see note at top) */
523    #endif
524
525
526/*
527--------------------------------------------------------------------------------
528  User exception handler.
529--------------------------------------------------------------------------------
530*/
531
532    .type       _xt_user_exc,@function
533    .align      4
534
535_xt_user_exc:
536
537    /* If level 1 interrupt then jump to the dispatcher */
538    rsr     a0, EXCCAUSE
539    beqi    a0, EXCCAUSE_LEVEL1INTERRUPT, _xt_lowint1
540
541    /* Handle any coprocessor exceptions. Rely on the fact that exception
542       numbers above EXCCAUSE_CP0_DISABLED all relate to the coprocessors.
543    */
544    #if XCHAL_CP_NUM > 0
545    bgeui   a0, EXCCAUSE_CP0_DISABLED, _xt_to_coproc_exc
546    #endif
547
548    /* Handle alloca and syscall exceptions */
549    #if XCHAL_HAVE_WINDOWED
550    beqi    a0, EXCCAUSE_ALLOCA,  _xt_to_alloca_exc
551    #endif
552    beqi    a0, EXCCAUSE_SYSCALL, _xt_to_syscall_exc
553
554    /* Handle all other exceptions. All can have user-defined handlers. */
555    /* NOTE: we'll stay on the user stack for exception handling.       */
556
557    /* Allocate exception frame and save minimal context. */
558    mov     a0, sp
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 */
563    #endif
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 */
572    #endif
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
576
577    /* Save exc cause and vaddr into exception frame */
578    rsr     a0, EXCCAUSE
579    s32i    a0, sp, XT_STK_EXCCAUSE
580    rsr     a0, EXCVADDR
581    s32i    a0, sp, XT_STK_EXCVADDR
582
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
586    #else
587    movi    a0, PS_INTLEVEL(XCHAL_EXCM_LEVEL) | PS_UM | PS_WOE
588    #endif
589    wsr     a0, PS
590
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 */
598    #else
599    rsync                                   /* wait for WSR.PS to complete */
600    #endif
601    #endif
602
603    rsr     a2, EXCCAUSE                    /* recover exc cause */
604
605    #ifdef XT_INTEXC_HOOKS
606    /*
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).
609    */
610    movi    a4, _xt_intexc_hooks
611    l32i    a4, a4, 0                       /* user exception hook index 0 */
612    beqz    a4, 1f
613.Ln_xt_user_exc_call_hook:
614    #ifdef __XTENSA_CALL0_ABI__
615    callx0  a4
616    beqi    a2, -1, .L_xt_user_done
617    #else
618    mov     a6, a2
619    callx4  a4
620    beqi    a6, -1, .L_xt_user_done
621    mov     a2, a6
622    #endif
6231:
624    #endif
625
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 */
633    #else
634    mov     a6, sp                          /* a6 = pointer to exc frame */
635    callx4  a4                              /* call handler */
636    #endif
637
638.L_xt_user_done:
639
640    /* Restore context and return */
641    call0   _xt_context_restore
642    l32i    a0, sp, XT_STK_PS               /* retrieve interruptee's PS */
643    wsr     a0, PS
644    l32i    a0, sp, XT_STK_PC               /* retrieve interruptee's PC */
645    wsr     a0, EPC_1
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 */
650
651
652/*
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--------------------------------------------------------------------------------
657*/
658
659    .global     _xt_user_exit
660    .type       _xt_user_exit,@function
661    .align      4
662_xt_user_exit:
663    l32i    a0, sp, XT_STK_PS               /* retrieve interruptee's PS */
664    wsr     a0, PS
665    l32i    a0, sp, XT_STK_PC               /* retrieve interruptee's PC */
666    wsr     a0, EPC_1
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 */
671
672
673/*
674--------------------------------------------------------------------------------
675Syscall Exception Handler (jumped to from User Exception Handler).
676Syscall 0 is required to spill the register windows (no-op in Call 0 ABI).
677Only syscall 0 is handled here. Other syscalls return -1 to caller in a2.
678--------------------------------------------------------------------------------
679*/
680
681    .text
682    .type       _xt_syscall_exc,@function
683    .align      4
684_xt_syscall_exc:
685
686    #ifdef __XTENSA_CALL0_ABI__
687    /*
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.
691    rsr     a0, PS
692    addi    a0, a0, -PS_EXCM_MASK
693    wsr     a0, PS
694    */
695    addi    sp, sp, -16
696    s32i    a2, sp, 8
697    s32i    a3, sp, 12
698    #else   /* Windowed ABI */
699    /*
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.
704    */
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
709    #endif
710
711    /*
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.
715    */
716    rsr     a2, EPC_1                       /* a2 = PC of 'syscall' */
717    addi    a3, a2, 3                       /* ++PC                 */
718    #if XCHAL_HAVE_LOOPS
719    rsr     a0, LEND                        /* if (PC == LEND       */
720    bne     a3, a0, 1f
721    rsr     a0, LCOUNT                      /*     && LCOUNT != 0)  */
722    beqz    a0, 1f                          /* {                    */
723    addi    a0, a0, -1                      /*   --LCOUNT           */
724    rsr     a3, LBEG                        /*   PC = LBEG          */
725    wsr     a0, LCOUNT                      /* }                    */
726    #endif
7271:  wsr     a3, EPC_1                       /* update PC            */
728
729    /* Restore interruptee's context and return from exception. */
730    #ifdef __XTENSA_CALL0_ABI__
731    l32i    a2, sp, 8
732    l32i    a3, sp, 12
733    addi    sp, sp, 16
734    #else
735    call0   _xt_context_restore
736    addi    sp, sp, XT_STK_FRMSZ
737    #endif
738    movi    a0, -1
739    movnez  a2, a0, a2                      /* return -1 if not syscall 0 */
740    rsr     a0, EXCSAVE_1
741    rfe
742
743/*
744--------------------------------------------------------------------------------
745Co-Processor Exception Handler (jumped to from User Exception Handler).
746These exceptions are generated by co-processor instructions, which are only
747allowed in thread code (not in interrupts or kernel code). This restriction is
748deliberately imposed to reduce the burden of state-save/restore in interrupts.
749--------------------------------------------------------------------------------
750*/
751#if XCHAL_CP_NUM > 0
752
753    .section .rodata, "a"
754
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
762
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 */
766    .set    i, 0
767_xt_coproc_mask:
768    .rept   XCHAL_CP_MAX
769    .long   (i<<16) | (1<<i)    // upper 16-bits = i, lower = bitmask
770    .set    i, i+1
771    .endr
772
773    .data
774
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 */
779_xt_coproc_owner_sa:
780    .space  XCHAL_CP_MAX << 2
781
782    .text
783
784
785    .align  4
786.L_goto_invalid:
787    j   .L_xt_coproc_invalid    /* not in a thread (invalid) */
788    .align  4
789.L_goto_done:
790    j   .L_xt_coproc_done
791
792
793/*
794--------------------------------------------------------------------------------
795  Coprocessor exception handler.
796  At entry, only a0 has been saved (in EXCSAVE_1).
797--------------------------------------------------------------------------------
798*/
799
800    .type   _xt_coproc_exc,@function
801    .align  4
802
803_xt_coproc_exc:
804
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 */
811    #endif
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 */
820    #endif
821    movi    a0, _xt_user_exit               /* save exit point for dispatch */
822    s32i    a0, sp, XT_STK_EXIT
823
824    rsr     a0, EXCCAUSE
825    s32i    a5, sp, XT_STK_A5               /* save a5 */
826    addi    a5, a0, -EXCCAUSE_CP0_DISABLED  /* a5 = CP index */
827
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
833
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) */
837
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) */
846    wsr     a4, CPENABLE
847
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 */
853
854    /* Only need to context switch if new owner != old owner. */
855    beq     a15, a2, .L_goto_done           /* new owner == old, we're done */
856
857    /* If no old owner then nothing to save. */
858    beqz    a2, .L_check_new
859
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    */
863
864.L_save_old:
865    /* Save old owner's coprocessor state. */
866
867    movi    a5, _xt_coproc_sa_offset
868
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 */
872
873    extui   a4,  a0,  16,  5                /* a4 = CP index = n */
874    addx4   a5,  a4,  a5                    /* a5 = &_xt_coproc_sa_offset[n] */
875
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 */
884
885    /*
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.
891    */
892    xchal_cpi_store_funcbody
893
894.L_check_new:
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. */
897
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 */
903
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 */
910
911    /*
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.
917    */
918    xchal_cpi_load_funcbody
919
920    /* Restore interruptee's saved registers. */
921    /* Can omit rsync for wsr.CPENABLE here because _xt_user_exit does it. */
922.L_xt_coproc_done:
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) */
930
931.L_check_cs:
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
946    j       .L_xt_coproc_done
947
948    /* Co-processor exception occurred outside a thread (not supported). */
949.L_xt_coproc_invalid:
950    #if XCHAL_HAVE_DEBUG
951    break   1, 1                            /* unhandled user exception */
952    #endif
953    call0   _xt_panic                       /* not in a thread (invalid) */
954    /* never returns */
955
956
957#endif /* XCHAL_CP_NUM */
958
959
960/*
961-------------------------------------------------------------------------------
962  Level 1 interrupt dispatch. Assumes stack frame has not been allocated yet.
963-------------------------------------------------------------------------------
964*/
965
966    .text
967    .type       _xt_lowint1,@function
968    .align      4
969
970_xt_lowint1:
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
982
983    /* Save rest of interrupt context and enter RTOS. */
984    call0   XT_RTOS_INT_ENTER               /* common RTOS interrupt entry */
985
986    /* !! We are now on the RTOS system stack !! */
987
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
991    #else
992    movi    a0, PS_INTLEVEL(1) | PS_UM | PS_WOE
993    #endif
994    wsr     a0, PS
995    rsync
996
997    /* OK to call C code at this point, dispatch user ISRs */
998
999    dispatch_c_isr 1 XCHAL_INTLEVEL1_MASK
1000
1001    /* Done handling interrupts, transfer control to OS */
1002    call0   XT_RTOS_INT_EXIT                /* does not return directly here */
1003
1004
1005/*
1006-------------------------------------------------------------------------------
1007  MEDIUM PRIORITY (LEVEL 2+) INTERRUPT VECTORS AND LOW LEVEL HANDLERS.
1008
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.
1013
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.
1018
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.
1025
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-------------------------------------------------------------------------------
1031*/
1032
1033#if XCHAL_EXCM_LEVEL >= 2
1034
1035    .begin      literal_prefix .Level2InterruptVector
1036    .section    .Level2InterruptVector.text, "ax"
1037    .global     _Level2Vector
1038    .type       _Level2Vector,@function
1039    .align      4
1040    .literal_position
1041
1042_Level2Vector:
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) */
1046
1047    .end        literal_prefix
1048
1049    .text
1050    .type       _xt_medint2,@function
1051    .align      4
1052_xt_medint2:
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
1064
1065    /* Save rest of interrupt context and enter RTOS. */
1066    call0   XT_RTOS_INT_ENTER               /* common RTOS interrupt entry */
1067
1068    /* !! We are now on the RTOS system stack !! */
1069
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
1073    #else
1074    movi    a0, PS_INTLEVEL(2) | PS_UM | PS_WOE
1075    #endif
1076    wsr     a0, PS
1077    rsync
1078
1079    /* OK to call C code at this point, dispatch user ISRs */
1080
1081    dispatch_c_isr 2 XCHAL_INTLEVEL2_MASK
1082
1083    /* Done handling interrupts, transfer control to OS */
1084    call0   XT_RTOS_INT_EXIT                /* does not return directly here */
1085
1086    /*
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.
1089    */
1090    .global     _xt_medint2_exit
1091    .type       _xt_medint2_exit,@function
1092    .align      4
1093_xt_medint2_exit:
1094    /* Restore only level-specific regs (the rest were already restored) */
1095    l32i    a0, sp, XT_STK_PS               /* retrieve interruptee's PS */
1096    wsr     a0, EPS_2
1097    l32i    a0, sp, XT_STK_PC               /* retrieve interruptee's PC */
1098    wsr     a0, EPC_2
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 */
1102    rfi     2
1103
1104#endif  /* Level 2 */
1105
1106#if XCHAL_EXCM_LEVEL >= 3
1107
1108    .begin      literal_prefix .Level3InterruptVector
1109    .section    .Level3InterruptVector.text, "ax"
1110    .global     _Level3Vector
1111    .type       _Level3Vector,@function
1112    .align      4
1113    .literal_position
1114
1115_Level3Vector:
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) */
1119
1120    .end        literal_prefix
1121
1122    .text
1123    .type       _xt_medint3,@function
1124    .align      4
1125_xt_medint3:
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
1137
1138    /* Save rest of interrupt context and enter RTOS. */
1139    call0   XT_RTOS_INT_ENTER               /* common RTOS interrupt entry */
1140
1141    /* !! We are now on the RTOS system stack !! */
1142
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
1146    #else
1147    movi    a0, PS_INTLEVEL(3) | PS_UM | PS_WOE
1148    #endif
1149    wsr     a0, PS
1150    rsync
1151
1152    /* OK to call C code at this point, dispatch user ISRs */
1153
1154    dispatch_c_isr 3 XCHAL_INTLEVEL3_MASK
1155
1156    /* Done handling interrupts, transfer control to OS */
1157    call0   XT_RTOS_INT_EXIT                /* does not return directly here */
1158
1159    /*
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.
1162    */
1163    .global     _xt_medint3_exit
1164    .type       _xt_medint3_exit,@function
1165    .align      4
1166_xt_medint3_exit:
1167    /* Restore only level-specific regs (the rest were already restored) */
1168    l32i    a0, sp, XT_STK_PS               /* retrieve interruptee's PS */
1169    wsr     a0, EPS_3
1170    l32i    a0, sp, XT_STK_PC               /* retrieve interruptee's PC */
1171    wsr     a0, EPC_3
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 */
1175    rfi     3
1176
1177#endif  /* Level 3 */
1178
1179#if XCHAL_EXCM_LEVEL >= 4
1180
1181    .begin      literal_prefix .Level4InterruptVector
1182    .section    .Level4InterruptVector.text, "ax"
1183    .global     _Level4Vector
1184    .type       _Level4Vector,@function
1185    .align      4
1186    .literal_position
1187
1188_Level4Vector:
1189    wsr     a0, EXCSAVE_4                   /* preserve a0 */
1190    call0   _xt_medint4                     /* load interrupt handler */
1191
1192    .end        literal_prefix
1193
1194    .text
1195    .type       _xt_medint4,@function
1196    .align      4
1197_xt_medint4:
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
1209
1210    /* Save rest of interrupt context and enter RTOS. */
1211    call0   XT_RTOS_INT_ENTER               /* common RTOS interrupt entry */
1212
1213    /* !! We are now on the RTOS system stack !! */
1214
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
1218    #else
1219    movi    a0, PS_INTLEVEL(4) | PS_UM | PS_WOE
1220    #endif
1221    wsr     a0, PS
1222    rsync
1223
1224    /* OK to call C code at this point, dispatch user ISRs */
1225
1226    dispatch_c_isr 4 XCHAL_INTLEVEL4_MASK
1227
1228    /* Done handling interrupts, transfer control to OS */
1229    call0   XT_RTOS_INT_EXIT                /* does not return directly here */
1230
1231    /*
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.
1234    */
1235    .global     _xt_medint4_exit
1236    .type       _xt_medint4_exit,@function
1237    .align      4
1238_xt_medint4_exit:
1239    /* Restore only level-specific regs (the rest were already restored) */
1240    l32i    a0, sp, XT_STK_PS               /* retrieve interruptee's PS */
1241    wsr     a0, EPS_4
1242    l32i    a0, sp, XT_STK_PC               /* retrieve interruptee's PC */
1243    wsr     a0, EPC_4
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 */
1247    rfi     4
1248
1249#endif  /* Level 4 */
1250
1251#if XCHAL_EXCM_LEVEL >= 5
1252
1253    .begin      literal_prefix .Level5InterruptVector
1254    .section    .Level5InterruptVector.text, "ax"
1255    .global     _Level5Vector
1256    .type       _Level5Vector,@function
1257    .align      4
1258    .literal_position
1259
1260_Level5Vector:
1261    wsr     a0, EXCSAVE_5                   /* preserve a0 */
1262    call0   _xt_medint5                     /* load interrupt handler */
1263
1264    .end        literal_prefix
1265
1266    .text
1267    .type       _xt_medint5,@function
1268    .align      4
1269_xt_medint5:
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
1281
1282    /* Save rest of interrupt context and enter RTOS. */
1283    call0   XT_RTOS_INT_ENTER               /* common RTOS interrupt entry */
1284
1285    /* !! We are now on the RTOS system stack !! */
1286
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
1290    #else
1291    movi    a0, PS_INTLEVEL(5) | PS_UM | PS_WOE
1292    #endif
1293    wsr     a0, PS
1294    rsync
1295
1296    /* OK to call C code at this point, dispatch user ISRs */
1297
1298    dispatch_c_isr 5 XCHAL_INTLEVEL5_MASK
1299
1300    /* Done handling interrupts, transfer control to OS */
1301    call0   XT_RTOS_INT_EXIT                /* does not return directly here */
1302
1303    /*
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.
1306    */
1307    .global     _xt_medint5_exit
1308    .type       _xt_medint5_exit,@function
1309    .align      4
1310_xt_medint5_exit:
1311    /* Restore only level-specific regs (the rest were already restored) */
1312    l32i    a0, sp, XT_STK_PS               /* retrieve interruptee's PS */
1313    wsr     a0, EPS_5
1314    l32i    a0, sp, XT_STK_PC               /* retrieve interruptee's PC */
1315    wsr     a0, EPC_5
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 */
1319    rfi     5
1320
1321#endif  /* Level 5 */
1322
1323#if XCHAL_EXCM_LEVEL >= 6
1324
1325    .begin      literal_prefix .Level6InterruptVector
1326    .section    .Level6InterruptVector.text, "ax"
1327    .global     _Level6Vector
1328    .type       _Level6Vector,@function
1329    .align      4
1330    .literal_position
1331
1332_Level6Vector:
1333    wsr     a0, EXCSAVE_6                   /* preserve a0 */
1334    call0   _xt_medint6                     /* load interrupt handler */
1335
1336    .end        literal_prefix
1337
1338    .text
1339    .type       _xt_medint6,@function
1340    .align      4
1341_xt_medint6:
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
1353
1354    /* Save rest of interrupt context and enter RTOS. */
1355    call0   XT_RTOS_INT_ENTER               /* common RTOS interrupt entry */
1356
1357    /* !! We are now on the RTOS system stack !! */
1358
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
1362    #else
1363    movi    a0, PS_INTLEVEL(6) | PS_UM | PS_WOE
1364    #endif
1365    wsr     a0, PS
1366    rsync
1367
1368    /* OK to call C code at this point, dispatch user ISRs */
1369
1370    dispatch_c_isr 6 XCHAL_INTLEVEL6_MASK
1371
1372    /* Done handling interrupts, transfer control to OS */
1373    call0   XT_RTOS_INT_EXIT                /* does not return directly here */
1374
1375    /*
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.
1378    */
1379    .global     _xt_medint6_exit
1380    .type       _xt_medint6_exit,@function
1381    .align      4
1382_xt_medint6_exit:
1383    /* Restore only level-specific regs (the rest were already restored) */
1384    l32i    a0, sp, XT_STK_PS               /* retrieve interruptee's PS */
1385    wsr     a0, EPS_6
1386    l32i    a0, sp, XT_STK_PC               /* retrieve interruptee's PC */
1387    wsr     a0, EPC_6
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 */
1391    rfi     6
1392
1393#endif  /* Level 6 */
1394
1395
1396/*******************************************************************************
1397
1398HIGH PRIORITY (LEVEL > XCHAL_EXCM_LEVEL) INTERRUPT VECTORS AND HANDLERS
1399
1400High priority interrupts are by definition those with priorities greater
1401than XCHAL_EXCM_LEVEL. This includes non-maskable (NMI). High priority
1402interrupts cannot interact with the RTOS, that is they must save all regs
1403they use and not call any RTOS function.
1404
1405A further restriction imposed by the Xtensa windowed architecture is that
1406high priority interrupts must not modify the stack area even logically
1407"above" the top of the interrupted stack (they need to provide their
1408own stack or static save area).
1409
1410Cadence Design Systems recommends high priority interrupt handlers be coded in assembly
1411and used for purposes requiring very short service times.
1412
1413Here are templates for high priority (level 2+) interrupt vectors.
1414They assume only one interrupt per level to avoid the burden of identifying
1415which interrupts at this level are pending and enabled. This allows for
1416minimum latency and avoids having to save/restore a2 in addition to a0.
1417If more than one interrupt per high priority level is configured, this burden
1418is on the handler which in any case must provide a way to save and restore
1419registers it uses without touching the interrupted stack.
1420
1421Each vector goes at a predetermined location according to the Xtensa
1422hardware configuration, which is ensured by its placement in a special
1423section known to the Xtensa linker support package (LSP). It performs
1424the minimum necessary before jumping to the handler in the .text section.
1425
1426*******************************************************************************/
1427
1428/*
1429Currently only shells for high priority interrupt handlers are provided
1430here. However a template and example can be found in the Cadence Design Systems tools
1431documentation: "Microprocessor Programmer's Guide".
1432*/
1433
1434#if XCHAL_NUM_INTLEVELS >=2 && XCHAL_EXCM_LEVEL <2 && XCHAL_DEBUGLEVEL !=2
1435
1436    .begin      literal_prefix .Level2InterruptVector
1437    .section    .Level2InterruptVector.text, "ax"
1438    .global     _Level2Vector
1439    .type       _Level2Vector,@function
1440    .align      4
1441_Level2Vector:
1442    wsr     a0, EXCSAVE_2                   /* preserve a0 */
1443    call0   _xt_highint2                    /* load interrupt handler */
1444
1445    .end        literal_prefix
1446
1447    .text
1448    .type       _xt_highint2,@function
1449    .align      4
1450_xt_highint2:
1451
1452    #ifdef XT_INTEXC_HOOKS
1453    /* Call interrupt hook if present to (pre)handle interrupts. */
1454    movi    a0, _xt_intexc_hooks
1455    l32i    a0, a0, 2<<2
1456    beqz    a0, 1f
1457.Ln_xt_highint2_call_hook:
1458    callx0  a0                              /* must NOT disturb stack! */
14591:
1460    #endif
1461
1462    /* USER_EDIT:
1463    ADD HIGH PRIORITY LEVEL 2 INTERRUPT HANDLER CODE HERE.
1464    */
1465
1466    .align  4
1467.L_xt_highint2_exit:
1468    rsr     a0, EXCSAVE_2                   /* restore a0 */
1469    rfi     2
1470
1471#endif  /* Level 2 */
1472
1473#if XCHAL_NUM_INTLEVELS >=3 && XCHAL_EXCM_LEVEL <3 && XCHAL_DEBUGLEVEL !=3
1474
1475    .begin      literal_prefix .Level3InterruptVector
1476    .section    .Level3InterruptVector.text, "ax"
1477    .global     _Level3Vector
1478    .type       _Level3Vector,@function
1479    .align      4
1480_Level3Vector:
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) */
1484
1485    .end        literal_prefix
1486
1487    .text
1488    .type       _xt_highint3,@function
1489    .align      4
1490_xt_highint3:
1491
1492    #ifdef XT_INTEXC_HOOKS
1493    /* Call interrupt hook if present to (pre)handle interrupts. */
1494    movi    a0, _xt_intexc_hooks
1495    l32i    a0, a0, 3<<2
1496    beqz    a0, 1f
1497.Ln_xt_highint3_call_hook:
1498    callx0  a0                              /* must NOT disturb stack! */
14991:
1500    #endif
1501
1502    /* USER_EDIT:
1503    ADD HIGH PRIORITY LEVEL 3 INTERRUPT HANDLER CODE HERE.
1504    */
1505
1506    .align  4
1507.L_xt_highint3_exit:
1508    rsr     a0, EXCSAVE_3                   /* restore a0 */
1509    rfi     3
1510
1511#endif  /* Level 3 */
1512
1513#if XCHAL_NUM_INTLEVELS >=4 && XCHAL_EXCM_LEVEL <4 && XCHAL_DEBUGLEVEL !=4
1514
1515    .begin      literal_prefix .Level4InterruptVector
1516    .section    .Level4InterruptVector.text, "ax"
1517    .global     _Level4Vector
1518    .type       _Level4Vector,@function
1519    .align      4
1520_Level4Vector:
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) */
1524
1525    .end        literal_prefix
1526
1527    .text
1528    .type       _xt_highint4,@function
1529    .align      4
1530_xt_highint4:
1531
1532    #ifdef XT_INTEXC_HOOKS
1533    /* Call interrupt hook if present to (pre)handle interrupts. */
1534    movi    a0, _xt_intexc_hooks
1535    l32i    a0, a0, 4<<2
1536    beqz    a0, 1f
1537.Ln_xt_highint4_call_hook:
1538    callx0  a0                              /* must NOT disturb stack! */
15391:
1540    #endif
1541
1542    /* USER_EDIT:
1543    ADD HIGH PRIORITY LEVEL 4 INTERRUPT HANDLER CODE HERE.
1544    */
1545
1546    .align  4
1547.L_xt_highint4_exit:
1548    rsr     a0, EXCSAVE_4                   /* restore a0 */
1549    rfi     4
1550
1551#endif  /* Level 4 */
1552
1553#if XCHAL_NUM_INTLEVELS >=5 && XCHAL_EXCM_LEVEL <5 && XCHAL_DEBUGLEVEL !=5
1554
1555    .begin      literal_prefix .Level5InterruptVector
1556    .section    .Level5InterruptVector.text, "ax"
1557    .global     _Level5Vector
1558    .type       _Level5Vector,@function
1559    .align      4
1560_Level5Vector:
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) */
1564
1565    .end        literal_prefix
1566
1567    .text
1568    .type       _xt_highint5,@function
1569    .align      4
1570_xt_highint5:
1571
1572    #ifdef XT_INTEXC_HOOKS
1573    /* Call interrupt hook if present to (pre)handle interrupts. */
1574    movi    a0, _xt_intexc_hooks
1575    l32i    a0, a0, 5<<2
1576    beqz    a0, 1f
1577.Ln_xt_highint5_call_hook:
1578    callx0  a0                              /* must NOT disturb stack! */
15791:
1580    #endif
1581
1582    /* USER_EDIT:
1583    ADD HIGH PRIORITY LEVEL 5 INTERRUPT HANDLER CODE HERE.
1584    */
1585
1586    .align  4
1587.L_xt_highint5_exit:
1588    rsr     a0, EXCSAVE_5                   /* restore a0 */
1589    rfi     5
1590
1591#endif  /* Level 5 */
1592
1593#if XCHAL_NUM_INTLEVELS >=6 && XCHAL_EXCM_LEVEL <6 && XCHAL_DEBUGLEVEL !=6
1594
1595    .begin      literal_prefix .Level6InterruptVector
1596    .section    .Level6InterruptVector.text, "ax"
1597    .global     _Level6Vector
1598    .type       _Level6Vector,@function
1599    .align      4
1600_Level6Vector:
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) */
1604
1605    .end        literal_prefix
1606
1607    .text
1608    .type       _xt_highint6,@function
1609    .align      4
1610_xt_highint6:
1611
1612    #ifdef XT_INTEXC_HOOKS
1613    /* Call interrupt hook if present to (pre)handle interrupts. */
1614    movi    a0, _xt_intexc_hooks
1615    l32i    a0, a0, 6<<2
1616    beqz    a0, 1f
1617.Ln_xt_highint6_call_hook:
1618    callx0  a0                              /* must NOT disturb stack! */
16191:
1620    #endif
1621
1622    /* USER_EDIT:
1623    ADD HIGH PRIORITY LEVEL 6 INTERRUPT HANDLER CODE HERE.
1624    */
1625
1626    .align  4
1627.L_xt_highint6_exit:
1628    rsr     a0, EXCSAVE_6                   /* restore a0 */
1629    rfi     6
1630
1631#endif  /* Level 6 */
1632
1633#if XCHAL_HAVE_NMI
1634
1635    .begin      literal_prefix .NMIExceptionVector
1636    .section    .NMIExceptionVector.text, "ax"
1637    .global     _NMIExceptionVector
1638    .type       _NMIExceptionVector,@function
1639    .align      4
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) */
1644
1645    .end        literal_prefix
1646
1647    .text
1648    .type       _xt_nmi,@function
1649    .align      4
1650_xt_nmi:
1651
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
1656    beqz    a0, 1f
1657.Ln_xt_nmi_call_hook:
1658    callx0  a0                              /* must NOT disturb stack! */
16591:
1660    #endif
1661
1662    /* USER_EDIT:
1663    ADD HIGH PRIORITY NON-MASKABLE INTERRUPT (NMI) HANDLER CODE HERE.
1664    */
1665
1666    .align  4
1667.L_xt_nmi_exit:
1668    rsr     a0, EXCSAVE + XCHAL_NMILEVEL    /* restore a0 */
1669    rfi     XCHAL_NMILEVEL
1670
1671#endif  /* NMI */
1672
1673
1674/*******************************************************************************
1675
1676WINDOW OVERFLOW AND UNDERFLOW EXCEPTION VECTORS AND ALLOCA EXCEPTION HANDLER
1677
1678Here is the code for each window overflow/underflow exception vector and
1679(interspersed) efficient code for handling the alloca exception cause.
1680Window exceptions are handled entirely in the vector area and are very
1681tight for performance. The alloca exception is also handled entirely in
1682the window vector area so comes at essentially no cost in code size.
1683Users should never need to modify them and Cadence Design Systems recommends
1684they do not.
1685
1686Window handlers go at predetermined vector locations according to the
1687Xtensa hardware configuration, which is ensured by their placement in a
1688special section known to the Xtensa linker support package (LSP). Since
1689their offsets in that section are always the same, the LSPs do not define
1690a section per vector.
1691
1692These things are coded for XEA2 only (XEA1 is not supported).
1693
1694Note on Underflow Handlers:
1695The underflow handler for returning from call[i+1] to call[i]
1696must preserve all the registers from call[i+1]'s window.
1697In particular, a0 and a1 must be preserved because the RETW instruction
1698will be reexecuted (and may even underflow if an intervening exception
1699has flushed call[i]'s registers).
1700Registers a2 and up may contain return values.
1701
1702*******************************************************************************/
1703
1704#if XCHAL_HAVE_WINDOWED
1705
1706    .section .WindowVectors.text, "ax"
1707
1708/*
1709--------------------------------------------------------------------------------
1710Window Overflow Exception for Call4.
1711
1712Invoked if a call[i] referenced a register (a4-a15)
1713that contains data from ancestor call[j];
1714call[j] had done a call4 to call[j+1].
1715On entry here:
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--------------------------------------------------------------------------------
1721*/
1722
1723    .org    0x0
1724    .global _WindowOverflow4
1725_WindowOverflow4:
1726
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 */
1732
1733/*
1734--------------------------------------------------------------------------------
1735Window Underflow Exception for Call4
1736
1737Invoked by RETW returning from call[i+1] to call[i]
1738where call[i]'s registers must be reloaded (not live in ARs);
1739where call[i] had done a call4 to call[i+1].
1740On entry here:
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--------------------------------------------------------------------------------
1746*/
1747
1748    .org    0x40
1749    .global _WindowUnderflow4
1750_WindowUnderflow4:
1751
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 */
1756    rfwu
1757
1758/*
1759--------------------------------------------------------------------------------
1760Handle alloca exception generated by interruptee executing 'movsp'.
1761This uses space between the window vectors, so is essentially "free".
1762All interruptee's regs are intact except a0 which is saved in EXCSAVE_1,
1763and PS.EXCM has been set by the exception hardware (can't be interrupted).
1764The fact the alloca exception was taken means the registers associated with
1765the base-save area have been spilled and will be restored by the underflow
1766handler, so those 4 registers are available for scratch.
1767The code is optimized to avoid unaligned branches and minimize cache misses.
1768--------------------------------------------------------------------------------
1769*/
1770
1771    .align  4
1772    .global _xt_alloca_exc
1773_xt_alloca_exc:
1774
1775    rsr     a0, WINDOWBASE  /* grab WINDOWBASE before rotw changes it */
1776    rotw    -1              /* WINDOWBASE goes to a4, new a0-a3 are scratch */
1777    rsr     a2, PS
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 */
1784    rsync
1785
1786    _bbci.l a4, 31, _WindowUnderflow4
1787    rotw    -1              /* original a0 goes to a8 */
1788    _bbci.l a8, 30, _WindowUnderflow8
1789    rotw    -1
1790    j               _WindowUnderflow12
1791
1792/*
1793--------------------------------------------------------------------------------
1794Window Overflow Exception for Call8
1795
1796Invoked if a call[i] referenced a register (a4-a15)
1797that contains data from ancestor call[j];
1798call[j] had done a call8 to call[j+1].
1799On entry here:
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--------------------------------------------------------------------------------
1805*/
1806
1807    .org    0x80
1808    .global _WindowOverflow8
1809_WindowOverflow8:
1810
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 */
1822
1823/*
1824--------------------------------------------------------------------------------
1825Window Underflow Exception for Call8
1826
1827Invoked by RETW returning from call[i+1] to call[i]
1828where call[i]'s registers must be reloaded (not live in ARs);
1829where call[i] had done a call8 to call[i+1].
1830On entry here:
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--------------------------------------------------------------------------------
1836*/
1837
1838    .org    0xC0
1839    .global _WindowUnderflow8
1840_WindowUnderflow8:
1841
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 */
1852    rfwu
1853
1854/*
1855--------------------------------------------------------------------------------
1856Window Overflow Exception for Call12
1857
1858Invoked if a call[i] referenced a register (a4-a15)
1859that contains data from ancestor call[j];
1860call[j] had done a call12 to call[j+1].
1861On entry here:
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--------------------------------------------------------------------------------
1867*/
1868
1869    .org    0x100
1870    .global _WindowOverflow12
1871_WindowOverflow12:
1872
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 */
1888
1889/*
1890--------------------------------------------------------------------------------
1891Window Underflow Exception for Call12
1892
1893Invoked by RETW returning from call[i+1] to call[i]
1894where call[i]'s registers must be reloaded (not live in ARs);
1895where call[i] had done a call12 to call[i+1].
1896On entry here:
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--------------------------------------------------------------------------------
1902*/
1903
1904    .org 0x140
1905    .global _WindowUnderflow12
1906_WindowUnderflow12:
1907
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 */
1922    rfwu
1923
1924#endif /* XCHAL_HAVE_WINDOWED */
1925