mirror of
				https://github.com/FreeRTOS/FreeRTOS-Kernel.git
				synced 2025-11-04 11:09:01 +01:00 
			
		
		
		
	* Xtensa_ESP32: Added esp-idf v4.4.2 specific changes * Xtensa_ESP32: Updated SPDX license identifiers
		
			
				
	
	
		
			2068 lines
		
	
	
		
			79 KiB
		
	
	
	
		
			ArmAsm
		
	
	
	
	
	
			
		
		
	
	
			2068 lines
		
	
	
		
			79 KiB
		
	
	
	
		
			ArmAsm
		
	
	
	
	
	
/*
 | 
						|
 * SPDX-FileCopyrightText: 2015-2019 Cadence Design Systems, Inc.
 | 
						|
 *
 | 
						|
 * SPDX-License-Identifier: MIT
 | 
						|
 *
 | 
						|
 * SPDX-FileContributor: 2016-2022 Espressif Systems (Shanghai) CO LTD
 | 
						|
 */
 | 
						|
/*
 | 
						|
 * Copyright (c) 2015-2019 Cadence Design Systems, Inc.
 | 
						|
 *
 | 
						|
 * Permission is hereby granted, free of charge, to any person obtaining
 | 
						|
 * a copy of this software and associated documentation files (the
 | 
						|
 * "Software"), to deal in the Software without restriction, including
 | 
						|
 * without limitation the rights to use, copy, modify, merge, publish,
 | 
						|
 * distribute, sublicense, and/or sell copies of the Software, and to
 | 
						|
 * permit persons to whom the Software is furnished to do so, subject to
 | 
						|
 * the following conditions:
 | 
						|
 *
 | 
						|
 * The above copyright notice and this permission notice shall be included
 | 
						|
 * in all copies or substantial portions of the Software.
 | 
						|
 *
 | 
						|
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 | 
						|
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 | 
						|
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 | 
						|
 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
 | 
						|
 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
 | 
						|
 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
 | 
						|
 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 | 
						|
 */
 | 
						|
 | 
						|
/*******************************************************************************
 | 
						|
--------------------------------------------------------------------------------
 | 
						|
 | 
						|
        XTENSA VECTORS AND LOW LEVEL HANDLERS FOR AN RTOS
 | 
						|
 | 
						|
  Xtensa low level exception and interrupt vectors and handlers for an RTOS.
 | 
						|
 | 
						|
  Interrupt handlers and user exception handlers support interaction with
 | 
						|
  the RTOS by calling XT_RTOS_INT_ENTER and XT_RTOS_INT_EXIT before and
 | 
						|
  after user's specific interrupt handlers. These macros are defined in
 | 
						|
  xtensa_<rtos>.h to call suitable functions in a specific RTOS.
 | 
						|
 | 
						|
  Users can install application-specific interrupt handlers for low and
 | 
						|
  medium level interrupts, by calling xt_set_interrupt_handler(). These
 | 
						|
  handlers can be written in C, and must obey C calling convention. The
 | 
						|
  handler table is indexed by the interrupt number. Each handler may be
 | 
						|
  provided with an argument.
 | 
						|
 | 
						|
  Note that the system timer interrupt is handled specially, and is
 | 
						|
  dispatched to the RTOS-specific handler. This timer cannot be hooked
 | 
						|
  by application code.
 | 
						|
 | 
						|
  Optional hooks are also provided to install a handler per level at
 | 
						|
  run-time, made available by compiling this source file with
 | 
						|
  '-DXT_INTEXC_HOOKS' (useful for automated testing).
 | 
						|
 | 
						|
!!  This file is a template that usually needs to be modified to handle       !!
 | 
						|
!!  application specific interrupts. Search USER_EDIT for helpful comments    !!
 | 
						|
!!  on where to insert handlers and how to write them.                        !!
 | 
						|
 | 
						|
  Users can also install application-specific exception handlers in the
 | 
						|
  same way, by calling xt_set_exception_handler(). One handler slot is
 | 
						|
  provided for each exception type. Note that some exceptions are handled
 | 
						|
  by the porting layer itself, and cannot be taken over by application
 | 
						|
  code in this manner. These are the alloca, syscall, and coprocessor
 | 
						|
  exceptions.
 | 
						|
 | 
						|
  The exception handlers can be written in C, and must follow C calling
 | 
						|
  convention. Each handler is passed a pointer to an exception frame as
 | 
						|
  its single argument. The exception frame is created on the stack, and
 | 
						|
  holds the saved context of the thread that took the exception. If the
 | 
						|
  handler returns, the context will be restored and the instruction that
 | 
						|
  caused the exception will be retried. If the handler makes any changes
 | 
						|
  to the saved state in the exception frame, the changes will be applied
 | 
						|
  when restoring the context.
 | 
						|
 | 
						|
  Because Xtensa is a configurable architecture, this port supports all user
 | 
						|
  generated configurations (except restrictions stated in the release notes).
 | 
						|
  This is accomplished by conditional compilation using macros and functions
 | 
						|
  defined in the Xtensa HAL (hardware adaptation layer) for your configuration.
 | 
						|
  Only the relevant parts of this file will be included in your RTOS build.
 | 
						|
  For example, this file provides interrupt vector templates for all types and
 | 
						|
  all priority levels, but only the ones in your configuration are built.
 | 
						|
 | 
						|
  NOTES on the use of 'call0' for long jumps instead of 'j':
 | 
						|
   1. This file should be assembled with the -mlongcalls option to xt-xcc.
 | 
						|
   2. The -mlongcalls compiler option causes 'call0 dest' to be expanded to
 | 
						|
      a sequence 'l32r a0, dest' 'callx0 a0' which works regardless of the
 | 
						|
      distance from the call to the destination. The linker then relaxes
 | 
						|
      it back to 'call0 dest' if it determines that dest is within range.
 | 
						|
      This allows more flexibility in locating code without the performance
 | 
						|
      overhead of the 'l32r' literal data load in cases where the destination
 | 
						|
      is in range of 'call0'. There is an additional benefit in that 'call0'
 | 
						|
      has a longer range than 'j' due to the target being word-aligned, so
 | 
						|
      the 'l32r' sequence is less likely needed.
 | 
						|
   3. The use of 'call0' with -mlongcalls requires that register a0 not be
 | 
						|
      live at the time of the call, which is always the case for a function
 | 
						|
      call but needs to be ensured if 'call0' is used as a jump in lieu of 'j'.
 | 
						|
   4. This use of 'call0' is independent of the C function call ABI.
 | 
						|
 | 
						|
*******************************************************************************/
 | 
						|
 | 
						|
#include "xtensa_rtos.h"
 | 
						|
#include "esp_idf_version.h"
 | 
						|
#if (ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(4, 2, 0))
 | 
						|
#include "esp_panic.h"
 | 
						|
#else
 | 
						|
#include "esp_private/panic_reason.h"
 | 
						|
#endif /* ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(4, 2, 0) */
 | 
						|
#include "sdkconfig.h"
 | 
						|
#include "soc/soc.h"
 | 
						|
 | 
						|
/*
 | 
						|
  Define for workaround: pin no-cpu-affinity tasks to a cpu when fpu is used.
 | 
						|
  Please change this when the tcb structure is changed
 | 
						|
*/
 | 
						|
#define TASKTCB_XCOREID_OFFSET (0x38+configMAX_TASK_NAME_LEN+3)&~3
 | 
						|
.extern pxCurrentTCB
 | 
						|
 | 
						|
/*
 | 
						|
--------------------------------------------------------------------------------
 | 
						|
    In order for backtracing to be able to trace from the pre-exception stack
 | 
						|
    across to the exception stack (including nested interrupts), we need to create
 | 
						|
    a pseudo base-save area to make it appear like the exception dispatcher was
 | 
						|
    triggered by a CALL4 from the pre-exception code. In reality, the exception
 | 
						|
    dispatcher uses the same window as pre-exception code, and only CALL0s are
 | 
						|
    used within the exception dispatcher.
 | 
						|
 | 
						|
    To create the pseudo base-save area, we need to store a copy of the pre-exception's
 | 
						|
    base save area (a0 to a4) below the exception dispatcher's SP. EXCSAVE_x will
 | 
						|
    be used to store a copy of the SP that points to the interrupted code's exception
 | 
						|
    frame just in case the exception dispatcher's SP does not point to the exception
 | 
						|
    frame (which is the case when switching from task to interrupt stack).
 | 
						|
 | 
						|
    Clearing the pseudo base-save area is uncessary as the interrupt dispatcher
 | 
						|
    will restore the current SP to that of the pre-exception SP.
 | 
						|
--------------------------------------------------------------------------------
 | 
						|
*/
 | 
						|
#ifdef CONFIG_FREERTOS_INTERRUPT_BACKTRACE
 | 
						|
#define XT_DEBUG_BACKTRACE    1
 | 
						|
#endif
 | 
						|
 | 
						|
 | 
						|
/*
 | 
						|
--------------------------------------------------------------------------------
 | 
						|
  Defines used to access _xtos_interrupt_table.
 | 
						|
--------------------------------------------------------------------------------
 | 
						|
*/
 | 
						|
#define XIE_HANDLER     0
 | 
						|
#define XIE_ARG         4
 | 
						|
#define XIE_SIZE        8
 | 
						|
 | 
						|
 | 
						|
/*
 | 
						|
  Macro get_percpu_entry_for - convert a per-core ID into a multicore entry.
 | 
						|
  Basically does reg=reg*portNUM_PROCESSORS+current_core_id
 | 
						|
  Multiple versions here to optimize for specific portNUM_PROCESSORS values.
 | 
						|
*/
 | 
						|
    .macro get_percpu_entry_for reg scratch
 | 
						|
#if (portNUM_PROCESSORS == 1)
 | 
						|
    /* No need to do anything */
 | 
						|
#elif  (portNUM_PROCESSORS == 2)
 | 
						|
    /* Optimized 2-core code. */
 | 
						|
    getcoreid \scratch
 | 
						|
    addx2 \reg,\reg,\scratch
 | 
						|
#else
 | 
						|
    /* Generalized n-core code. Untested! */
 | 
						|
    movi \scratch,portNUM_PROCESSORS
 | 
						|
    mull \scratch,\reg,\scratch
 | 
						|
    getcoreid \reg
 | 
						|
    add \reg,\scratch,\reg
 | 
						|
#endif
 | 
						|
   .endm
 | 
						|
/*
 | 
						|
--------------------------------------------------------------------------------
 | 
						|
  Macro extract_msb - return the input with only the highest bit set.
 | 
						|
 | 
						|
  Input  : "ain"  - Input value, clobbered.
 | 
						|
  Output : "aout" - Output value, has only one bit set, MSB of "ain".
 | 
						|
  The two arguments must be different AR registers.
 | 
						|
--------------------------------------------------------------------------------
 | 
						|
*/
 | 
						|
 | 
						|
    .macro  extract_msb     aout ain
 | 
						|
1:
 | 
						|
    addi    \aout, \ain, -1         /* aout = ain - 1        */
 | 
						|
    and     \ain, \ain, \aout       /* ain  = ain & aout     */
 | 
						|
    bnez    \ain, 1b                /* repeat until ain == 0 */
 | 
						|
    addi    \aout, \aout, 1         /* return aout + 1       */
 | 
						|
    .endm
 | 
						|
 | 
						|
/*
 | 
						|
--------------------------------------------------------------------------------
 | 
						|
  Macro dispatch_c_isr - dispatch interrupts to user ISRs.
 | 
						|
  This will dispatch to user handlers (if any) that are registered in the
 | 
						|
  XTOS dispatch table (_xtos_interrupt_table). These handlers would have
 | 
						|
  been registered by calling _xtos_set_interrupt_handler(). There is one
 | 
						|
  exception - the timer interrupt used by the OS will not be dispatched
 | 
						|
  to a user handler - this must be handled by the caller of this macro.
 | 
						|
 | 
						|
  Level triggered and software interrupts are automatically deasserted by
 | 
						|
  this code.
 | 
						|
 | 
						|
  ASSUMPTIONS:
 | 
						|
    -- PS.INTLEVEL is set to "level" at entry
 | 
						|
    -- PS.EXCM = 0, C calling enabled
 | 
						|
 | 
						|
  NOTE: For CALL0 ABI, a12-a15 have not yet been saved.
 | 
						|
 | 
						|
  NOTE: This macro will use registers a0 and a2-a7. The arguments are:
 | 
						|
    level -- interrupt level
 | 
						|
    mask  -- interrupt bitmask for this level
 | 
						|
--------------------------------------------------------------------------------
 | 
						|
*/
 | 
						|
 | 
						|
    .macro  dispatch_c_isr    level  mask
 | 
						|
 | 
						|
    #ifdef CONFIG_PM_TRACE
 | 
						|
    movi a6, 0 /* = ESP_PM_TRACE_IDLE */
 | 
						|
    getcoreid a7
 | 
						|
    call4 esp_pm_trace_exit
 | 
						|
    #endif // CONFIG_PM_TRACE
 | 
						|
 | 
						|
    /* Get mask of pending, enabled interrupts at this level into a2. */
 | 
						|
 | 
						|
.L_xt_user_int_&level&:
 | 
						|
    rsr     a2, INTENABLE
 | 
						|
    rsr     a3, INTERRUPT
 | 
						|
    movi    a4, \mask
 | 
						|
    and     a2, a2, a3
 | 
						|
    and     a2, a2, a4
 | 
						|
    beqz    a2, 9f                          /* nothing to do */
 | 
						|
 | 
						|
    /* This bit of code provides a nice debug backtrace in the debugger.
 | 
						|
       It does take a few more instructions, so undef XT_DEBUG_BACKTRACE
 | 
						|
       if you want to save the cycles.
 | 
						|
       At this point, the exception frame should have been allocated and filled,
 | 
						|
       and current sp points to the interrupt stack (for non-nested interrupt)
 | 
						|
       or below the allocated exception frame (for nested interrupts). Copy the
 | 
						|
       pre-exception's base save area below the current SP.
 | 
						|
    */
 | 
						|
    #ifdef XT_DEBUG_BACKTRACE
 | 
						|
    #ifndef __XTENSA_CALL0_ABI__
 | 
						|
    #if (ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 2, 0))
 | 
						|
    rsr     a0, EXCSAVE_1 + \level - 1      /* Get exception frame pointer stored in EXCSAVE_x */
 | 
						|
    l32i    a3, a0, XT_STK_A0               /* Copy pre-exception a0 (return address) */
 | 
						|
    s32e    a3, a1, -16
 | 
						|
    l32i    a3, a0, XT_STK_A1               /* Copy pre-exception a1 (stack pointer) */
 | 
						|
    s32e    a3, a1, -12
 | 
						|
    #endif /* ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 2, 0) */
 | 
						|
    /* Backtracing only needs a0 and a1, no need to create full base save area.
 | 
						|
       Also need to change current frame's return address to point to pre-exception's
 | 
						|
       last run instruction.
 | 
						|
     */
 | 
						|
    rsr     a0, EPC_1 + \level - 1          /* return address */
 | 
						|
    movi    a4, 0xC0000000                  /* constant with top 2 bits set (call size) */
 | 
						|
    or      a0, a0, a4                      /* set top 2 bits */
 | 
						|
    addx2   a0, a4, a0                      /* clear top bit -- simulating call4 size   */
 | 
						|
    #endif
 | 
						|
    #endif
 | 
						|
 | 
						|
    #ifdef CONFIG_PM_ENABLE
 | 
						|
    call4 esp_pm_impl_isr_hook
 | 
						|
    #endif
 | 
						|
 | 
						|
    #ifdef XT_INTEXC_HOOKS
 | 
						|
    /* Call interrupt hook if present to (pre)handle interrupts. */
 | 
						|
    movi    a4, _xt_intexc_hooks
 | 
						|
    l32i    a4, a4, \level << 2
 | 
						|
    beqz    a4, 2f
 | 
						|
    #ifdef __XTENSA_CALL0_ABI__
 | 
						|
    callx0  a4
 | 
						|
    beqz    a2, 9f
 | 
						|
    #else
 | 
						|
    mov     a6, a2
 | 
						|
    callx4  a4
 | 
						|
    beqz    a6, 9f
 | 
						|
    mov     a2, a6
 | 
						|
    #endif
 | 
						|
2:
 | 
						|
    #endif
 | 
						|
 | 
						|
    /* Now look up in the dispatch table and call user ISR if any. */
 | 
						|
    /* If multiple bits are set then MSB has highest priority.     */
 | 
						|
 | 
						|
    extract_msb  a4, a2                     /* a4 = MSB of a2, a2 trashed */
 | 
						|
 | 
						|
    #ifdef XT_USE_SWPRI
 | 
						|
    /* Enable all interrupts at this level that are numerically higher
 | 
						|
       than the one we just selected, since they are treated as higher
 | 
						|
       priority.
 | 
						|
    */
 | 
						|
    movi    a3, \mask                       /* a3 = all interrupts at this level */
 | 
						|
    add     a2, a4, a4                      /* a2 = a4 << 1 */
 | 
						|
    addi    a2, a2, -1                      /* a2 = mask of 1's <= a4 bit */
 | 
						|
    and     a2, a2, a3                      /* a2 = mask of all bits <= a4 at this level */
 | 
						|
    movi    a3, _xt_intdata
 | 
						|
    l32i    a6, a3, 4                       /* a6 = _xt_vpri_mask */
 | 
						|
    neg     a2, a2
 | 
						|
    addi    a2, a2, -1                      /* a2 = mask to apply */
 | 
						|
    and     a5, a6, a2                      /* mask off all bits <= a4 bit */
 | 
						|
    s32i    a5, a3, 4                       /* update _xt_vpri_mask */
 | 
						|
    rsr     a3, INTENABLE
 | 
						|
    and     a3, a3, a2                      /* mask off all bits <= a4 bit */
 | 
						|
    wsr     a3, INTENABLE
 | 
						|
    rsil    a3, \level - 1                  /* lower interrupt level by 1 */
 | 
						|
    #endif
 | 
						|
 | 
						|
    #ifdef XT_RTOS_TIMER_INT
 | 
						|
    movi    a3, XT_TIMER_INTEN              /* a3 = timer interrupt bit */
 | 
						|
    wsr     a4, INTCLEAR                    /* clear sw or edge-triggered interrupt */
 | 
						|
    beq     a3, a4, 7f                      /* if timer interrupt then skip table */
 | 
						|
    #else
 | 
						|
    wsr     a4, INTCLEAR                    /* clear sw or edge-triggered interrupt */
 | 
						|
    #endif // XT_RTOS_TIMER_INT
 | 
						|
 | 
						|
    find_ms_setbit a3, a4, a3, 0            /* a3 = interrupt number */
 | 
						|
 | 
						|
    get_percpu_entry_for a3, a12
 | 
						|
    movi    a4, _xt_interrupt_table
 | 
						|
    addx8   a3, a3, a4                      /* a3 = address of interrupt table entry */
 | 
						|
    l32i    a4, a3, XIE_HANDLER             /* a4 = handler address */
 | 
						|
    #ifdef __XTENSA_CALL0_ABI__
 | 
						|
    mov     a12, a6                         /* save in callee-saved reg */
 | 
						|
    l32i    a2, a3, XIE_ARG                 /* a2 = handler arg */
 | 
						|
    callx0  a4                              /* call handler */
 | 
						|
    mov     a2, a12
 | 
						|
    #else
 | 
						|
    mov     a2, a6                          /* save in windowed reg */
 | 
						|
    l32i    a6, a3, XIE_ARG                 /* a6 = handler arg */
 | 
						|
    callx4  a4                              /* call handler */
 | 
						|
    #endif
 | 
						|
 | 
						|
    #ifdef XT_USE_SWPRI
 | 
						|
    j       8f
 | 
						|
    #else
 | 
						|
    j       .L_xt_user_int_&level&          /* check for more interrupts */
 | 
						|
    #endif
 | 
						|
    #ifdef XT_RTOS_TIMER_INT
 | 
						|
7:
 | 
						|
 | 
						|
    .ifeq XT_TIMER_INTPRI - \level
 | 
						|
.L_xt_user_int_timer_&level&:
 | 
						|
    /*
 | 
						|
    Interrupt handler for the RTOS tick timer if at this level.
 | 
						|
    We'll be reading the interrupt state again after this call
 | 
						|
    so no need to preserve any registers except a6 (vpri_mask).
 | 
						|
    */
 | 
						|
 | 
						|
    #ifdef __XTENSA_CALL0_ABI__
 | 
						|
    mov     a12, a6
 | 
						|
    call0   XT_RTOS_TIMER_INT
 | 
						|
    mov     a2, a12
 | 
						|
    #else
 | 
						|
    mov     a2, a6
 | 
						|
    call4   XT_RTOS_TIMER_INT
 | 
						|
    #endif
 | 
						|
    .endif
 | 
						|
    #endif // XT_RTOS_TIMER_INT
 | 
						|
 | 
						|
    #ifdef XT_USE_SWPRI
 | 
						|
    j       8f
 | 
						|
    #else
 | 
						|
    j       .L_xt_user_int_&level&          /* check for more interrupts */
 | 
						|
    #endif
 | 
						|
 | 
						|
    #ifdef XT_USE_SWPRI
 | 
						|
8:
 | 
						|
    /* Restore old value of _xt_vpri_mask from a2. Also update INTENABLE from
 | 
						|
       virtual _xt_intenable which _could_ have changed during interrupt
 | 
						|
       processing. */
 | 
						|
 | 
						|
    movi    a3, _xt_intdata
 | 
						|
    l32i    a4, a3, 0                       /* a4 = _xt_intenable    */
 | 
						|
    s32i    a2, a3, 4                       /* update _xt_vpri_mask  */
 | 
						|
    and     a4, a4, a2                      /* a4 = masked intenable */
 | 
						|
    wsr     a4, INTENABLE                   /* update INTENABLE      */
 | 
						|
    #endif
 | 
						|
 | 
						|
9:
 | 
						|
    /* done */
 | 
						|
 | 
						|
    .endm
 | 
						|
 | 
						|
#if (ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(4, 2, 0))
 | 
						|
/*
 | 
						|
--------------------------------------------------------------------------------
 | 
						|
  Panic handler.
 | 
						|
  Should be reached by call0 (preferable) or jump only. If call0, a0 says where
 | 
						|
  from. If on simulator, display panic message and abort, else loop indefinitely.
 | 
						|
--------------------------------------------------------------------------------
 | 
						|
*/
 | 
						|
 | 
						|
    .section .iram1,"ax"
 | 
						|
    .global panicHandler
 | 
						|
 | 
						|
    .global     _xt_panic
 | 
						|
    .type       _xt_panic,@function
 | 
						|
    .align      4
 | 
						|
    .literal_position
 | 
						|
    .align      4
 | 
						|
 | 
						|
_xt_panic:
 | 
						|
    /* Allocate exception frame and save minimal context. */
 | 
						|
    mov     a0, sp
 | 
						|
    addi    sp, sp, -XT_STK_FRMSZ
 | 
						|
    s32i    a0, sp, XT_STK_A1
 | 
						|
    #if XCHAL_HAVE_WINDOWED
 | 
						|
    s32e    a0, sp, -12                     /* for debug backtrace */
 | 
						|
    #endif
 | 
						|
    rsr     a0, PS                          /* save interruptee's PS */
 | 
						|
    s32i    a0, sp, XT_STK_PS
 | 
						|
    rsr     a0, EPC_1                       /* save interruptee's PC */
 | 
						|
    s32i    a0, sp, XT_STK_PC
 | 
						|
    #if XCHAL_HAVE_WINDOWED
 | 
						|
    s32e    a0, sp, -16                     /* for debug backtrace */
 | 
						|
    #endif
 | 
						|
    s32i    a12, sp, XT_STK_A12             /* _xt_context_save requires A12- */
 | 
						|
    s32i    a13, sp, XT_STK_A13             /* A13 to have already been saved */
 | 
						|
    call0   _xt_context_save
 | 
						|
 | 
						|
    /* Save exc cause and vaddr into exception frame */
 | 
						|
    rsr     a0, EXCCAUSE
 | 
						|
    s32i    a0, sp, XT_STK_EXCCAUSE
 | 
						|
    rsr     a0, EXCVADDR
 | 
						|
    s32i    a0, sp, XT_STK_EXCVADDR
 | 
						|
 | 
						|
    /* _xt_context_save seems to save the current a0, but we need the interuptees a0. Fix this. */
 | 
						|
    rsr     a0, EXCSAVE_1                   /* save interruptee's a0 */
 | 
						|
 | 
						|
    s32i    a0, sp, XT_STK_A0
 | 
						|
 | 
						|
    /* Set up PS for C, disable all interrupts except NMI and debug, and clear EXCM. */
 | 
						|
    movi    a0, PS_INTLEVEL(5) | PS_UM | PS_WOE
 | 
						|
    wsr     a0, PS
 | 
						|
 | 
						|
    //Call panic handler
 | 
						|
    mov     a6,sp
 | 
						|
    call4 panicHandler
 | 
						|
 | 
						|
 | 
						|
    .align 4
 | 
						|
//Call using call0. Prints the hex char in a2. Kills a3, a4, a5
 | 
						|
panic_print_hex:
 | 
						|
    movi a3,0x60000000
 | 
						|
    movi a4,8
 | 
						|
panic_print_hex_loop:
 | 
						|
    l32i a5, a3, 0x1c
 | 
						|
    extui a5, a5, 16, 8
 | 
						|
    bgei a5,64,panic_print_hex_loop
 | 
						|
 | 
						|
    srli a5,a2,28
 | 
						|
    bgei a5,10,panic_print_hex_a
 | 
						|
    addi a5,a5,'0'
 | 
						|
    j panic_print_hex_ok
 | 
						|
panic_print_hex_a:
 | 
						|
    addi a5,a5,'A'-10
 | 
						|
panic_print_hex_ok:
 | 
						|
    s32i a5,a3,0
 | 
						|
    slli a2,a2,4
 | 
						|
 | 
						|
    addi a4,a4,-1
 | 
						|
    bnei a4,0,panic_print_hex_loop
 | 
						|
    movi a5,' '
 | 
						|
    s32i a5,a3,0
 | 
						|
 | 
						|
    ret
 | 
						|
#endif /* ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 2, 0) */
 | 
						|
 | 
						|
 | 
						|
    .section    .rodata, "a"
 | 
						|
    .align      4
 | 
						|
 | 
						|
/*
 | 
						|
--------------------------------------------------------------------------------
 | 
						|
    Hooks to dynamically install handlers for exceptions and interrupts.
 | 
						|
    Allows automated regression frameworks to install handlers per test.
 | 
						|
    Consists of an array of function pointers indexed by interrupt level,
 | 
						|
    with index 0 containing the entry for user exceptions.
 | 
						|
    Initialized with all 0s, meaning no handler is installed at each level.
 | 
						|
    See comment in xtensa_rtos.h for more details.
 | 
						|
 | 
						|
    *WARNING*  This array is for all CPUs, that is, installing a hook for
 | 
						|
    one CPU will install it for all others as well!
 | 
						|
--------------------------------------------------------------------------------
 | 
						|
*/
 | 
						|
 | 
						|
    #ifdef XT_INTEXC_HOOKS
 | 
						|
    .data
 | 
						|
    .global     _xt_intexc_hooks
 | 
						|
    .type       _xt_intexc_hooks,@object
 | 
						|
    .align      4
 | 
						|
 | 
						|
_xt_intexc_hooks:
 | 
						|
    .fill       XT_INTEXC_HOOK_NUM, 4, 0
 | 
						|
    #endif
 | 
						|
 | 
						|
 | 
						|
/*
 | 
						|
--------------------------------------------------------------------------------
 | 
						|
  EXCEPTION AND LEVEL 1 INTERRUPT VECTORS AND LOW LEVEL HANDLERS
 | 
						|
  (except window exception vectors).
 | 
						|
 | 
						|
  Each vector goes at a predetermined location according to the Xtensa
 | 
						|
  hardware configuration, which is ensured by its placement in a special
 | 
						|
  section known to the Xtensa linker support package (LSP). It performs
 | 
						|
  the minimum necessary before jumping to the handler in the .text section.
 | 
						|
 | 
						|
  The corresponding handler goes in the normal .text section. It sets up
 | 
						|
  the appropriate stack frame, saves a few vector-specific registers and
 | 
						|
  calls XT_RTOS_INT_ENTER to save the rest of the interrupted context
 | 
						|
  and enter the RTOS, then sets up a C environment. It then calls the
 | 
						|
  user's interrupt handler code (which may be coded in C) and finally
 | 
						|
  calls XT_RTOS_INT_EXIT to transfer control to the RTOS for scheduling.
 | 
						|
 | 
						|
  While XT_RTOS_INT_EXIT does not return directly to the interruptee,
 | 
						|
  eventually the RTOS scheduler will want to dispatch the interrupted
 | 
						|
  task or handler. The scheduler will return to the exit point that was
 | 
						|
  saved in the interrupt stack frame at XT_STK_EXIT.
 | 
						|
--------------------------------------------------------------------------------
 | 
						|
*/
 | 
						|
 | 
						|
 | 
						|
/*
 | 
						|
--------------------------------------------------------------------------------
 | 
						|
Debug Exception.
 | 
						|
--------------------------------------------------------------------------------
 | 
						|
*/
 | 
						|
 | 
						|
#if XCHAL_HAVE_DEBUG
 | 
						|
 | 
						|
    .begin      literal_prefix .DebugExceptionVector
 | 
						|
    .section    .DebugExceptionVector.text, "ax"
 | 
						|
    .global     _DebugExceptionVector
 | 
						|
    .align      4
 | 
						|
    .global     xt_debugexception
 | 
						|
_DebugExceptionVector:
 | 
						|
    wsr     a0, EXCSAVE+XCHAL_DEBUGLEVEL    /* preserve a0 */
 | 
						|
    call0   xt_debugexception            /* load exception handler */
 | 
						|
 | 
						|
    .end        literal_prefix
 | 
						|
 | 
						|
#endif
 | 
						|
 | 
						|
/*
 | 
						|
--------------------------------------------------------------------------------
 | 
						|
Double Exception.
 | 
						|
Double exceptions are not a normal occurrence. They indicate a bug of some kind.
 | 
						|
--------------------------------------------------------------------------------
 | 
						|
*/
 | 
						|
 | 
						|
#ifdef XCHAL_DOUBLEEXC_VECTOR_VADDR
 | 
						|
 | 
						|
    .begin      literal_prefix .DoubleExceptionVector
 | 
						|
    .section    .DoubleExceptionVector.text, "ax"
 | 
						|
    .global     _DoubleExceptionVector
 | 
						|
    .align      4
 | 
						|
 | 
						|
_DoubleExceptionVector:
 | 
						|
 | 
						|
    #if XCHAL_HAVE_DEBUG
 | 
						|
    break   1, 4                            /* unhandled double exception */
 | 
						|
    #endif
 | 
						|
    movi    a0,PANIC_RSN_DOUBLEEXCEPTION
 | 
						|
    wsr     a0,EXCCAUSE
 | 
						|
    call0   _xt_panic                       /* does not return */
 | 
						|
    rfde                                    /* make a0 point here not later */
 | 
						|
 | 
						|
    .end        literal_prefix
 | 
						|
 | 
						|
#endif /* XCHAL_DOUBLEEXC_VECTOR_VADDR */
 | 
						|
 | 
						|
/*
 | 
						|
--------------------------------------------------------------------------------
 | 
						|
Kernel Exception (including Level 1 Interrupt from kernel mode).
 | 
						|
--------------------------------------------------------------------------------
 | 
						|
*/
 | 
						|
 | 
						|
    .begin      literal_prefix .KernelExceptionVector
 | 
						|
    .section    .KernelExceptionVector.text, "ax"
 | 
						|
    .global     _KernelExceptionVector
 | 
						|
    .align      4
 | 
						|
 | 
						|
_KernelExceptionVector:
 | 
						|
 | 
						|
    wsr     a0, EXCSAVE_1                   /* preserve a0 */
 | 
						|
    call0   _xt_kernel_exc                  /* kernel exception handler */
 | 
						|
    /* never returns here - call0 is used as a jump (see note at top) */
 | 
						|
 | 
						|
    .end        literal_prefix
 | 
						|
 | 
						|
    .section .iram1,"ax"
 | 
						|
    .align      4
 | 
						|
 | 
						|
_xt_kernel_exc:
 | 
						|
    #if XCHAL_HAVE_DEBUG
 | 
						|
    break   1, 0                            /* unhandled kernel exception */
 | 
						|
    #endif
 | 
						|
    movi    a0,PANIC_RSN_KERNELEXCEPTION
 | 
						|
    wsr     a0,EXCCAUSE
 | 
						|
    call0   _xt_panic                       /* does not return */
 | 
						|
    rfe                                     /* make a0 point here not there */
 | 
						|
 | 
						|
 | 
						|
/*
 | 
						|
--------------------------------------------------------------------------------
 | 
						|
User Exception (including Level 1 Interrupt from user mode).
 | 
						|
--------------------------------------------------------------------------------
 | 
						|
*/
 | 
						|
 | 
						|
    .begin      literal_prefix .UserExceptionVector
 | 
						|
    .section    .UserExceptionVector.text, "ax"
 | 
						|
    .global     _UserExceptionVector
 | 
						|
    .type       _UserExceptionVector,@function
 | 
						|
    .align      4
 | 
						|
 | 
						|
_UserExceptionVector:
 | 
						|
 | 
						|
    wsr     a0, EXCSAVE_1                   /* preserve a0 */
 | 
						|
    call0   _xt_user_exc                    /* user exception handler */
 | 
						|
    /* never returns here - call0 is used as a jump (see note at top) */
 | 
						|
 | 
						|
    .end        literal_prefix
 | 
						|
 | 
						|
/*
 | 
						|
--------------------------------------------------------------------------------
 | 
						|
  Insert some waypoints for jumping beyond the signed 8-bit range of
 | 
						|
  conditional branch instructions, so the conditional branchces to specific
 | 
						|
  exception handlers are not taken in the mainline. Saves some cycles in the
 | 
						|
  mainline.
 | 
						|
--------------------------------------------------------------------------------
 | 
						|
*/
 | 
						|
 | 
						|
#ifdef CONFIG_ESP32_IRAM_AS_8BIT_ACCESSIBLE_MEMORY
 | 
						|
    .global   LoadStoreErrorHandler
 | 
						|
    .global   AlignmentErrorHandler
 | 
						|
#endif
 | 
						|
 | 
						|
    .section .iram1,"ax"
 | 
						|
 | 
						|
    #if XCHAL_HAVE_WINDOWED
 | 
						|
    .align      4
 | 
						|
_xt_to_alloca_exc:
 | 
						|
    call0   _xt_alloca_exc                  /* in window vectors section */
 | 
						|
    /* never returns here - call0 is used as a jump (see note at top) */
 | 
						|
    #endif
 | 
						|
 | 
						|
    .align      4
 | 
						|
_xt_to_syscall_exc:
 | 
						|
    call0   _xt_syscall_exc
 | 
						|
    /* never returns here - call0 is used as a jump (see note at top) */
 | 
						|
 | 
						|
    #if XCHAL_CP_NUM > 0
 | 
						|
    .align      4
 | 
						|
_xt_to_coproc_exc:
 | 
						|
    call0   _xt_coproc_exc
 | 
						|
    /* never returns here - call0 is used as a jump (see note at top) */
 | 
						|
    #endif
 | 
						|
 | 
						|
#ifdef CONFIG_ESP32_IRAM_AS_8BIT_ACCESSIBLE_MEMORY
 | 
						|
    .align      4
 | 
						|
_call_loadstore_handler:
 | 
						|
    call0   LoadStoreErrorHandler
 | 
						|
    /* This will return only if wrong opcode or address out of range*/
 | 
						|
    j       .LS_exit
 | 
						|
 | 
						|
    .align      4
 | 
						|
_call_alignment_handler:
 | 
						|
    call0   AlignmentErrorHandler
 | 
						|
    /* This will return only if wrong opcode or address out of range*/
 | 
						|
    addi    a0, a0, 1
 | 
						|
    j       .LS_exit
 | 
						|
#endif
 | 
						|
 | 
						|
/*
 | 
						|
--------------------------------------------------------------------------------
 | 
						|
  User exception handler.
 | 
						|
--------------------------------------------------------------------------------
 | 
						|
*/
 | 
						|
 | 
						|
    .type       _xt_user_exc,@function
 | 
						|
    .align      4
 | 
						|
 | 
						|
_xt_user_exc:
 | 
						|
 | 
						|
    /* If level 1 interrupt then jump to the dispatcher */
 | 
						|
    rsr     a0, EXCCAUSE
 | 
						|
    beqi    a0, EXCCAUSE_LEVEL1INTERRUPT, _xt_lowint1
 | 
						|
 | 
						|
    /* Handle any coprocessor exceptions. Rely on the fact that exception
 | 
						|
       numbers above EXCCAUSE_CP0_DISABLED all relate to the coprocessors.
 | 
						|
    */
 | 
						|
    #if XCHAL_CP_NUM > 0
 | 
						|
    bgeui   a0, EXCCAUSE_CP0_DISABLED, _xt_to_coproc_exc
 | 
						|
    #endif
 | 
						|
 | 
						|
    /* Handle alloca and syscall exceptions */
 | 
						|
    #if XCHAL_HAVE_WINDOWED
 | 
						|
    beqi    a0, EXCCAUSE_ALLOCA,  _xt_to_alloca_exc
 | 
						|
    #endif
 | 
						|
    beqi    a0, EXCCAUSE_SYSCALL, _xt_to_syscall_exc
 | 
						|
 | 
						|
#ifdef CONFIG_ESP32_IRAM_AS_8BIT_ACCESSIBLE_MEMORY
 | 
						|
    beqi    a0, EXCCAUSE_LOAD_STORE_ERROR, _call_loadstore_handler
 | 
						|
 | 
						|
    addi    a0, a0, -1
 | 
						|
    beqi    a0, 8, _call_alignment_handler
 | 
						|
    addi    a0, a0, 1
 | 
						|
.LS_exit:
 | 
						|
#endif
 | 
						|
 | 
						|
    /* Handle all other exceptions. All can have user-defined handlers. */
 | 
						|
    /* NOTE: we'll stay on the user stack for exception handling.       */
 | 
						|
 | 
						|
    /* Allocate exception frame and save minimal context. */
 | 
						|
    mov     a0, sp
 | 
						|
    addi    sp, sp, -XT_STK_FRMSZ
 | 
						|
    s32i    a0, sp, XT_STK_A1
 | 
						|
    #if XCHAL_HAVE_WINDOWED
 | 
						|
    s32e    a0, sp, -12                     /* for debug backtrace */
 | 
						|
    #endif
 | 
						|
    rsr     a0, PS                          /* save interruptee's PS */
 | 
						|
    s32i    a0, sp, XT_STK_PS
 | 
						|
    rsr     a0, EPC_1                       /* save interruptee's PC */
 | 
						|
    s32i    a0, sp, XT_STK_PC
 | 
						|
    #if XCHAL_HAVE_WINDOWED
 | 
						|
    s32e    a0, sp, -16                     /* for debug backtrace */
 | 
						|
    #endif
 | 
						|
    s32i    a12, sp, XT_STK_A12             /* _xt_context_save requires A12- */
 | 
						|
    s32i    a13, sp, XT_STK_A13             /* A13 to have already been saved */
 | 
						|
    call0   _xt_context_save
 | 
						|
 | 
						|
    /* Save exc cause and vaddr into exception frame */
 | 
						|
    rsr     a0, EXCCAUSE
 | 
						|
    s32i    a0, sp, XT_STK_EXCCAUSE
 | 
						|
    rsr     a0, EXCVADDR
 | 
						|
    s32i    a0, sp, XT_STK_EXCVADDR
 | 
						|
 | 
						|
    /* _xt_context_save seems to save the current a0, but we need the interuptees a0. Fix this. */
 | 
						|
    rsr     a0, EXCSAVE_1                   /* save interruptee's a0 */
 | 
						|
    s32i    a0, sp, XT_STK_A0
 | 
						|
 | 
						|
    /* Set up PS for C, reenable debug and NMI interrupts, and clear EXCM. */
 | 
						|
    #ifdef __XTENSA_CALL0_ABI__
 | 
						|
    movi    a0, PS_INTLEVEL(XCHAL_DEBUGLEVEL - 2) | PS_UM
 | 
						|
    #else
 | 
						|
    movi    a0, PS_INTLEVEL(XCHAL_DEBUGLEVEL - 2) | PS_UM | PS_WOE
 | 
						|
    #endif
 | 
						|
    wsr     a0, PS
 | 
						|
 | 
						|
    /*
 | 
						|
        Create pseudo base save area. At this point, sp is still pointing to the
 | 
						|
        allocated and filled exception stack frame.
 | 
						|
    */
 | 
						|
    #ifdef XT_DEBUG_BACKTRACE
 | 
						|
    #ifndef __XTENSA_CALL0_ABI__
 | 
						|
    #if (ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 2, 0))
 | 
						|
    l32i    a3, sp, XT_STK_A0               /* Copy pre-exception a0 (return address) */
 | 
						|
    s32e    a3, sp, -16
 | 
						|
    l32i    a3, sp, XT_STK_A1               /* Copy pre-exception a1 (stack pointer) */
 | 
						|
    s32e    a3, sp, -12
 | 
						|
    #endif /* ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 2, 0) */
 | 
						|
    rsr     a0, EPC_1                       /* return address for debug backtrace */
 | 
						|
    movi    a5, 0xC0000000                  /* constant with top 2 bits set (call size) */
 | 
						|
    rsync                                   /* wait for WSR.PS to complete */
 | 
						|
    or      a0, a0, a5                      /* set top 2 bits */
 | 
						|
    addx2   a0, a5, a0                      /* clear top bit -- thus simulating call4 size */
 | 
						|
    #else
 | 
						|
    rsync                                   /* wait for WSR.PS to complete */
 | 
						|
    #endif
 | 
						|
    #endif
 | 
						|
 | 
						|
    rsr     a2, EXCCAUSE                    /* recover exc cause */
 | 
						|
 | 
						|
    #ifdef XT_INTEXC_HOOKS
 | 
						|
    /*
 | 
						|
    Call exception hook to pre-handle exceptions (if installed).
 | 
						|
    Pass EXCCAUSE in a2, and check result in a2 (if -1, skip default handling).
 | 
						|
    */
 | 
						|
    movi    a4, _xt_intexc_hooks
 | 
						|
    l32i    a4, a4, 0                       /* user exception hook index 0 */
 | 
						|
    beqz    a4, 1f
 | 
						|
.Ln_xt_user_exc_call_hook:
 | 
						|
    #ifdef __XTENSA_CALL0_ABI__
 | 
						|
    callx0  a4
 | 
						|
    beqi    a2, -1, .L_xt_user_done
 | 
						|
    #else
 | 
						|
    mov     a6, a2
 | 
						|
    callx4  a4
 | 
						|
    beqi    a6, -1, .L_xt_user_done
 | 
						|
    mov     a2, a6
 | 
						|
    #endif
 | 
						|
1:
 | 
						|
    #endif
 | 
						|
 | 
						|
    rsr     a2, EXCCAUSE                    /* recover exc cause */
 | 
						|
    movi    a3, _xt_exception_table
 | 
						|
    get_percpu_entry_for a2, a4
 | 
						|
    addx4   a4, a2, a3                      /* a4 = address of exception table entry */
 | 
						|
    l32i    a4, a4, 0                       /* a4 = handler address */
 | 
						|
    #ifdef __XTENSA_CALL0_ABI__
 | 
						|
    mov     a2, sp                          /* a2 = pointer to exc frame */
 | 
						|
    callx0  a4                              /* call handler */
 | 
						|
    #else
 | 
						|
    mov     a6, sp                          /* a6 = pointer to exc frame */
 | 
						|
    callx4  a4                              /* call handler */
 | 
						|
    #endif
 | 
						|
 | 
						|
.L_xt_user_done:
 | 
						|
 | 
						|
    /* Restore context and return */
 | 
						|
    call0   _xt_context_restore
 | 
						|
    l32i    a0, sp, XT_STK_PS               /* retrieve interruptee's PS */
 | 
						|
    wsr     a0, PS
 | 
						|
    l32i    a0, sp, XT_STK_PC               /* retrieve interruptee's PC */
 | 
						|
    wsr     a0, EPC_1
 | 
						|
    l32i    a0, sp, XT_STK_A0               /* retrieve interruptee's A0 */
 | 
						|
    l32i    sp, sp, XT_STK_A1               /* remove exception frame */
 | 
						|
    rsync                                   /* ensure PS and EPC written */
 | 
						|
    rfe                                     /* PS.EXCM is cleared */
 | 
						|
 | 
						|
 | 
						|
/*
 | 
						|
--------------------------------------------------------------------------------
 | 
						|
  Exit point for dispatch. Saved in interrupt stack frame at XT_STK_EXIT
 | 
						|
  on entry and used to return to a thread or interrupted interrupt handler.
 | 
						|
--------------------------------------------------------------------------------
 | 
						|
*/
 | 
						|
 | 
						|
    .global     _xt_user_exit
 | 
						|
    .type       _xt_user_exit,@function
 | 
						|
    .align      4
 | 
						|
_xt_user_exit:
 | 
						|
    l32i    a0, sp, XT_STK_PS               /* retrieve interruptee's PS */
 | 
						|
    wsr     a0, PS
 | 
						|
    l32i    a0, sp, XT_STK_PC               /* retrieve interruptee's PC */
 | 
						|
    wsr     a0, EPC_1
 | 
						|
    l32i    a0, sp, XT_STK_A0               /* retrieve interruptee's A0 */
 | 
						|
    l32i    sp, sp, XT_STK_A1               /* remove interrupt stack frame */
 | 
						|
    rsync                                   /* ensure PS and EPC written */
 | 
						|
    rfe                                     /* PS.EXCM is cleared */
 | 
						|
 | 
						|
 | 
						|
/*
 | 
						|
 | 
						|
--------------------------------------------------------------------------------
 | 
						|
Syscall Exception Handler (jumped to from User Exception Handler).
 | 
						|
Syscall 0 is required to spill the register windows (no-op in Call 0 ABI).
 | 
						|
Only syscall 0 is handled here. Other syscalls return -1 to caller in a2.
 | 
						|
--------------------------------------------------------------------------------
 | 
						|
*/
 | 
						|
 | 
						|
    .section .iram1,"ax"
 | 
						|
    .type       _xt_syscall_exc,@function
 | 
						|
    .align      4
 | 
						|
_xt_syscall_exc:
 | 
						|
 | 
						|
    #ifdef __XTENSA_CALL0_ABI__
 | 
						|
    /*
 | 
						|
    Save minimal regs for scratch. Syscall 0 does nothing in Call0 ABI.
 | 
						|
    Use a minimal stack frame (16B) to save A2 & A3 for scratch.
 | 
						|
    PS.EXCM could be cleared here, but unlikely to improve worst-case latency.
 | 
						|
    rsr     a0, PS
 | 
						|
    addi    a0, a0, -PS_EXCM_MASK
 | 
						|
    wsr     a0, PS
 | 
						|
    */
 | 
						|
    addi    sp, sp, -16
 | 
						|
    s32i    a2, sp, 8
 | 
						|
    s32i    a3, sp, 12
 | 
						|
    #else   /* Windowed ABI */
 | 
						|
    /*
 | 
						|
    Save necessary context and spill the register windows.
 | 
						|
    PS.EXCM is still set and must remain set until after the spill.
 | 
						|
    Reuse context save function though it saves more than necessary.
 | 
						|
    For this reason, a full interrupt stack frame is allocated.
 | 
						|
    */
 | 
						|
    addi    sp, sp, -XT_STK_FRMSZ           /* allocate interrupt stack frame */
 | 
						|
    s32i    a12, sp, XT_STK_A12             /* _xt_context_save requires A12- */
 | 
						|
    s32i    a13, sp, XT_STK_A13             /* A13 to have already been saved */
 | 
						|
    call0   _xt_context_save
 | 
						|
    #endif
 | 
						|
 | 
						|
    /*
 | 
						|
    Grab the interruptee's PC and skip over the 'syscall' instruction.
 | 
						|
    If it's at the end of a zero-overhead loop and it's not on the last
 | 
						|
    iteration, decrement loop counter and skip to beginning of loop.
 | 
						|
    */
 | 
						|
    rsr     a2, EPC_1                       /* a2 = PC of 'syscall' */
 | 
						|
    addi    a3, a2, 3                       /* ++PC                 */
 | 
						|
    #if XCHAL_HAVE_LOOPS
 | 
						|
    rsr     a0, LEND                        /* if (PC == LEND       */
 | 
						|
    bne     a3, a0, 1f
 | 
						|
    rsr     a0, LCOUNT                      /*     && LCOUNT != 0)  */
 | 
						|
    beqz    a0, 1f                          /* {                    */
 | 
						|
    addi    a0, a0, -1                      /*   --LCOUNT           */
 | 
						|
    rsr     a3, LBEG                        /*   PC = LBEG          */
 | 
						|
    wsr     a0, LCOUNT                      /* }                    */
 | 
						|
    #endif
 | 
						|
1:  wsr     a3, EPC_1                       /* update PC            */
 | 
						|
 | 
						|
    /* Restore interruptee's context and return from exception. */
 | 
						|
    #ifdef __XTENSA_CALL0_ABI__
 | 
						|
    l32i    a2, sp, 8
 | 
						|
    l32i    a3, sp, 12
 | 
						|
    addi    sp, sp, 16
 | 
						|
    #else
 | 
						|
    call0   _xt_context_restore
 | 
						|
    addi    sp, sp, XT_STK_FRMSZ
 | 
						|
    #endif
 | 
						|
    movi    a0, -1
 | 
						|
    movnez  a2, a0, a2                      /* return -1 if not syscall 0 */
 | 
						|
    rsr     a0, EXCSAVE_1
 | 
						|
    rfe
 | 
						|
 | 
						|
/*
 | 
						|
--------------------------------------------------------------------------------
 | 
						|
Co-Processor Exception Handler (jumped to from User Exception Handler).
 | 
						|
These exceptions are generated by co-processor instructions, which are only
 | 
						|
allowed in thread code (not in interrupts or kernel code). This restriction is
 | 
						|
deliberately imposed to reduce the burden of state-save/restore in interrupts.
 | 
						|
--------------------------------------------------------------------------------
 | 
						|
*/
 | 
						|
#if XCHAL_CP_NUM > 0
 | 
						|
 | 
						|
    .section .rodata, "a"
 | 
						|
 | 
						|
/* Offset to CP n save area in thread's CP save area. */
 | 
						|
    .global _xt_coproc_sa_offset
 | 
						|
    .type   _xt_coproc_sa_offset,@object
 | 
						|
    .align  16                      /* minimize crossing cache boundaries */
 | 
						|
_xt_coproc_sa_offset:
 | 
						|
    .word   XT_CP0_SA, XT_CP1_SA, XT_CP2_SA, XT_CP3_SA
 | 
						|
    .word   XT_CP4_SA, XT_CP5_SA, XT_CP6_SA, XT_CP7_SA
 | 
						|
 | 
						|
/* Bitmask for CP n's CPENABLE bit. */
 | 
						|
    .type   _xt_coproc_mask,@object
 | 
						|
    .align  16,,8                   /* try to keep it all in one cache line */
 | 
						|
    .set    i, 0
 | 
						|
_xt_coproc_mask:
 | 
						|
    .rept   XCHAL_CP_MAX
 | 
						|
    .long   (i<<16) | (1<<i)    // upper 16-bits = i, lower = bitmask
 | 
						|
    .set    i, i+1
 | 
						|
    .endr
 | 
						|
 | 
						|
    .data
 | 
						|
 | 
						|
/* Owner thread of CP n, identified by thread's CP save area (0 = unowned). */
 | 
						|
    .global _xt_coproc_owner_sa
 | 
						|
    .type   _xt_coproc_owner_sa,@object
 | 
						|
    .align  16,,XCHAL_CP_MAX<<2     /* minimize crossing cache boundaries */
 | 
						|
_xt_coproc_owner_sa:
 | 
						|
    .space  (XCHAL_CP_MAX * portNUM_PROCESSORS) << 2
 | 
						|
 | 
						|
    .section .iram1,"ax"
 | 
						|
 | 
						|
 | 
						|
    .align  4
 | 
						|
.L_goto_invalid:
 | 
						|
    j   .L_xt_coproc_invalid    /* not in a thread (invalid) */
 | 
						|
    .align  4
 | 
						|
.L_goto_done:
 | 
						|
    j   .L_xt_coproc_done
 | 
						|
 | 
						|
 | 
						|
/*
 | 
						|
--------------------------------------------------------------------------------
 | 
						|
  Coprocessor exception handler.
 | 
						|
  At entry, only a0 has been saved (in EXCSAVE_1).
 | 
						|
--------------------------------------------------------------------------------
 | 
						|
*/
 | 
						|
 | 
						|
    .type   _xt_coproc_exc,@function
 | 
						|
    .align  4
 | 
						|
 | 
						|
_xt_coproc_exc:
 | 
						|
 | 
						|
    /* Allocate interrupt stack frame and save minimal context. */
 | 
						|
    mov     a0, sp                          /* sp == a1 */
 | 
						|
    addi    sp, sp, -XT_STK_FRMSZ           /* allocate interrupt stack frame */
 | 
						|
    s32i    a0, sp, XT_STK_A1               /* save pre-interrupt SP */
 | 
						|
    #if XCHAL_HAVE_WINDOWED
 | 
						|
    s32e    a0, sp, -12                     /* for debug backtrace */
 | 
						|
    #endif
 | 
						|
    rsr     a0, PS                          /* save interruptee's PS */
 | 
						|
    s32i    a0, sp, XT_STK_PS
 | 
						|
    rsr     a0, EPC_1                       /* save interruptee's PC */
 | 
						|
    s32i    a0, sp, XT_STK_PC
 | 
						|
    rsr     a0, EXCSAVE_1                   /* save interruptee's a0 */
 | 
						|
    s32i    a0, sp, XT_STK_A0
 | 
						|
    #if XCHAL_HAVE_WINDOWED
 | 
						|
    s32e    a0, sp, -16                     /* for debug backtrace */
 | 
						|
    #endif
 | 
						|
    movi    a0, _xt_user_exit               /* save exit point for dispatch */
 | 
						|
    s32i    a0, sp, XT_STK_EXIT
 | 
						|
 | 
						|
    rsr     a0, EXCCAUSE
 | 
						|
    s32i    a5, sp, XT_STK_A5               /* save a5 */
 | 
						|
    addi    a5, a0, -EXCCAUSE_CP0_DISABLED  /* a5 = CP index */
 | 
						|
 | 
						|
    /* Save a few more of interruptee's registers (a5 was already saved). */
 | 
						|
    s32i    a2,  sp, XT_STK_A2
 | 
						|
    s32i    a3,  sp, XT_STK_A3
 | 
						|
    s32i    a4,  sp, XT_STK_A4
 | 
						|
    s32i    a15, sp, XT_STK_A15
 | 
						|
 | 
						|
    /* Get co-processor state save area of new owner thread. */
 | 
						|
    call0   XT_RTOS_CP_STATE                /* a15 = new owner's save area */
 | 
						|
    #if (ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(4, 2, 0))
 | 
						|
    beqz    a15, .L_goto_invalid            /* not in a thread (invalid) */
 | 
						|
    #else
 | 
						|
    #ifndef CONFIG_FREERTOS_FPU_IN_ISR
 | 
						|
    beqz    a15, .L_goto_invalid
 | 
						|
    #endif
 | 
						|
    #endif /* ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(4, 2, 0) */
 | 
						|
 | 
						|
    /*When FPU in ISR is enabled we could deal with zeroed a15 */
 | 
						|
 | 
						|
    /* Enable the co-processor's bit in CPENABLE. */
 | 
						|
    movi    a0, _xt_coproc_mask
 | 
						|
    rsr     a4, CPENABLE                    /* a4 = CPENABLE */
 | 
						|
    addx4   a0, a5, a0                      /* a0 = &_xt_coproc_mask[n] */
 | 
						|
    l32i    a0, a0, 0                       /* a0 = (n << 16) | (1 << n) */
 | 
						|
 | 
						|
    /* FPU operations are incompatible with non-pinned tasks. If we have a FPU operation
 | 
						|
       here, to keep the entire thing from crashing, it's better to pin the task to whatever
 | 
						|
       core we're running on now. */
 | 
						|
    movi    a2, pxCurrentTCB
 | 
						|
    getcoreid a3
 | 
						|
    addx4     a2,  a3, a2
 | 
						|
    l32i    a2, a2, 0                       /* a2 = start of pxCurrentTCB[cpuid] */
 | 
						|
    addi    a2, a2, TASKTCB_XCOREID_OFFSET  /* offset to xCoreID in tcb struct */
 | 
						|
    s32i    a3, a2, 0                       /* store current cpuid */
 | 
						|
 | 
						|
    /* Grab correct xt_coproc_owner_sa for this core */
 | 
						|
    movi    a2, XCHAL_CP_MAX << 2
 | 
						|
    mull    a2, a2, a3                      /* multiply by current processor id */
 | 
						|
    movi    a3, _xt_coproc_owner_sa         /* a3 = base of owner array */
 | 
						|
    add     a3, a3, a2                      /* a3 = owner area needed for this processor */
 | 
						|
 | 
						|
    extui   a2, a0, 0, 16                   /* coprocessor bitmask portion */
 | 
						|
    or      a4, a4, a2                      /* a4 = CPENABLE | (1 << n) */
 | 
						|
    wsr     a4, CPENABLE
 | 
						|
 | 
						|
/*
 | 
						|
Keep loading _xt_coproc_owner_sa[n] atomic (=load once, then use that value
 | 
						|
everywhere): _xt_coproc_release assumes it works like this in order not to need
 | 
						|
locking.
 | 
						|
*/
 | 
						|
 | 
						|
 | 
						|
    /* Get old coprocessor owner thread (save area ptr) and assign new one.  */
 | 
						|
    addx4   a3,  a5, a3                      /* a3 = &_xt_coproc_owner_sa[n] */
 | 
						|
    l32i    a2,  a3, 0                       /* a2 = old owner's save area */
 | 
						|
    s32i    a15, a3, 0                       /* _xt_coproc_owner_sa[n] = new */
 | 
						|
    rsync                                    /* ensure wsr.CPENABLE is complete */
 | 
						|
 | 
						|
    /* Only need to context switch if new owner != old owner. */
 | 
						|
    /* If float is necessary on ISR, we need to remove this check */
 | 
						|
    /* below, because on restoring from ISR we may have new == old condition used
 | 
						|
     * to force cp restore to next thread
 | 
						|
     */
 | 
						|
    #if (ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 2, 0))
 | 
						|
    #ifndef CONFIG_FREERTOS_FPU_IN_ISR
 | 
						|
    #endif
 | 
						|
    beq     a15, a2, .L_goto_done           /* new owner == old, we're done */
 | 
						|
    #if (ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 2, 0))
 | 
						|
    #endif
 | 
						|
    #endif
 | 
						|
 | 
						|
    /* If no old owner then nothing to save. */
 | 
						|
    beqz    a2, .L_check_new
 | 
						|
 | 
						|
    /* If old owner not actively using CP then nothing to save. */
 | 
						|
    l16ui   a4,  a2,  XT_CPENABLE           /* a4 = old owner's CPENABLE */
 | 
						|
    bnone   a4,  a0,  .L_check_new          /* old owner not using CP    */
 | 
						|
 | 
						|
.L_save_old:
 | 
						|
    /* Save old owner's coprocessor state. */
 | 
						|
 | 
						|
    movi    a5, _xt_coproc_sa_offset
 | 
						|
 | 
						|
    /* Mark old owner state as no longer active (CPENABLE bit n clear). */
 | 
						|
    xor     a4,  a4,  a0                    /* clear CP bit in CPENABLE    */
 | 
						|
    s16i    a4,  a2,  XT_CPENABLE           /* update old owner's CPENABLE */
 | 
						|
 | 
						|
    extui   a4,  a0,  16,  5                /* a4 = CP index = n */
 | 
						|
    addx4   a5,  a4,  a5                    /* a5 = &_xt_coproc_sa_offset[n] */
 | 
						|
 | 
						|
    /* Mark old owner state as saved (CPSTORED bit n set). */
 | 
						|
    l16ui   a4,  a2,  XT_CPSTORED           /* a4 = old owner's CPSTORED */
 | 
						|
    l32i    a5,  a5,  0                     /* a5 = XT_CP[n]_SA offset */
 | 
						|
    or      a4,  a4,  a0                    /* set CP in old owner's CPSTORED */
 | 
						|
    s16i    a4,  a2,  XT_CPSTORED           /* update old owner's CPSTORED */
 | 
						|
    l32i    a2, a2, XT_CP_ASA               /* ptr to actual (aligned) save area */
 | 
						|
    extui   a3, a0, 16, 5                   /* a3 = CP index = n */
 | 
						|
    add     a2, a2, a5                      /* a2 = old owner's area for CP n */
 | 
						|
 | 
						|
    /*
 | 
						|
    The config-specific HAL macro invoked below destroys a2-5, preserves a0-1.
 | 
						|
    It is theoretically possible for Xtensa processor designers to write TIE
 | 
						|
    that causes more address registers to be affected, but it is generally
 | 
						|
    unlikely. If that ever happens, more registers needs to be saved/restored
 | 
						|
    around this macro invocation, and the value in a15 needs to be recomputed.
 | 
						|
    */
 | 
						|
    xchal_cpi_store_funcbody
 | 
						|
 | 
						|
.L_check_new:
 | 
						|
    /* Check if any state has to be restored for new owner. */
 | 
						|
    /* NOTE: a15 = new owner's save area, cannot be zero when we get here. */
 | 
						|
    #if (ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 2, 0))
 | 
						|
    beqz    a15, .L_xt_coproc_done
 | 
						|
    #endif /* ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 2, 0) */
 | 
						|
 | 
						|
    l16ui   a3,  a15, XT_CPSTORED           /* a3 = new owner's CPSTORED */
 | 
						|
    movi    a4, _xt_coproc_sa_offset
 | 
						|
    bnone   a3,  a0,  .L_check_cs           /* full CP not saved, check callee-saved */
 | 
						|
    xor     a3,  a3,  a0                    /* CPSTORED bit is set, clear it */
 | 
						|
    s16i    a3,  a15, XT_CPSTORED           /* update new owner's CPSTORED */
 | 
						|
 | 
						|
    /* Adjust new owner's save area pointers to area for CP n. */
 | 
						|
    extui   a3,  a0, 16, 5                  /* a3 = CP index = n */
 | 
						|
    addx4   a4,  a3, a4                     /* a4 = &_xt_coproc_sa_offset[n] */
 | 
						|
    l32i    a4,  a4, 0                      /* a4 = XT_CP[n]_SA */
 | 
						|
    l32i    a5, a15, XT_CP_ASA              /* ptr to actual (aligned) save area */
 | 
						|
    add     a2,  a4, a5                     /* a2 = new owner's area for CP */
 | 
						|
 | 
						|
    /*
 | 
						|
    The config-specific HAL macro invoked below destroys a2-5, preserves a0-1.
 | 
						|
    It is theoretically possible for Xtensa processor designers to write TIE
 | 
						|
    that causes more address registers to be affected, but it is generally
 | 
						|
    unlikely. If that ever happens, more registers needs to be saved/restored
 | 
						|
    around this macro invocation.
 | 
						|
    */
 | 
						|
    xchal_cpi_load_funcbody
 | 
						|
 | 
						|
    /* Restore interruptee's saved registers. */
 | 
						|
    /* Can omit rsync for wsr.CPENABLE here because _xt_user_exit does it. */
 | 
						|
.L_xt_coproc_done:
 | 
						|
    l32i    a15, sp, XT_STK_A15
 | 
						|
    l32i    a5,  sp, XT_STK_A5
 | 
						|
    l32i    a4,  sp, XT_STK_A4
 | 
						|
    l32i    a3,  sp, XT_STK_A3
 | 
						|
    l32i    a2,  sp, XT_STK_A2
 | 
						|
    call0   _xt_user_exit                   /* return via exit dispatcher */
 | 
						|
    /* Never returns here - call0 is used as a jump (see note at top) */
 | 
						|
 | 
						|
.L_check_cs:
 | 
						|
    /* a0 = CP mask in low bits, a15 = new owner's save area */
 | 
						|
    l16ui   a2, a15, XT_CP_CS_ST            /* a2 = mask of CPs saved    */
 | 
						|
    bnone   a2,  a0, .L_xt_coproc_done      /* if no match then done     */
 | 
						|
    and     a2,  a2, a0                     /* a2 = which CPs to restore */
 | 
						|
    extui   a2,  a2, 0, 8                   /* extract low 8 bits        */
 | 
						|
    s32i    a6,  sp, XT_STK_A6              /* save extra needed regs    */
 | 
						|
    s32i    a7,  sp, XT_STK_A7
 | 
						|
    s32i    a13, sp, XT_STK_A13
 | 
						|
    s32i    a14, sp, XT_STK_A14
 | 
						|
    call0   _xt_coproc_restorecs            /* restore CP registers      */
 | 
						|
    l32i    a6,  sp, XT_STK_A6              /* restore saved registers   */
 | 
						|
    l32i    a7,  sp, XT_STK_A7
 | 
						|
    l32i    a13, sp, XT_STK_A13
 | 
						|
    l32i    a14, sp, XT_STK_A14
 | 
						|
    j       .L_xt_coproc_done
 | 
						|
 | 
						|
    /* Co-processor exception occurred outside a thread (not supported). */
 | 
						|
.L_xt_coproc_invalid:
 | 
						|
    movi    a0,PANIC_RSN_COPROCEXCEPTION
 | 
						|
    wsr     a0,EXCCAUSE
 | 
						|
    call0   _xt_panic                       /* not in a thread (invalid) */
 | 
						|
    /* never returns */
 | 
						|
 | 
						|
 | 
						|
#endif /* XCHAL_CP_NUM */
 | 
						|
 | 
						|
 | 
						|
/*
 | 
						|
-------------------------------------------------------------------------------
 | 
						|
  Level 1 interrupt dispatch. Assumes stack frame has not been allocated yet.
 | 
						|
-------------------------------------------------------------------------------
 | 
						|
*/
 | 
						|
 | 
						|
    .section .iram1,"ax"
 | 
						|
    .type       _xt_lowint1,@function
 | 
						|
    .align      4
 | 
						|
 | 
						|
_xt_lowint1:
 | 
						|
    mov     a0, sp                          /* sp == a1 */
 | 
						|
    addi    sp, sp, -XT_STK_FRMSZ           /* allocate interrupt stack frame */
 | 
						|
    s32i    a0, sp, XT_STK_A1               /* save pre-interrupt SP */
 | 
						|
    rsr     a0, PS                          /* save interruptee's PS */
 | 
						|
    s32i    a0, sp, XT_STK_PS
 | 
						|
    rsr     a0, EPC_1                       /* save interruptee's PC */
 | 
						|
    s32i    a0, sp, XT_STK_PC
 | 
						|
    rsr     a0, EXCSAVE_1                   /* save interruptee's a0 */
 | 
						|
    s32i    a0, sp, XT_STK_A0
 | 
						|
    movi    a0, _xt_user_exit               /* save exit point for dispatch */
 | 
						|
    s32i    a0, sp, XT_STK_EXIT
 | 
						|
 | 
						|
    #if (ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 2, 0))
 | 
						|
    /* EXCSAVE_1 should now be free to use. Use it to keep a copy of the
 | 
						|
    current stack pointer that points to the exception frame (XT_STK_FRAME).*/
 | 
						|
    #ifdef XT_DEBUG_BACKTRACE
 | 
						|
    #ifndef __XTENSA_CALL0_ABI__
 | 
						|
    mov     a0, sp
 | 
						|
    wsr     a0, EXCSAVE_1
 | 
						|
    #endif
 | 
						|
    #endif
 | 
						|
    #endif /* ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 2, 0) */
 | 
						|
 | 
						|
 | 
						|
    /* Save rest of interrupt context and enter RTOS. */
 | 
						|
    call0   XT_RTOS_INT_ENTER               /* common RTOS interrupt entry */
 | 
						|
 | 
						|
    /* !! We are now on the RTOS system stack !! */
 | 
						|
 | 
						|
    /* Set up PS for C, enable interrupts above this level and clear EXCM. */
 | 
						|
    #ifdef __XTENSA_CALL0_ABI__
 | 
						|
    movi    a0, PS_INTLEVEL(1) | PS_UM
 | 
						|
    #else
 | 
						|
    movi    a0, PS_INTLEVEL(1) | PS_UM | PS_WOE
 | 
						|
    #endif
 | 
						|
    wsr     a0, PS
 | 
						|
    rsync
 | 
						|
 | 
						|
    /* OK to call C code at this point, dispatch user ISRs */
 | 
						|
 | 
						|
    dispatch_c_isr 1 XCHAL_INTLEVEL1_MASK
 | 
						|
 | 
						|
    /* Done handling interrupts, transfer control to OS */
 | 
						|
    call0   XT_RTOS_INT_EXIT                /* does not return directly here */
 | 
						|
 | 
						|
 | 
						|
/*
 | 
						|
-------------------------------------------------------------------------------
 | 
						|
  MEDIUM PRIORITY (LEVEL 2+) INTERRUPT VECTORS AND LOW LEVEL HANDLERS.
 | 
						|
 | 
						|
  Medium priority interrupts are by definition those with priority greater
 | 
						|
  than 1 and not greater than XCHAL_EXCM_LEVEL. These are disabled by
 | 
						|
  setting PS.EXCM and therefore can easily support a C environment for
 | 
						|
  handlers in C, and interact safely with an RTOS.
 | 
						|
 | 
						|
  Each vector goes at a predetermined location according to the Xtensa
 | 
						|
  hardware configuration, which is ensured by its placement in a special
 | 
						|
  section known to the Xtensa linker support package (LSP). It performs
 | 
						|
  the minimum necessary before jumping to the handler in the .text section.
 | 
						|
 | 
						|
  The corresponding handler goes in the normal .text section. It sets up
 | 
						|
  the appropriate stack frame, saves a few vector-specific registers and
 | 
						|
  calls XT_RTOS_INT_ENTER to save the rest of the interrupted context
 | 
						|
  and enter the RTOS, then sets up a C environment. It then calls the
 | 
						|
  user's interrupt handler code (which may be coded in C) and finally
 | 
						|
  calls XT_RTOS_INT_EXIT to transfer control to the RTOS for scheduling.
 | 
						|
 | 
						|
  While XT_RTOS_INT_EXIT does not return directly to the interruptee,
 | 
						|
  eventually the RTOS scheduler will want to dispatch the interrupted
 | 
						|
  task or handler. The scheduler will return to the exit point that was
 | 
						|
  saved in the interrupt stack frame at XT_STK_EXIT.
 | 
						|
-------------------------------------------------------------------------------
 | 
						|
*/
 | 
						|
 | 
						|
#if XCHAL_EXCM_LEVEL >= 2
 | 
						|
 | 
						|
    .begin      literal_prefix .Level2InterruptVector
 | 
						|
    .section    .Level2InterruptVector.text, "ax"
 | 
						|
    .global     _Level2Vector
 | 
						|
    .type       _Level2Vector,@function
 | 
						|
    .align      4
 | 
						|
_Level2Vector:
 | 
						|
    wsr     a0, EXCSAVE_2                   /* preserve a0 */
 | 
						|
    call0   _xt_medint2                     /* load interrupt handler */
 | 
						|
    /* never returns here - call0 is used as a jump (see note at top) */
 | 
						|
 | 
						|
    .end        literal_prefix
 | 
						|
 | 
						|
    .section .iram1,"ax"
 | 
						|
    .type       _xt_medint2,@function
 | 
						|
    .align      4
 | 
						|
_xt_medint2:
 | 
						|
    mov     a0, sp                          /* sp == a1 */
 | 
						|
    addi    sp, sp, -XT_STK_FRMSZ           /* allocate interrupt stack frame */
 | 
						|
    s32i    a0, sp, XT_STK_A1               /* save pre-interrupt SP */
 | 
						|
    rsr     a0, EPS_2                       /* save interruptee's PS */
 | 
						|
    s32i    a0, sp, XT_STK_PS
 | 
						|
    rsr     a0, EPC_2                       /* save interruptee's PC */
 | 
						|
    s32i    a0, sp, XT_STK_PC
 | 
						|
    rsr     a0, EXCSAVE_2                   /* save interruptee's a0 */
 | 
						|
    s32i    a0, sp, XT_STK_A0
 | 
						|
    movi    a0, _xt_medint2_exit            /* save exit point for dispatch */
 | 
						|
    s32i    a0, sp, XT_STK_EXIT
 | 
						|
 | 
						|
    /* EXCSAVE_2 should now be free to use. Use it to keep a copy of the
 | 
						|
    current stack pointer that points to the exception frame (XT_STK_FRAME).*/
 | 
						|
    #if (ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 2, 0))
 | 
						|
    #ifdef XT_DEBUG_BACKTRACE
 | 
						|
    #ifndef __XTENSA_CALL0_ABI__
 | 
						|
    mov     a0, sp
 | 
						|
    wsr     a0, EXCSAVE_2
 | 
						|
    #endif
 | 
						|
    #endif
 | 
						|
    #endif /* ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 2, 0) */
 | 
						|
 | 
						|
 | 
						|
    /* Save rest of interrupt context and enter RTOS. */
 | 
						|
    call0   XT_RTOS_INT_ENTER               /* common RTOS interrupt entry */
 | 
						|
 | 
						|
    /* !! We are now on the RTOS system stack !! */
 | 
						|
 | 
						|
    /* Set up PS for C, enable interrupts above this level and clear EXCM. */
 | 
						|
    #ifdef __XTENSA_CALL0_ABI__
 | 
						|
    movi    a0, PS_INTLEVEL(2) | PS_UM
 | 
						|
    #else
 | 
						|
    movi    a0, PS_INTLEVEL(2) | PS_UM | PS_WOE
 | 
						|
    #endif
 | 
						|
    wsr     a0, PS
 | 
						|
    rsync
 | 
						|
 | 
						|
    /* OK to call C code at this point, dispatch user ISRs */
 | 
						|
 | 
						|
    dispatch_c_isr 2 XCHAL_INTLEVEL2_MASK
 | 
						|
 | 
						|
    /* Done handling interrupts, transfer control to OS */
 | 
						|
    call0   XT_RTOS_INT_EXIT                /* does not return directly here */
 | 
						|
 | 
						|
    /*
 | 
						|
    Exit point for dispatch. Saved in interrupt stack frame at XT_STK_EXIT
 | 
						|
    on entry and used to return to a thread or interrupted interrupt handler.
 | 
						|
    */
 | 
						|
    .global     _xt_medint2_exit
 | 
						|
    .type       _xt_medint2_exit,@function
 | 
						|
    .align      4
 | 
						|
_xt_medint2_exit:
 | 
						|
    /* Restore only level-specific regs (the rest were already restored) */
 | 
						|
    l32i    a0, sp, XT_STK_PS               /* retrieve interruptee's PS */
 | 
						|
    wsr     a0, EPS_2
 | 
						|
    l32i    a0, sp, XT_STK_PC               /* retrieve interruptee's PC */
 | 
						|
    wsr     a0, EPC_2
 | 
						|
    l32i    a0, sp, XT_STK_A0               /* retrieve interruptee's A0 */
 | 
						|
    l32i    sp, sp, XT_STK_A1               /* remove interrupt stack frame */
 | 
						|
    rsync                                   /* ensure EPS and EPC written */
 | 
						|
    rfi     2
 | 
						|
 | 
						|
#endif  /* Level 2 */
 | 
						|
 | 
						|
#if XCHAL_EXCM_LEVEL >= 3
 | 
						|
 | 
						|
    .begin      literal_prefix .Level3InterruptVector
 | 
						|
    .section    .Level3InterruptVector.text, "ax"
 | 
						|
    .global     _Level3Vector
 | 
						|
    .type       _Level3Vector,@function
 | 
						|
    .align      4
 | 
						|
_Level3Vector:
 | 
						|
    wsr     a0, EXCSAVE_3                   /* preserve a0 */
 | 
						|
    call0   _xt_medint3                     /* load interrupt handler */
 | 
						|
    /* never returns here - call0 is used as a jump (see note at top) */
 | 
						|
 | 
						|
    .end        literal_prefix
 | 
						|
 | 
						|
    .section .iram1,"ax"
 | 
						|
    .type       _xt_medint3,@function
 | 
						|
    .align      4
 | 
						|
_xt_medint3:
 | 
						|
    mov     a0, sp                          /* sp == a1 */
 | 
						|
    addi    sp, sp, -XT_STK_FRMSZ           /* allocate interrupt stack frame */
 | 
						|
    s32i    a0, sp, XT_STK_A1               /* save pre-interrupt SP */
 | 
						|
    rsr     a0, EPS_3                       /* save interruptee's PS */
 | 
						|
    s32i    a0, sp, XT_STK_PS
 | 
						|
    rsr     a0, EPC_3                       /* save interruptee's PC */
 | 
						|
    s32i    a0, sp, XT_STK_PC
 | 
						|
    rsr     a0, EXCSAVE_3                   /* save interruptee's a0 */
 | 
						|
    s32i    a0, sp, XT_STK_A0
 | 
						|
    movi    a0, _xt_medint3_exit            /* save exit point for dispatch */
 | 
						|
    s32i    a0, sp, XT_STK_EXIT
 | 
						|
 | 
						|
    /* EXCSAVE_3 should now be free to use. Use it to keep a copy of the
 | 
						|
    current stack pointer that points to the exception frame (XT_STK_FRAME).*/
 | 
						|
    #if (ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 2, 0))
 | 
						|
    #ifdef XT_DEBUG_BACKTRACE
 | 
						|
    #ifndef __XTENSA_CALL0_ABI__
 | 
						|
    mov     a0, sp
 | 
						|
    wsr     a0, EXCSAVE_3
 | 
						|
    #endif
 | 
						|
    #endif
 | 
						|
    #endif /* ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 2, 0) */
 | 
						|
 | 
						|
 | 
						|
    /* Save rest of interrupt context and enter RTOS. */
 | 
						|
    call0   XT_RTOS_INT_ENTER               /* common RTOS interrupt entry */
 | 
						|
 | 
						|
    /* !! We are now on the RTOS system stack !! */
 | 
						|
 | 
						|
    /* Set up PS for C, enable interrupts above this level and clear EXCM. */
 | 
						|
    #ifdef __XTENSA_CALL0_ABI__
 | 
						|
    movi    a0, PS_INTLEVEL(3) | PS_UM
 | 
						|
    #else
 | 
						|
    movi    a0, PS_INTLEVEL(3) | PS_UM | PS_WOE
 | 
						|
    #endif
 | 
						|
    wsr     a0, PS
 | 
						|
    rsync
 | 
						|
 | 
						|
    /* OK to call C code at this point, dispatch user ISRs */
 | 
						|
 | 
						|
    dispatch_c_isr 3 XCHAL_INTLEVEL3_MASK
 | 
						|
 | 
						|
    /* Done handling interrupts, transfer control to OS */
 | 
						|
    call0   XT_RTOS_INT_EXIT                /* does not return directly here */
 | 
						|
 | 
						|
    /*
 | 
						|
    Exit point for dispatch. Saved in interrupt stack frame at XT_STK_EXIT
 | 
						|
    on entry and used to return to a thread or interrupted interrupt handler.
 | 
						|
    */
 | 
						|
    .global     _xt_medint3_exit
 | 
						|
    .type       _xt_medint3_exit,@function
 | 
						|
    .align      4
 | 
						|
_xt_medint3_exit:
 | 
						|
    /* Restore only level-specific regs (the rest were already restored) */
 | 
						|
    l32i    a0, sp, XT_STK_PS               /* retrieve interruptee's PS */
 | 
						|
    wsr     a0, EPS_3
 | 
						|
    l32i    a0, sp, XT_STK_PC               /* retrieve interruptee's PC */
 | 
						|
    wsr     a0, EPC_3
 | 
						|
    l32i    a0, sp, XT_STK_A0               /* retrieve interruptee's A0 */
 | 
						|
    l32i    sp, sp, XT_STK_A1               /* remove interrupt stack frame */
 | 
						|
    rsync                                   /* ensure EPS and EPC written */
 | 
						|
    rfi     3
 | 
						|
 | 
						|
#endif  /* Level 3 */
 | 
						|
 | 
						|
#if XCHAL_EXCM_LEVEL >= 4
 | 
						|
 | 
						|
    .begin      literal_prefix .Level4InterruptVector
 | 
						|
    .section    .Level4InterruptVector.text, "ax"
 | 
						|
    .global     _Level4Vector
 | 
						|
    .type       _Level4Vector,@function
 | 
						|
    .align      4
 | 
						|
_Level4Vector:
 | 
						|
    wsr     a0, EXCSAVE_4                   /* preserve a0 */
 | 
						|
    call0   _xt_medint4                     /* load interrupt handler */
 | 
						|
 | 
						|
    .end        literal_prefix
 | 
						|
 | 
						|
    .section .iram1,"ax"
 | 
						|
    .type       _xt_medint4,@function
 | 
						|
    .align      4
 | 
						|
_xt_medint4:
 | 
						|
    mov     a0, sp                          /* sp == a1 */
 | 
						|
    addi    sp, sp, -XT_STK_FRMSZ           /* allocate interrupt stack frame */
 | 
						|
    s32i    a0, sp, XT_STK_A1               /* save pre-interrupt SP */
 | 
						|
    rsr     a0, EPS_4                       /* save interruptee's PS */
 | 
						|
    s32i    a0, sp, XT_STK_PS
 | 
						|
    rsr     a0, EPC_4                       /* save interruptee's PC */
 | 
						|
    s32i    a0, sp, XT_STK_PC
 | 
						|
    rsr     a0, EXCSAVE_4                   /* save interruptee's a0 */
 | 
						|
    s32i    a0, sp, XT_STK_A0
 | 
						|
    movi    a0, _xt_medint4_exit            /* save exit point for dispatch */
 | 
						|
    s32i    a0, sp, XT_STK_EXIT
 | 
						|
 | 
						|
    /* EXCSAVE_4 should now be free to use. Use it to keep a copy of the
 | 
						|
    current stack pointer that points to the exception frame (XT_STK_FRAME).*/
 | 
						|
    #if (ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 2, 0))
 | 
						|
    #ifdef XT_DEBUG_BACKTRACE
 | 
						|
    #ifndef __XTENSA_CALL0_ABI__
 | 
						|
    mov     a0, sp
 | 
						|
    wsr     a0, EXCSAVE_4
 | 
						|
    #endif
 | 
						|
    #endif
 | 
						|
    #endif /* ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 2, 0) */
 | 
						|
 | 
						|
 | 
						|
    /* Save rest of interrupt context and enter RTOS. */
 | 
						|
    call0   XT_RTOS_INT_ENTER               /* common RTOS interrupt entry */
 | 
						|
 | 
						|
    /* !! We are now on the RTOS system stack !! */
 | 
						|
 | 
						|
    /* Set up PS for C, enable interrupts above this level and clear EXCM. */
 | 
						|
    #ifdef __XTENSA_CALL0_ABI__
 | 
						|
    movi    a0, PS_INTLEVEL(4) | PS_UM
 | 
						|
    #else
 | 
						|
    movi    a0, PS_INTLEVEL(4) | PS_UM | PS_WOE
 | 
						|
    #endif
 | 
						|
    wsr     a0, PS
 | 
						|
    rsync
 | 
						|
 | 
						|
    /* OK to call C code at this point, dispatch user ISRs */
 | 
						|
 | 
						|
    dispatch_c_isr 4 XCHAL_INTLEVEL4_MASK
 | 
						|
 | 
						|
    /* Done handling interrupts, transfer control to OS */
 | 
						|
    call0   XT_RTOS_INT_EXIT                /* does not return directly here */
 | 
						|
 | 
						|
    /*
 | 
						|
    Exit point for dispatch. Saved in interrupt stack frame at XT_STK_EXIT
 | 
						|
    on entry and used to return to a thread or interrupted interrupt handler.
 | 
						|
    */
 | 
						|
    .global     _xt_medint4_exit
 | 
						|
    .type       _xt_medint4_exit,@function
 | 
						|
    .align      4
 | 
						|
_xt_medint4_exit:
 | 
						|
    /* Restore only level-specific regs (the rest were already restored) */
 | 
						|
    l32i    a0, sp, XT_STK_PS               /* retrieve interruptee's PS */
 | 
						|
    wsr     a0, EPS_4
 | 
						|
    l32i    a0, sp, XT_STK_PC               /* retrieve interruptee's PC */
 | 
						|
    wsr     a0, EPC_4
 | 
						|
    l32i    a0, sp, XT_STK_A0               /* retrieve interruptee's A0 */
 | 
						|
    l32i    sp, sp, XT_STK_A1               /* remove interrupt stack frame */
 | 
						|
    rsync                                   /* ensure EPS and EPC written */
 | 
						|
    rfi     4
 | 
						|
 | 
						|
#endif  /* Level 4 */
 | 
						|
 | 
						|
#if XCHAL_EXCM_LEVEL >= 5
 | 
						|
 | 
						|
    .begin      literal_prefix .Level5InterruptVector
 | 
						|
    .section    .Level5InterruptVector.text, "ax"
 | 
						|
    .global     _Level5Vector
 | 
						|
    .type       _Level5Vector,@function
 | 
						|
    .align      4
 | 
						|
_Level5Vector:
 | 
						|
    wsr     a0, EXCSAVE_5                   /* preserve a0 */
 | 
						|
    call0   _xt_medint5                     /* load interrupt handler */
 | 
						|
 | 
						|
    .end        literal_prefix
 | 
						|
 | 
						|
    .section .iram1,"ax"
 | 
						|
    .type       _xt_medint5,@function
 | 
						|
    .align      4
 | 
						|
_xt_medint5:
 | 
						|
    mov     a0, sp                          /* sp == a1 */
 | 
						|
    addi    sp, sp, -XT_STK_FRMSZ           /* allocate interrupt stack frame */
 | 
						|
    s32i    a0, sp, XT_STK_A1               /* save pre-interrupt SP */
 | 
						|
    rsr     a0, EPS_5                       /* save interruptee's PS */
 | 
						|
    s32i    a0, sp, XT_STK_PS
 | 
						|
    rsr     a0, EPC_5                       /* save interruptee's PC */
 | 
						|
    s32i    a0, sp, XT_STK_PC
 | 
						|
    rsr     a0, EXCSAVE_5                   /* save interruptee's a0 */
 | 
						|
    s32i    a0, sp, XT_STK_A0
 | 
						|
    movi    a0, _xt_medint5_exit            /* save exit point for dispatch */
 | 
						|
    s32i    a0, sp, XT_STK_EXIT
 | 
						|
 | 
						|
    /* EXCSAVE_5 should now be free to use. Use it to keep a copy of the
 | 
						|
    current stack pointer that points to the exception frame (XT_STK_FRAME).*/
 | 
						|
    #if (ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 2, 0))
 | 
						|
    #ifdef XT_DEBUG_BACKTRACE
 | 
						|
    #ifndef __XTENSA_CALL0_ABI__
 | 
						|
    mov     a0, sp
 | 
						|
    wsr     a0, EXCSAVE_5
 | 
						|
    #endif
 | 
						|
    #endif
 | 
						|
    #endif /* ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 2, 0) */
 | 
						|
 | 
						|
    /* Save rest of interrupt context and enter RTOS. */
 | 
						|
    call0   XT_RTOS_INT_ENTER               /* common RTOS interrupt entry */
 | 
						|
 | 
						|
    /* !! We are now on the RTOS system stack !! */
 | 
						|
 | 
						|
    /* Set up PS for C, enable interrupts above this level and clear EXCM. */
 | 
						|
    #ifdef __XTENSA_CALL0_ABI__
 | 
						|
    movi    a0, PS_INTLEVEL(5) | PS_UM
 | 
						|
    #else
 | 
						|
    movi    a0, PS_INTLEVEL(5) | PS_UM | PS_WOE
 | 
						|
    #endif
 | 
						|
    wsr     a0, PS
 | 
						|
    rsync
 | 
						|
 | 
						|
    /* OK to call C code at this point, dispatch user ISRs */
 | 
						|
 | 
						|
    dispatch_c_isr 5 XCHAL_INTLEVEL5_MASK
 | 
						|
 | 
						|
    /* Done handling interrupts, transfer control to OS */
 | 
						|
    call0   XT_RTOS_INT_EXIT                /* does not return directly here */
 | 
						|
 | 
						|
    /*
 | 
						|
    Exit point for dispatch. Saved in interrupt stack frame at XT_STK_EXIT
 | 
						|
    on entry and used to return to a thread or interrupted interrupt handler.
 | 
						|
    */
 | 
						|
    .global     _xt_medint5_exit
 | 
						|
    .type       _xt_medint5_exit,@function
 | 
						|
    .align      4
 | 
						|
_xt_medint5_exit:
 | 
						|
    /* Restore only level-specific regs (the rest were already restored) */
 | 
						|
    l32i    a0, sp, XT_STK_PS               /* retrieve interruptee's PS */
 | 
						|
    wsr     a0, EPS_5
 | 
						|
    l32i    a0, sp, XT_STK_PC               /* retrieve interruptee's PC */
 | 
						|
    wsr     a0, EPC_5
 | 
						|
    l32i    a0, sp, XT_STK_A0               /* retrieve interruptee's A0 */
 | 
						|
    l32i    sp, sp, XT_STK_A1               /* remove interrupt stack frame */
 | 
						|
    rsync                                   /* ensure EPS and EPC written */
 | 
						|
    rfi     5
 | 
						|
 | 
						|
#endif  /* Level 5 */
 | 
						|
 | 
						|
#if XCHAL_EXCM_LEVEL >= 6
 | 
						|
 | 
						|
    .begin      literal_prefix .Level6InterruptVector
 | 
						|
    .section    .Level6InterruptVector.text, "ax"
 | 
						|
    .global     _Level6Vector
 | 
						|
    .type       _Level6Vector,@function
 | 
						|
    .align      4
 | 
						|
_Level6Vector:
 | 
						|
    wsr     a0, EXCSAVE_6                   /* preserve a0 */
 | 
						|
    call0   _xt_medint6                     /* load interrupt handler */
 | 
						|
 | 
						|
    .end        literal_prefix
 | 
						|
 | 
						|
    .section .iram1,"ax"
 | 
						|
    .type       _xt_medint6,@function
 | 
						|
    .align      4
 | 
						|
_xt_medint6:
 | 
						|
    mov     a0, sp                          /* sp == a1 */
 | 
						|
    addi    sp, sp, -XT_STK_FRMSZ           /* allocate interrupt stack frame */
 | 
						|
    s32i    a0, sp, XT_STK_A1               /* save pre-interrupt SP */
 | 
						|
    rsr     a0, EPS_6                       /* save interruptee's PS */
 | 
						|
    s32i    a0, sp, XT_STK_PS
 | 
						|
    rsr     a0, EPC_6                       /* save interruptee's PC */
 | 
						|
    s32i    a0, sp, XT_STK_PC
 | 
						|
    rsr     a0, EXCSAVE_6                   /* save interruptee's a0 */
 | 
						|
    s32i    a0, sp, XT_STK_A0
 | 
						|
    movi    a0, _xt_medint6_exit            /* save exit point for dispatch */
 | 
						|
    s32i    a0, sp, XT_STK_EXIT
 | 
						|
 | 
						|
    /* EXCSAVE_6 should now be free to use. Use it to keep a copy of the
 | 
						|
    current stack pointer that points to the exception frame (XT_STK_FRAME).*/
 | 
						|
    #if (ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 2, 0))
 | 
						|
    #ifdef XT_DEBUG_BACKTRACE
 | 
						|
    #ifndef __XTENSA_CALL0_ABI__
 | 
						|
    mov     a0, sp
 | 
						|
    wsr     a0, EXCSAVE_6
 | 
						|
    #endif
 | 
						|
    #endif
 | 
						|
    #endif /* ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 2, 0) */
 | 
						|
 | 
						|
    /* Save rest of interrupt context and enter RTOS. */
 | 
						|
    call0   XT_RTOS_INT_ENTER               /* common RTOS interrupt entry */
 | 
						|
 | 
						|
    /* !! We are now on the RTOS system stack !! */
 | 
						|
 | 
						|
    /* Set up PS for C, enable interrupts above this level and clear EXCM. */
 | 
						|
    #ifdef __XTENSA_CALL0_ABI__
 | 
						|
    movi    a0, PS_INTLEVEL(6) | PS_UM
 | 
						|
    #else
 | 
						|
    movi    a0, PS_INTLEVEL(6) | PS_UM | PS_WOE
 | 
						|
    #endif
 | 
						|
    wsr     a0, PS
 | 
						|
    rsync
 | 
						|
 | 
						|
    /* OK to call C code at this point, dispatch user ISRs */
 | 
						|
 | 
						|
    dispatch_c_isr 6 XCHAL_INTLEVEL6_MASK
 | 
						|
 | 
						|
    /* Done handling interrupts, transfer control to OS */
 | 
						|
    call0   XT_RTOS_INT_EXIT                /* does not return directly here */
 | 
						|
 | 
						|
    /*
 | 
						|
    Exit point for dispatch. Saved in interrupt stack frame at XT_STK_EXIT
 | 
						|
    on entry and used to return to a thread or interrupted interrupt handler.
 | 
						|
    */
 | 
						|
    .global     _xt_medint6_exit
 | 
						|
    .type       _xt_medint6_exit,@function
 | 
						|
    .align      4
 | 
						|
_xt_medint6_exit:
 | 
						|
    /* Restore only level-specific regs (the rest were already restored) */
 | 
						|
    l32i    a0, sp, XT_STK_PS               /* retrieve interruptee's PS */
 | 
						|
    wsr     a0, EPS_6
 | 
						|
    l32i    a0, sp, XT_STK_PC               /* retrieve interruptee's PC */
 | 
						|
    wsr     a0, EPC_6
 | 
						|
    l32i    a0, sp, XT_STK_A0               /* retrieve interruptee's A0 */
 | 
						|
    l32i    sp, sp, XT_STK_A1               /* remove interrupt stack frame */
 | 
						|
    rsync                                   /* ensure EPS and EPC written */
 | 
						|
    rfi     6
 | 
						|
 | 
						|
#endif  /* Level 6 */
 | 
						|
 | 
						|
 | 
						|
/*******************************************************************************
 | 
						|
 | 
						|
HIGH PRIORITY (LEVEL > XCHAL_EXCM_LEVEL) INTERRUPT VECTORS AND HANDLERS
 | 
						|
 | 
						|
High priority interrupts are by definition those with priorities greater
 | 
						|
than XCHAL_EXCM_LEVEL. This includes non-maskable (NMI). High priority
 | 
						|
interrupts cannot interact with the RTOS, that is they must save all regs
 | 
						|
they use and not call any RTOS function.
 | 
						|
 | 
						|
A further restriction imposed by the Xtensa windowed architecture is that
 | 
						|
high priority interrupts must not modify the stack area even logically
 | 
						|
"above" the top of the interrupted stack (they need to provide their
 | 
						|
own stack or static save area).
 | 
						|
 | 
						|
Cadence Design Systems recommends high priority interrupt handlers be coded in assembly
 | 
						|
and used for purposes requiring very short service times.
 | 
						|
 | 
						|
Here are templates for high priority (level 2+) interrupt vectors.
 | 
						|
They assume only one interrupt per level to avoid the burden of identifying
 | 
						|
which interrupts at this level are pending and enabled. This allows for
 | 
						|
minimum latency and avoids having to save/restore a2 in addition to a0.
 | 
						|
If more than one interrupt per high priority level is configured, this burden
 | 
						|
is on the handler which in any case must provide a way to save and restore
 | 
						|
registers it uses without touching the interrupted stack.
 | 
						|
 | 
						|
Each vector goes at a predetermined location according to the Xtensa
 | 
						|
hardware configuration, which is ensured by its placement in a special
 | 
						|
section known to the Xtensa linker support package (LSP). It performs
 | 
						|
the minimum necessary before jumping to the handler in the .text section.
 | 
						|
 | 
						|
*******************************************************************************/
 | 
						|
 | 
						|
/*
 | 
						|
These stubs just call xt_highintX/xt_nmi to handle the real interrupt. Please define
 | 
						|
these in an external assembly source file. If these symbols are not defined anywhere
 | 
						|
else, the defaults in xtensa_vector_defaults.S are used.
 | 
						|
*/
 | 
						|
 | 
						|
#if XCHAL_NUM_INTLEVELS >=2 && XCHAL_EXCM_LEVEL <2 && XCHAL_DEBUGLEVEL !=2
 | 
						|
 | 
						|
    .begin      literal_prefix .Level2InterruptVector
 | 
						|
    .section    .Level2InterruptVector.text, "ax"
 | 
						|
    .global     _Level2Vector
 | 
						|
    .type       _Level2Vector,@function
 | 
						|
    .global     xt_highint2
 | 
						|
    .align      4
 | 
						|
_Level2Vector:
 | 
						|
    wsr     a0, EXCSAVE_2                   /* preserve a0 */
 | 
						|
    call0   xt_highint2                    /* load interrupt handler */
 | 
						|
 | 
						|
    .end        literal_prefix
 | 
						|
 | 
						|
#endif  /* Level 2 */
 | 
						|
 | 
						|
#if XCHAL_NUM_INTLEVELS >=3 && XCHAL_EXCM_LEVEL <3 && XCHAL_DEBUGLEVEL !=3
 | 
						|
 | 
						|
    .begin      literal_prefix .Level3InterruptVector
 | 
						|
    .section    .Level3InterruptVector.text, "ax"
 | 
						|
    .global     _Level3Vector
 | 
						|
    .type       _Level3Vector,@function
 | 
						|
    .global     xt_highint3
 | 
						|
    .align      4
 | 
						|
_Level3Vector:
 | 
						|
    wsr     a0, EXCSAVE_3                   /* preserve a0 */
 | 
						|
    call0   xt_highint3                    /* load interrupt handler */
 | 
						|
    /* never returns here - call0 is used as a jump (see note at top) */
 | 
						|
 | 
						|
    .end        literal_prefix
 | 
						|
 | 
						|
#endif  /* Level 3 */
 | 
						|
 | 
						|
#if XCHAL_NUM_INTLEVELS >=4 && XCHAL_EXCM_LEVEL <4 && XCHAL_DEBUGLEVEL !=4
 | 
						|
 | 
						|
    .begin      literal_prefix .Level4InterruptVector
 | 
						|
    .section    .Level4InterruptVector.text, "ax"
 | 
						|
    .global     _Level4Vector
 | 
						|
    .type       _Level4Vector,@function
 | 
						|
    .global     xt_highint4
 | 
						|
    .align      4
 | 
						|
_Level4Vector:
 | 
						|
    wsr     a0, EXCSAVE_4                   /* preserve a0 */
 | 
						|
    call0   xt_highint4                    /* load interrupt handler */
 | 
						|
    /* never returns here - call0 is used as a jump (see note at top) */
 | 
						|
 | 
						|
    .end        literal_prefix
 | 
						|
 | 
						|
#endif  /* Level 4 */
 | 
						|
 | 
						|
#if XCHAL_NUM_INTLEVELS >=5 && XCHAL_EXCM_LEVEL <5 && XCHAL_DEBUGLEVEL !=5
 | 
						|
 | 
						|
    .begin      literal_prefix .Level5InterruptVector
 | 
						|
    .section    .Level5InterruptVector.text, "ax"
 | 
						|
    .global     _Level5Vector
 | 
						|
    .type       _Level5Vector,@function
 | 
						|
    .global     xt_highint5
 | 
						|
    .align      4
 | 
						|
_Level5Vector:
 | 
						|
    wsr     a0, EXCSAVE_5                   /* preserve a0 */
 | 
						|
    call0   xt_highint5                    /* load interrupt handler */
 | 
						|
    /* never returns here - call0 is used as a jump (see note at top) */
 | 
						|
 | 
						|
    .end        literal_prefix
 | 
						|
 | 
						|
#endif  /* Level 5 */
 | 
						|
 | 
						|
#if XCHAL_NUM_INTLEVELS >=6 && XCHAL_EXCM_LEVEL <6 && XCHAL_DEBUGLEVEL !=6
 | 
						|
 | 
						|
    .begin      literal_prefix .Level6InterruptVector
 | 
						|
    .section    .Level6InterruptVector.text, "ax"
 | 
						|
    .global     _Level6Vector
 | 
						|
    .type       _Level6Vector,@function
 | 
						|
    .global     xt_highint6
 | 
						|
    .align      4
 | 
						|
_Level6Vector:
 | 
						|
    wsr     a0, EXCSAVE_6                   /* preserve a0 */
 | 
						|
    call0   xt_highint6                    /* load interrupt handler */
 | 
						|
    /* never returns here - call0 is used as a jump (see note at top) */
 | 
						|
 | 
						|
    .end        literal_prefix
 | 
						|
 | 
						|
#endif  /* Level 6 */
 | 
						|
 | 
						|
#if XCHAL_HAVE_NMI
 | 
						|
 | 
						|
    .begin      literal_prefix .NMIExceptionVector
 | 
						|
    .section    .NMIExceptionVector.text, "ax"
 | 
						|
    .global     _NMIExceptionVector
 | 
						|
    .type       _NMIExceptionVector,@function
 | 
						|
    .global     xt_nmi
 | 
						|
    .align      4
 | 
						|
_NMIExceptionVector:
 | 
						|
    wsr     a0, EXCSAVE + XCHAL_NMILEVEL  _ /* preserve a0 */
 | 
						|
    call0   xt_nmi                         /* load interrupt handler */
 | 
						|
    /* never returns here - call0 is used as a jump (see note at top) */
 | 
						|
 | 
						|
    .end        literal_prefix
 | 
						|
 | 
						|
#endif  /* NMI */
 | 
						|
 | 
						|
 | 
						|
/*******************************************************************************
 | 
						|
 | 
						|
WINDOW OVERFLOW AND UNDERFLOW EXCEPTION VECTORS AND ALLOCA EXCEPTION HANDLER
 | 
						|
 | 
						|
Here is the code for each window overflow/underflow exception vector and
 | 
						|
(interspersed) efficient code for handling the alloca exception cause.
 | 
						|
Window exceptions are handled entirely in the vector area and are very
 | 
						|
tight for performance. The alloca exception is also handled entirely in
 | 
						|
the window vector area so comes at essentially no cost in code size.
 | 
						|
Users should never need to modify them and Cadence Design Systems recommends
 | 
						|
they do not.
 | 
						|
 | 
						|
Window handlers go at predetermined vector locations according to the
 | 
						|
Xtensa hardware configuration, which is ensured by their placement in a
 | 
						|
special section known to the Xtensa linker support package (LSP). Since
 | 
						|
their offsets in that section are always the same, the LSPs do not define
 | 
						|
a section per vector.
 | 
						|
 | 
						|
These things are coded for XEA2 only (XEA1 is not supported).
 | 
						|
 | 
						|
Note on Underflow Handlers:
 | 
						|
The underflow handler for returning from call[i+1] to call[i]
 | 
						|
must preserve all the registers from call[i+1]'s window.
 | 
						|
In particular, a0 and a1 must be preserved because the RETW instruction
 | 
						|
will be reexecuted (and may even underflow if an intervening exception
 | 
						|
has flushed call[i]'s registers).
 | 
						|
Registers a2 and up may contain return values.
 | 
						|
 | 
						|
*******************************************************************************/
 | 
						|
 | 
						|
#if XCHAL_HAVE_WINDOWED
 | 
						|
 | 
						|
    .section .WindowVectors.text, "ax"
 | 
						|
 | 
						|
/*
 | 
						|
--------------------------------------------------------------------------------
 | 
						|
Window Overflow Exception for Call4.
 | 
						|
 | 
						|
Invoked if a call[i] referenced a register (a4-a15)
 | 
						|
that contains data from ancestor call[j];
 | 
						|
call[j] had done a call4 to call[j+1].
 | 
						|
On entry here:
 | 
						|
    window rotated to call[j] start point;
 | 
						|
        a0-a3 are registers to be saved;
 | 
						|
        a4-a15 must be preserved;
 | 
						|
        a5 is call[j+1]'s stack pointer.
 | 
						|
--------------------------------------------------------------------------------
 | 
						|
*/
 | 
						|
 | 
						|
    .org    0x0
 | 
						|
    .global _WindowOverflow4
 | 
						|
_WindowOverflow4:
 | 
						|
 | 
						|
    s32e    a0, a5, -16     /* save a0 to call[j+1]'s stack frame */
 | 
						|
    s32e    a1, a5, -12     /* save a1 to call[j+1]'s stack frame */
 | 
						|
    s32e    a2, a5,  -8     /* save a2 to call[j+1]'s stack frame */
 | 
						|
    s32e    a3, a5,  -4     /* save a3 to call[j+1]'s stack frame */
 | 
						|
    rfwo                    /* rotates back to call[i] position */
 | 
						|
 | 
						|
/*
 | 
						|
--------------------------------------------------------------------------------
 | 
						|
Window Underflow Exception for Call4
 | 
						|
 | 
						|
Invoked by RETW returning from call[i+1] to call[i]
 | 
						|
where call[i]'s registers must be reloaded (not live in ARs);
 | 
						|
where call[i] had done a call4 to call[i+1].
 | 
						|
On entry here:
 | 
						|
        window rotated to call[i] start point;
 | 
						|
        a0-a3 are undefined, must be reloaded with call[i].reg[0..3];
 | 
						|
        a4-a15 must be preserved (they are call[i+1].reg[0..11]);
 | 
						|
        a5 is call[i+1]'s stack pointer.
 | 
						|
--------------------------------------------------------------------------------
 | 
						|
*/
 | 
						|
 | 
						|
    .org    0x40
 | 
						|
    .global _WindowUnderflow4
 | 
						|
_WindowUnderflow4:
 | 
						|
 | 
						|
    l32e    a0, a5, -16     /* restore a0 from call[i+1]'s stack frame */
 | 
						|
    l32e    a1, a5, -12     /* restore a1 from call[i+1]'s stack frame */
 | 
						|
    l32e    a2, a5,  -8     /* restore a2 from call[i+1]'s stack frame */
 | 
						|
    l32e    a3, a5,  -4     /* restore a3 from call[i+1]'s stack frame */
 | 
						|
    rfwu
 | 
						|
 | 
						|
/*
 | 
						|
--------------------------------------------------------------------------------
 | 
						|
Handle alloca exception generated by interruptee executing 'movsp'.
 | 
						|
This uses space between the window vectors, so is essentially "free".
 | 
						|
All interruptee's regs are intact except a0 which is saved in EXCSAVE_1,
 | 
						|
and PS.EXCM has been set by the exception hardware (can't be interrupted).
 | 
						|
The fact the alloca exception was taken means the registers associated with
 | 
						|
the base-save area have been spilled and will be restored by the underflow
 | 
						|
handler, so those 4 registers are available for scratch.
 | 
						|
The code is optimized to avoid unaligned branches and minimize cache misses.
 | 
						|
--------------------------------------------------------------------------------
 | 
						|
*/
 | 
						|
 | 
						|
    .align  4
 | 
						|
    .global _xt_alloca_exc
 | 
						|
_xt_alloca_exc:
 | 
						|
 | 
						|
    rsr     a0, WINDOWBASE  /* grab WINDOWBASE before rotw changes it */
 | 
						|
    rotw    -1              /* WINDOWBASE goes to a4, new a0-a3 are scratch */
 | 
						|
    rsr     a2, PS
 | 
						|
    extui   a3, a2, XCHAL_PS_OWB_SHIFT, XCHAL_PS_OWB_BITS
 | 
						|
    xor     a3, a3, a4      /* bits changed from old to current windowbase */
 | 
						|
    rsr     a4, EXCSAVE_1   /* restore original a0 (now in a4) */
 | 
						|
    slli    a3, a3, XCHAL_PS_OWB_SHIFT
 | 
						|
    xor     a2, a2, a3      /* flip changed bits in old window base */
 | 
						|
    wsr     a2, PS          /* update PS.OWB to new window base */
 | 
						|
    rsync
 | 
						|
 | 
						|
    _bbci.l a4, 31, _WindowUnderflow4
 | 
						|
    rotw    -1              /* original a0 goes to a8 */
 | 
						|
    _bbci.l a8, 30, _WindowUnderflow8
 | 
						|
    rotw    -1
 | 
						|
    j               _WindowUnderflow12
 | 
						|
 | 
						|
/*
 | 
						|
--------------------------------------------------------------------------------
 | 
						|
Window Overflow Exception for Call8
 | 
						|
 | 
						|
Invoked if a call[i] referenced a register (a4-a15)
 | 
						|
that contains data from ancestor call[j];
 | 
						|
call[j] had done a call8 to call[j+1].
 | 
						|
On entry here:
 | 
						|
    window rotated to call[j] start point;
 | 
						|
        a0-a7 are registers to be saved;
 | 
						|
        a8-a15 must be preserved;
 | 
						|
        a9 is call[j+1]'s stack pointer.
 | 
						|
--------------------------------------------------------------------------------
 | 
						|
*/
 | 
						|
 | 
						|
    .org    0x80
 | 
						|
    .global _WindowOverflow8
 | 
						|
_WindowOverflow8:
 | 
						|
 | 
						|
    s32e    a0, a9, -16     /* save a0 to call[j+1]'s stack frame */
 | 
						|
    l32e    a0, a1, -12     /* a0 <- call[j-1]'s sp
 | 
						|
                               (used to find end of call[j]'s frame) */
 | 
						|
    s32e    a1, a9, -12     /* save a1 to call[j+1]'s stack frame */
 | 
						|
    s32e    a2, a9,  -8     /* save a2 to call[j+1]'s stack frame */
 | 
						|
    s32e    a3, a9,  -4     /* save a3 to call[j+1]'s stack frame */
 | 
						|
    s32e    a4, a0, -32     /* save a4 to call[j]'s stack frame */
 | 
						|
    s32e    a5, a0, -28     /* save a5 to call[j]'s stack frame */
 | 
						|
    s32e    a6, a0, -24     /* save a6 to call[j]'s stack frame */
 | 
						|
    s32e    a7, a0, -20     /* save a7 to call[j]'s stack frame */
 | 
						|
    rfwo                    /* rotates back to call[i] position */
 | 
						|
 | 
						|
/*
 | 
						|
--------------------------------------------------------------------------------
 | 
						|
Window Underflow Exception for Call8
 | 
						|
 | 
						|
Invoked by RETW returning from call[i+1] to call[i]
 | 
						|
where call[i]'s registers must be reloaded (not live in ARs);
 | 
						|
where call[i] had done a call8 to call[i+1].
 | 
						|
On entry here:
 | 
						|
        window rotated to call[i] start point;
 | 
						|
        a0-a7 are undefined, must be reloaded with call[i].reg[0..7];
 | 
						|
        a8-a15 must be preserved (they are call[i+1].reg[0..7]);
 | 
						|
        a9 is call[i+1]'s stack pointer.
 | 
						|
--------------------------------------------------------------------------------
 | 
						|
*/
 | 
						|
 | 
						|
    .org    0xC0
 | 
						|
    .global _WindowUnderflow8
 | 
						|
_WindowUnderflow8:
 | 
						|
 | 
						|
    l32e    a0, a9, -16     /* restore a0 from call[i+1]'s stack frame */
 | 
						|
    l32e    a1, a9, -12     /* restore a1 from call[i+1]'s stack frame */
 | 
						|
    l32e    a2, a9,  -8     /* restore a2 from call[i+1]'s stack frame */
 | 
						|
    l32e    a7, a1, -12     /* a7 <- call[i-1]'s sp
 | 
						|
                               (used to find end of call[i]'s frame) */
 | 
						|
    l32e    a3, a9,  -4     /* restore a3 from call[i+1]'s stack frame */
 | 
						|
    l32e    a4, a7, -32     /* restore a4 from call[i]'s stack frame */
 | 
						|
    l32e    a5, a7, -28     /* restore a5 from call[i]'s stack frame */
 | 
						|
    l32e    a6, a7, -24     /* restore a6 from call[i]'s stack frame */
 | 
						|
    l32e    a7, a7, -20     /* restore a7 from call[i]'s stack frame */
 | 
						|
    rfwu
 | 
						|
 | 
						|
/*
 | 
						|
--------------------------------------------------------------------------------
 | 
						|
Window Overflow Exception for Call12
 | 
						|
 | 
						|
Invoked if a call[i] referenced a register (a4-a15)
 | 
						|
that contains data from ancestor call[j];
 | 
						|
call[j] had done a call12 to call[j+1].
 | 
						|
On entry here:
 | 
						|
    window rotated to call[j] start point;
 | 
						|
        a0-a11 are registers to be saved;
 | 
						|
        a12-a15 must be preserved;
 | 
						|
        a13 is call[j+1]'s stack pointer.
 | 
						|
--------------------------------------------------------------------------------
 | 
						|
*/
 | 
						|
 | 
						|
    .org    0x100
 | 
						|
    .global _WindowOverflow12
 | 
						|
_WindowOverflow12:
 | 
						|
 | 
						|
    s32e    a0,  a13, -16   /* save a0 to call[j+1]'s stack frame */
 | 
						|
    l32e    a0,  a1,  -12   /* a0 <- call[j-1]'s sp
 | 
						|
                               (used to find end of call[j]'s frame) */
 | 
						|
    s32e    a1,  a13, -12   /* save a1 to call[j+1]'s stack frame */
 | 
						|
    s32e    a2,  a13,  -8   /* save a2 to call[j+1]'s stack frame */
 | 
						|
    s32e    a3,  a13,  -4   /* save a3 to call[j+1]'s stack frame */
 | 
						|
    s32e    a4,  a0,  -48   /* save a4 to end of call[j]'s stack frame */
 | 
						|
    s32e    a5,  a0,  -44   /* save a5 to end of call[j]'s stack frame */
 | 
						|
    s32e    a6,  a0,  -40   /* save a6 to end of call[j]'s stack frame */
 | 
						|
    s32e    a7,  a0,  -36   /* save a7 to end of call[j]'s stack frame */
 | 
						|
    s32e    a8,  a0,  -32   /* save a8 to end of call[j]'s stack frame */
 | 
						|
    s32e    a9,  a0,  -28   /* save a9 to end of call[j]'s stack frame */
 | 
						|
    s32e    a10, a0,  -24   /* save a10 to end of call[j]'s stack frame */
 | 
						|
    s32e    a11, a0,  -20   /* save a11 to end of call[j]'s stack frame */
 | 
						|
    rfwo                    /* rotates back to call[i] position */
 | 
						|
 | 
						|
/*
 | 
						|
--------------------------------------------------------------------------------
 | 
						|
Window Underflow Exception for Call12
 | 
						|
 | 
						|
Invoked by RETW returning from call[i+1] to call[i]
 | 
						|
where call[i]'s registers must be reloaded (not live in ARs);
 | 
						|
where call[i] had done a call12 to call[i+1].
 | 
						|
On entry here:
 | 
						|
        window rotated to call[i] start point;
 | 
						|
        a0-a11 are undefined, must be reloaded with call[i].reg[0..11];
 | 
						|
        a12-a15 must be preserved (they are call[i+1].reg[0..3]);
 | 
						|
        a13 is call[i+1]'s stack pointer.
 | 
						|
--------------------------------------------------------------------------------
 | 
						|
*/
 | 
						|
 | 
						|
    .org 0x140
 | 
						|
    .global _WindowUnderflow12
 | 
						|
_WindowUnderflow12:
 | 
						|
 | 
						|
    l32e    a0,  a13, -16   /* restore a0 from call[i+1]'s stack frame */
 | 
						|
    l32e    a1,  a13, -12   /* restore a1 from call[i+1]'s stack frame */
 | 
						|
    l32e    a2,  a13,  -8   /* restore a2 from call[i+1]'s stack frame */
 | 
						|
    l32e    a11, a1,  -12   /* a11 <- call[i-1]'s sp
 | 
						|
                               (used to find end of call[i]'s frame) */
 | 
						|
    l32e    a3,  a13,  -4   /* restore a3 from call[i+1]'s stack frame */
 | 
						|
    l32e    a4,  a11, -48   /* restore a4 from end of call[i]'s stack frame */
 | 
						|
    l32e    a5,  a11, -44   /* restore a5 from end of call[i]'s stack frame */
 | 
						|
    l32e    a6,  a11, -40   /* restore a6 from end of call[i]'s stack frame */
 | 
						|
    l32e    a7,  a11, -36   /* restore a7 from end of call[i]'s stack frame */
 | 
						|
    l32e    a8,  a11, -32   /* restore a8 from end of call[i]'s stack frame */
 | 
						|
    l32e    a9,  a11, -28   /* restore a9 from end of call[i]'s stack frame */
 | 
						|
    l32e    a10, a11, -24   /* restore a10 from end of call[i]'s stack frame */
 | 
						|
    l32e    a11, a11, -20   /* restore a11 from end of call[i]'s stack frame */
 | 
						|
    rfwu
 | 
						|
 | 
						|
#endif /* XCHAL_HAVE_WINDOWED */
 | 
						|
 | 
						|
    .section    .UserEnter.text, "ax"
 | 
						|
    .global     call_user_start
 | 
						|
    .type       call_user_start,@function
 | 
						|
    .align      4
 | 
						|
    .literal_position
 |