#************************************************************************
#*
#*             Copyright 2012 Mentor Graphics Corporation
#*                         All Rights Reserved.
#*
#* THIS WORK CONTAINS TRADE SECRET AND PROPRIETARY INFORMATION WHICH IS
#* THE PROPERTY OF MENTOR GRAPHICS CORPORATION OR ITS LICENSORS AND IS
#* SUBJECT TO LICENSE TERMS.
#*
#************************************************************************

#************************************************************************
#*
#*  FILE NAME
#*
#*      tensilica.s
#*
#*  DESCRIPTION
#*
#*      This file contains the base ARM CSGNU specific functions
#*
#*  FUNCTIONS
#*
#*      ESAL_AR_STK_Unsolicited_Restore
#*      ESAL_AR_STK_Unsolicited_Switch
#*      ESAL_AR_STK_Startup_SP_Set
#*      ESAL_AR_ISR_Vector_Table
#*      ESAL_AR_ISR_External_Handler
#*      ESAL_AR_ISR_Critical_Handler
#*      ESAL_AR_ISR_Exception_Handler
#*      ESAL_TS_RTE_Lowlevel_Initialize
#*
#*  DEPENDENCIES
#*
#*      nucleus_gen_cfg.h
#*      tensilica_defs.inc                              Tensilica include file
#*
#*************************************************************************

#****************************
#* INCLUDE NECESSARY FILES  *
#****************************
    #include <xtensa/coreasm.h>
    #include <xtensa/config/system.h>
    #include <xtensa/config/specreg.h>
    #include <xtensa/config/core.h>
    #include "nucleus_gen_cfg.h"
    #include "tensilica_defs.inc"

#**********************************
#* EXTERNAL FUNCTION DECLARATIONS *
#**********************************

    .extern ESAL_AR_STK_Startup_SP_Set
    .extern ESAL_AR_STK_Unsolicited_Switch
    .extern ESAL_PR_ISR_Save_Coproc
    .extern ESAL_TS_RTE_Lowlevel_Initialize
    .extern OS_Init_Entry

#**********************************
#* EXTERNAL VARIABLE DECLARATIONS *
#**********************************

    .extern ESAL_GE_MEM_ROM_Support_Enabled
    .extern ESAL_GE_ISR_OS_Entry
    .extern ESAL_GE_ISR_OS_Nested_Entry
    .extern ESAL_GE_ISR_Exception_Handler
    .extern ESAL_GE_ISR_Executing
    .extern ESAL_GE_STK_System_SP
    .extern ESAL_GE_STK_Unsol_Switch_Req
    .extern ESAL_AR_ISR_Common_Return
    .extern ESAL_GE_STK_Unsol_Switch_Req
    .extern ESAL_GE_STK_Unsol_Switch_OS_Entry
    .extern ESAL_GE_STK_System_SP
    .extern __stack
    .extern TCD_Current_Thread
    .extern TCD_Execute_Task

#if ESAL_AR_ISR_HOOK_ENABLED

    .extern ISR_Start_Time

#endif

#************************************************************************
#*
#*   FUNCTION
#*
#*       ESAL_Entry
#*
#*   DESCRIPTION
#*
#*       This function is the entry point into ESAL.  Entry to this
#*       function is normally done through the reset mechanism
#*       (reset vector, jump from reset handler, etc) on the given
#*       processor / architecture.
#*
#*   CALLED BY
#*
#*       Reset
#*
#*   CALLS
#*
#*       ESAL_AR_STK_Startup_SP_Set
#*
#*   INPUTS
#*
#*       None
#*
#*   OUTPUTS
#*
#*       None
#*
#***********************************************************************/
#VOID    ESAL_Entry(VOID)
#********************
#* RESET VECTOR
#********************

    .section                    .ResetVector.text, "ax"

    .global _ResetVector
    .align  4

_ResetVector:
    j    ESAL_Entry
    .literal_position

    .global   ESAL_Entry
    .align    4

ESAL_Entry:

    /* we will use a0 as an initializer register so it contains zero during reset. */
    movi    a0, 0

    /* At this point, all interrupts are unwanted, shuting down by clearing INTENABLE. */
    wsr     a0, INTENABLE

    /* configure the interrupt level */
    rsil    a1, XCHAL_DEBUGLEVEL - 1

#if XCHAL_HAVE_WINDOWED
    /* Xtensa Windowed ABI windowing mechanism initialization */
    wsr     a0, WINDOWBASE
    rsync
    movi    a1, 1
    wsr     a1, WINDOWSTART
#endif

    /* clear interrupt conditions by writing the INTCLEAR register. */
    movi    a2, XCHAL_INTTYPE_MASK_EXTERN_EDGE | XCHAL_INTTYPE_MASK_SOFTWARE
    wsr     a2, INTCLEAR

    /* clear the PS.EXCM bit to enable the looping mechanisms.*/
    movi    a2, XCHAL_DEBUGLEVEL - 1
    wsr     a2, PS
    rsync

#if XCHAL_CP_NUM > 0
    /* Enable all co-processors to allow their use immediately. */
    movi    a2, 0xFFFFFFFF              /* set ALL bits, to allow dynamic TIE */
    wsr     a2, CPENABLE                /* no hurry, no need to rsync */
#endif

    /* Check if running from ROM (ROM support enabled) */

    movi  a8, ESAL_GE_MEM_ROM_Support_Enabled
    l32i  a7, a8, 0
    beqz  a7, ESAL_Entry_ROM_Support_End

ESAL_Entry_ROM_Support_End:

    movi a2,ESAL_AR_STK_Startup_SP_Set
    jx  a2

    .text

#************************************************************************
#*
#*  FUNCTION
#*
#*      ESAL_AR_ISR_Vector_Table
#*
#*  DESCRIPTION
#*
#*      Identifies the start address of the actual vector table /
#*      exception handling code utilized by the target processor /
#*      architecture.  Other interrupt related data structures
#*      (priority tables / etc) will be defined in their respective
#*      esal_xx_isr.c file.
#*
#*  CALLED BY
#*
#*      Interrupt
#*
#*  CALLS
#*
#*      Interrupt Service Routine
#*
#*  INPUTS
#*
#*      None
#*
#*  OUTPUTS
#*
#*      None
#*
#************************************************************************
#VOID ESAL_AR_ISR_Vector_Table(VOID)

    .align 4
    .globl ESAL_AR_ISR_Vector_Table
ESAL_AR_ISR_Vector_Table:

#**************************************************************************
#      _DoubleExceptionVector
#**************************************************************************

#ifdef XCHAL_DOUBLEEXC_VECTOR_VADDR


    # double exceptions are not a normal occurrence in this port of nucleus.
    # they indicate a bug of some kind.  for now, just loop infinitely.

    .section .DoubleExceptionVector.text, "ax"

    .global _DoubleExceptionVector
    .align  4

_DoubleExceptionVector:
    j       _DoubleExceptionVector_1
    .literal_position

_DoubleExceptionVector_1:

    # does not return
    movi    a0, ESAL_AR_ISR_SYSTEM_PANIC
    callx0  a0

    # make a0 point here not later
    nop

    .org    XSHAL_DOUBLEEXC_VECTOR_SIZE

    .text

#endif


#************************************************************************
#      _KernelExceptionVector
#***********************************************************************
# VOID _KernelExceptionVector(VOID)

    .section .KernelExceptionVector.text, "ax"

    .global _KernelExceptionVector
    .align  4

_KernelExceptionVector:
    j       _KernelExceptionVector_1
    .literal_position

_KernelExceptionVector_1:

    wsr     a3, EXCSAVE_1
    movi    a3, ESAL_AR_ISR_UserExceptionHandler
    jx      a3

    .text


#************************************************************************
#      _UserExceptionVector
#***********************************************************************
# VOID _UserExceptionVector(VOID)

    .section .UserExceptionVector.text, "ax"

    .global _UserExceptionVector
    .align  4

_UserExceptionVector:
    j       _UserExceptionVector_1
    .literal_position

_UserExceptionVector_1:

    wsr     a3, EXCSAVE_1
    movi    a3, ESAL_AR_ISR_UserExceptionHandler
    jx      a3

    .text

#************************************************************************
#    XTENSA WINDOW EXCEPTION VECTORS
#***********************************************************************

#if XCHAL_HAVE_WINDOWED

    .section .WindowVectors.text, "ax"

    # GENERAL NOTES:
    # 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.
    # The caller could also potentially assume that the callee's a0 and a1
    # (its own a4&a5 if call4, a8&a9 if call8, a12&a13 if call12)
    # are correct for whatever reason (not a clean thing to do in general,
    # but if it's possible, unless the ABI explicitly prohibits it,
    # it will eventually be done :) -- whether the the ABI needs to
    # prohibit this is a different question).

#************************************************************************
#      _WindowOverflow4
#***********************************************************************

    .align  64
    .global _WindowOverflow4
_WindowOverflow4:

    # save a0 to call[j+1]'s stack frame
    s32e    a0, a5, -16

    # save a1 to call[j+1]'s stack frame
    s32e    a1, a5, -12

    # save a2 to call[j+1]'s stack frame
    s32e    a2, a5,  -8

    # save a3 to call[j+1]'s stack frame
    s32e    a3, a5,  -4

    # rotates back to call[i] position
    rfwo

#************************************************************************
#      _WindowUnderflow4
#************************************************************************

    .align  64
    .global _WindowUnderflow4
_WindowUnderflow4:

    # restore a0 from call[i+1]'s stack frame
    l32e    a0, a5, -16

    # restore a1 from call[i+1]'s stack frame
    l32e    a1, a5, -12

    # restore a2 from call[i+1]'s stack frame
    l32e    a2, a5,  -8

    # restore a3 from call[i+1]'s stack frame
    l32e    a3, a5,  -4

    rfwu

#************************************************************************
#      _WindowOverflow8
#************************************************************************

    .align  64
    .global _WindowOverflow8
_WindowOverflow8:

    # save a0 to call[j+1]'s stack frame
    s32e    a0, a9, -16

    # a0 <- call[j-1]'s sp (used to find end of call[j]'s frame)
    l32e    a0, a1, -12

    # save a1 to call[j+1]'s stack frame
    s32e    a1, a9, -12

    # save a2 to call[j+1]'s stack frame
    s32e    a2, a9,  -8

    # save a3 to call[j+1]'s stack frame
    s32e    a3, a9,  -4

    # save a4 to call[j]'s stack frame
    s32e    a4, a0, -32

    # save a5 to call[j]'s stack frame
    s32e    a5, a0, -28

    # save a6 to call[j]'s stack frame
    s32e    a6, a0, -24

    # save a7 to call[j]'s stack frame
    s32e    a7, a0, -20

    # rotates back to call[i] position
    rfwo

#************************************************************************
#      _WindowUnderflow8
#************************************************************************

    .align  64
    .global _WindowUnderflow8
_WindowUnderflow8:

    # restore a0 from call[i+1]'s stack frame
    l32e    a0, a9, -16

    # restore a1 from call[i+1]'s stack frame
    l32e    a1, a9, -12

    # restore a2 from call[i+1]'s stack frame
    l32e    a2, a9,  -8

    # a7 <- call[i-1]'s sp (used to find end of call[i]'s frame)
    l32e    a7, a1, -12

    # restore a3 from call[i+1]'s stack frame
    l32e    a3, a9,  -4

    # restore a4 from call[i]'s stack frame
    l32e    a4, a7, -32

    # restore a5 from call[i]'s stack frame
    l32e    a5, a7, -28

    # restore a6 from call[i]'s stack frame
    l32e    a6, a7, -24

    # restore a7 from call[i]'s stack frame
    l32e    a7, a7, -20

    rfwu

#************************************************************************
#      _WindowOverflow12
#************************************************************************

    .align  64
    .global _WindowOverflow12
_WindowOverflow12:

    # save a0 to call[j+1]'s stack frame
    s32e    a0,  a13, -16

    # a0 <- call[j-1]'s sp (used to find end of call[j]'s frame)
    l32e    a0,  a1,  -12

    # save a1 to call[j+1]'s stack frame
    s32e    a1,  a13, -12

    # save a2 to call[j+1]'s stack frame
    s32e    a2,  a13,  -8

    # save a3 to call[j+1]'s stack frame
    s32e    a3,  a13,  -4

    # save a4 to end of call[j]'s stack frame
    s32e    a4,  a0,  -48

    # save a5 to end of call[j]'s stack frame
    s32e    a5,  a0,  -44

    # save a6 to end of call[j]'s stack frame
    s32e    a6,  a0,  -40

    # save a7 to end of call[j]'s stack frame
    s32e    a7,  a0,  -36

    # save a8 to end of call[j]'s stack frame
    s32e    a8,  a0,  -32

    # save a9 to end of call[j]'s stack frame
    s32e    a9,  a0,  -28

    # save a10 to end of call[j]'s stack frame
    s32e    a10, a0,  -24

    # save a11 to end of call[j]'s stack frame
    s32e    a11, a0,  -20

    # rotates back to call[i] position
    rfwo

#************************************************************************
#      _WindowUnderflow12
#************************************************************************

    .align  64
    .global _WindowUnderflow12
_WindowUnderflow12:

    # restore a0 from call[i+1]'s stack frame
    l32e    a0,  a13, -16

    # restore a1 from call[i+1]'s stack frame
    l32e    a1,  a13, -12

    # restore a2 from call[i+1]'s stack frame
    l32e    a2,  a13,  -8

    # a11 <- call[i-1]'s sp (used to find end of call[i]'s frame)
    l32e    a11, a1,  -12

    # restore a3 from call[i+1]'s stack frame
    l32e    a3,  a13,  -4

    # restore a4 from end of call[i]'s stack frame
    l32e    a4,  a11, -48

    # restore a5 from end of call[i]'s stack frame
    l32e    a5,  a11, -44

    # restore a6 from end of call[i]'s stack frame
    l32e    a6,  a11, -40

    # restore a7 from end of call[i]'s stack frame
    l32e    a7,  a11, -36

    # restore a8 from end of call[i]'s stack frame
    l32e    a8,  a11, -32

    # restore a9 from end of call[i]'s stack frame
    l32e    a9,  a11, -28

    # restore a10 from end of call[i]'s stack frame
    l32e    a10, a11, -24

    # restore a11 from end of call[i]'s stack frame
    l32e    a11, a11, -20

    rfwu

    .text

#endif

#************************************************************************
# MEDIUM-PRIORITY INTERRUPT VECTORS
#***********************************************************************

    # Notes:
    # Medium-priority interrupts are defined as interrupts at priority
    # levels such that 2 <= priority <= XCHAL_EXCM_LEVEL. These have
    # higher priority than level 1 interrupts, and yet are maskable by
    # the Nucleus kernel and thus can interact with Nucleus. Any higher
    # priority interrupts that are configured (priority > XCHAL_EXCM_LEVEL)
    # are called high-priority interrupts and cannot interact with the
    # Nucleus kernel (they generally must be handled in assembly not in C).

#************************************************************************
#      _Level2Vector
#************************************************************************

#if XCHAL_EXCM_LEVEL >= 2
    # Level 2 medium-priority interrupt vector:

    .section                .Level2InterruptVector.text, "ax"

    .global _Level2Vector
    .align  4

_Level2Vector:
    j       _Level2Vector_1
    .literal_position

_Level2Vector_1:

    wsr     a3, EXCSAVE_2
    movi    a3, ESAL_AR_ISR_Level2Handler
    jx      a3

    .org    XSHAL_INTLEVEL2_VECTOR_SIZE

    .text

#endif

#************************************************************************
#    _Level3Vector : Level 3 medium-priority interrupt vector:
#***********************************************************************

#if  XCHAL_EXCM_LEVEL >= 3

    .section                .Level3InterruptVector.text, "ax"

    .global _Level3Vector
    .align  4

_Level3Vector:
    j       _Level3Vector_1
    .literal_position

_Level3Vector_1:

    wsr     a3, EXCSAVE_3
    movi    a3, ESAL_AR_ISR_Level3Handler
    jx      a3

    .org    XSHAL_INTLEVEL3_VECTOR_SIZE

    .text

#endif

#************************************************************************
#    _Level4Vector: Level 4 medium-priority interrupt vector
#***********************************************************************

#if  XCHAL_EXCM_LEVEL >= 4

    .section                .Level4InterruptVector.text, "ax"

    .global _Level4Vector
    .align  4

_Level4Vector:
    j       _Level4Vector_1
    .literal_position

_Level4Vector_1:

    wsr     a3, EXCSAVE_4
    movi    a3, ESAL_AR_ISR_Level4Handler
    jx      a3

    .org    XSHAL_INTLEVEL4_VECTOR_SIZE

    .text

#endif

#************************************************************************
#    _Level5Vector: Level 5 medium-priority interrupt vector
#***********************************************************************

#if  XCHAL_EXCM_LEVEL >= 5

    .section                .Level5InterruptVector.text, "ax"

    .global _Level5Vector
    .align  4

_Level5Vector:
    j       _Level5Vector_1
    .literal_position

_Level5Vector_1:

    wsr     a3, EXCSAVE_5
    movi    a3, ESAL_AR_ISR_Level5Handler
    jx      a3

    .org    XSHAL_INTLEVEL5_VECTOR_SIZE

    .text

#endif

#************************************************************************
#    _Level6Vector: Level 6 medium-priority interrupt vector
#***********************************************************************

#if  XCHAL_EXCM_LEVEL >= 6

    .section                .Level6InterruptVector.text, "ax"

    .global _Level6Vector
    .align  4

_Level6Vector:
    j       _Level6Vector_1
    .literal_position

_Level6Vector_1:

    wsr     a3, EXCSAVE_6
    movi    a3, ESAL_AR_ISR_Level6Handler
    jx      a3

    .org    XSHAL_INTLEVEL6_VECTOR_SIZE

    .text

#endif

#**********************************************
#* Level 7 Non Maskable Interrupt vector
#**********************************************

#if XCHAL_HAVE_NMI

    .section                .NMIExceptionVector.text, "ax"

    .global _NMIExceptionVector
    .align  4

_NMIExceptionVector:
    j       _NMIExceptionVector_1
    .literal_position

_NMIExceptionVector_1:

    # does not return
    movi    a0, ESAL_AR_ISR_SYSTEM_PANIC
    callx0  a0

    # make a0 point here not later
    nop

    .text

#endif

#************************************************************************
#*      ESAL_AR_ISR_UserExceptionHandler
#************************************************************************

    .literal_position

    .align  4
    .globl  ESAL_AR_ISR_UserExceptionHandler
ESAL_AR_ISR_UserExceptionHandler:

    # Compute interrupt stack frame
    addi    a3, sp, -(ESAL_AR_STK_FRAME_SIZE + ESAL_AR_STK_SAVE_AREA_SIZE)

    # save a0-a2
    ESAL_AR_STK_MIN_SAVE

    # Determine cause of user exception
    rsr     a0, EXCCAUSE

    # Go to alloca handler
    beqi    a0, EXCCAUSE_ALLOCA, ESAL_AR_ISR_USER_Alloca

#if XCHAL_CP_NUM > 0
    # Go to coprocessor handler, a co-processor has been accessed
    # while it was disabled
    bgei    a0, XCHAL_EXCCAUSE_COPROCESSOR0_DISABLED, ESAL_AR_ISR_Coproc
#endif

    # not level-1 int
    bnei    a0, EXCCAUSE_LEVEL1_INTERRUPT, ESAL_AR_ISR_USER_Spurious

#if (ESAL_AR_ISR_HOOK_ENABLED == 1)

    # Save ccount value directly to the variable
    wsr     a1, EXCSAVE_2                   # Store a1
    movi    a0, ISR_Start_Time
    rsr     a1, 234                         # read ccount
    s32i    a1, a0,0
    rsr     a1, EXCSAVE_2                   # Rstore a1

#endif

    # Save EXCCAUSE for later dispatch
    s32i    a0, a3, ESAL_AR_STK_OFFSET_EXCCAUSE

    # Vector code saved a3 in EXCSAVE_n
    rsr     a1, EXCSAVE_1
    rsr     a2, EPC_1
    s32i    a1, a3, ESAL_AR_STK_OFFSET_A3
    s32i    a2, a3, ESAL_AR_STK_OFFSET_PC

    rsr     a1, PS

    # PS value to use for C code, keep all the interrupts disabled
#if XCHAL_HAVE_WINDOWED
    movi    a2, PS_WOE + PS_UM + XCHAL_EXCM_LEVEL
#else
    movi    a2, PS_UM + XCHAL_EXCM_LEVEL
#endif

    # Save Exception PS
    s32i    a1, a3, ESAL_AR_STK_OFFSET_PS

    # Mask of interrupt at this level
    movi    a1, XCHAL_INTLEVEL1_MASK

    # Jump to common interrupt handler
    call0   ESAL_AR_ISR_Common_Handler

Level1Dispatch:

    #  Here: a3==saved PS, sp==exception frame. Restore a0-a3 and return.
    wsr     a3, PS

    # Recompute Exception Stack Frame
    addi    a3, sp, -(ESAL_AR_STK_FRAME_SIZE + ESAL_AR_STK_SAVE_AREA_SIZE)

    l32i    a2, a3, ESAL_AR_STK_OFFSET_EXCCAUSE        # Save EXCCAUSE for later dispatch
    wsr     a2, EXCCAUSE

    l32i    a2, a3, ESAL_AR_STK_OFFSET_PC
    wsr     a2, EPC_1
    rsync

    # Restore a0-a3 and return.
    ESAL_AR_STK_MIN_RESTORE

    esync                                   # Ensure EPC has been written

    rfe

ESAL_AR_ISR_USER_Spurious:

    # does not return
    movi    a0, ESAL_AR_ISR_SYSTEM_PANIC
    callx0  a0

ESAL_AR_ISR_USER_Alloca:

    # alloca exception handler avoids using the stack to store temporaries
    # because that might overlap with the new stack pointer area requested.
    # so restore from the stack before jumping to the alloca handler:

    # Restore a0
    l32i    a0, a3, ESAL_AR_STK_OFFSET_A0
    movi    a3, ESAL_AR_ISR_Alloca_Handler
    jx      a3

#if XCHAL_CP_NUM > 0
ESAL_AR_ISR_Coproc:
    j       ESAL_AR_ISR_CoprocHandler
#endif

#************************************************************************
#
# FUNCTION
#
#      ESAL_AR_ISR_Level2Handler
#
# DESCRIPTION
#
#
# CALLED BY
#
#      Level 2 Interrupt Handler
#
# CALLS
#
#      ESAL_AR_ISR_Common_Handler
#
# INPUTS
#
#      None
#
# OUTPUTS
#
#      None
#
#***********************************************************************

#if  XCHAL_EXCM_LEVEL >= 2

    .align  4
    .global ESAL_AR_ISR_Level2Handler
ESAL_AR_ISR_Level2Handler:

    # Compute interrupt stack frame
    addi    a3, sp, -(ESAL_AR_STK_FRAME_SIZE + ESAL_AR_STK_SAVE_AREA_SIZE)

    # save a0-a2
    ESAL_AR_STK_MIN_SAVE

    # we will treat every interrupt as level 1
    movi    a0, EXCCAUSE_LEVEL1_INTERRUPT

    # save fake EXCCAUSE on interrupt stack
    s32i    a0, a3, ESAL_AR_STK_OFFSET_EXCCAUSE

    rsr     a0, EPC_2
    rsr     a1, EXCSAVE_2
    s32i    a0, a3, ESAL_AR_STK_OFFSET_PC
    s32i    a1, a3, ESAL_AR_STK_OFFSET_A3
    rsr     a1, EPS_2
    s32i    a1, a3, ESAL_AR_STK_OFFSET_PS

    # PS value to use for C code, keep all the interrupts disabled
#if XCHAL_HAVE_WINDOWED
    movi    a2, PS_WOE + PS_UM + XCHAL_EXCM_LEVEL
#else
    movi    a2, PS_UM + XCHAL_EXCM_LEVEL
#endif

    # put masks of interrupts at this level in a1
    movi    a1, XCHAL_INTLEVEL2_MASK

    call0   ESAL_AR_ISR_Common_Handler

Level2Dispatch:

    #  a3==saved PS, sp==exception frame.
    wsr     a3, EPS_2

    # Recompute Exception Stack Frame
    addi    a3, sp, -(ESAL_AR_STK_FRAME_SIZE + ESAL_AR_STK_SAVE_AREA_SIZE)

    # Restore EPC_2
    l32i    a2, a3, ESAL_AR_STK_OFFSET_PC
    wsr     a2, EPC_2
    rsync

    # Restore a0-a3 and return.
    ESAL_AR_STK_MIN_RESTORE

    # Ensure EPS and EPC have been written
    esync

    # return from interrupt
    rfi     2

#endif

#************************************************************************
#
# FUNCTION
#
#      ESAL_AR_ISR_Level3Handler
#
# DESCRIPTION
#
#
# CALLED BY
#
#      Level 3 Interrupt Handler
#
# CALLS
#
#      ESAL_AR_ISR_Common_Handler
#
# INPUTS
#
#      None
#
# OUTPUTS
#
#      None
#
#***********************************************************************

#if  XCHAL_EXCM_LEVEL >= 3

    .align  4
    .global ESAL_AR_ISR_Level3Handler
ESAL_AR_ISR_Level3Handler:

    # Compute interrupt stack frame
    addi    a3, sp, -(ESAL_AR_STK_FRAME_SIZE + ESAL_AR_STK_SAVE_AREA_SIZE)

    # save a0-a2
    ESAL_AR_STK_MIN_SAVE

    # we will treat every interrupt as level 1
    movi    a0, EXCCAUSE_LEVEL1_INTERRUPT

    # save fake EXCCAUSE on interrupt stack
    s32i    a0, a3, ESAL_AR_STK_OFFSET_EXCCAUSE

    rsr     a0, EPC_3
    rsr     a1, EXCSAVE_3
    s32i    a0, a3, ESAL_AR_STK_OFFSET_PC
    s32i    a1, a3, ESAL_AR_STK_OFFSET_A3
    rsr     a1, EPS_3
    s32i    a1, a3, ESAL_AR_STK_OFFSET_PS

    # PS value to use for C code, keep all the interrupts disabled
#if XCHAL_HAVE_WINDOWED
    movi    a2, PS_WOE + PS_UM + XCHAL_EXCM_LEVEL
#else
    movi    a2, PS_UM + XCHAL_EXCM_LEVEL
#endif

    # put masks of interrupts at this level in a1
    movi    a1, XCHAL_INTLEVEL3_MASK

    call0   ESAL_AR_ISR_Common_Handler

Level3Dispatch:

    #  a3==saved PS, sp==exception frame.
    wsr     a3, EPS_3

    # Recompute Exception Stack Frame
    addi    a3, sp, -(ESAL_AR_STK_FRAME_SIZE + ESAL_AR_STK_SAVE_AREA_SIZE)

    # Restore EPC_3
    l32i    a2, a3, ESAL_AR_STK_OFFSET_PC
    wsr     a2, EPC_3
    rsync

    # Restore a0-a3 and return.
    ESAL_AR_STK_MIN_RESTORE

    # Ensure EPS and EPC have been written
    esync

    # return from interrupt
    rfi     3

#endif

#************************************************************************
#
# FUNCTION
#
#      ESAL_AR_ISR_Level4Handler
#
# DESCRIPTION
#
#
# CALLED BY
#
#      Level 4 Interrupt Handler
#
# CALLS
#
#      ESAL_AR_ISR_Common_Handler
#
# INPUTS
#
#      None
#
# OUTPUTS
#
#      None
#
#***********************************************************************

#if  XCHAL_EXCM_LEVEL >= 4

    .align  4
    .global ESAL_AR_ISR_Level4Handler
ESAL_AR_ISR_Level4Handler:

    # Compute interrupt stack frame
    addi    a3, sp, -(ESAL_AR_STK_FRAME_SIZE + ESAL_AR_STK_SAVE_AREA_SIZE)

    # save a0-a2
    ESAL_AR_STK_MIN_SAVE

    # we will treat every interrupt as level 1
    movi    a0, EXCCAUSE_LEVEL1_INTERRUPT

    # save fake EXCCAUSE on interrupt stack
    s32i    a0, a3, ESAL_AR_STK_OFFSET_EXCCAUSE

    rsr     a0, EPC_4
    rsr     a1, EXCSAVE_4
    s32i    a0, a3, ESAL_AR_STK_OFFSET_PC
    s32i    a1, a3, ESAL_AR_STK_OFFSET_A3
    rsr     a1, EPS_4
    s32i    a1, a3, ESAL_AR_STK_OFFSET_PS

    # PS value to use for C code, keep all the interrupts disabled
#if XCHAL_HAVE_WINDOWED
    movi    a2, PS_WOE + PS_UM + XCHAL_EXCM_LEVEL
#else
    movi    a2, PS_UM + XCHAL_EXCM_LEVEL
#endif

    # put masks of interrupts at this level in a1
    movi    a1, XCHAL_INTLEVEL4_MASK

    call0   ESAL_AR_ISR_Common_Handler

Level4Dispatch:

    #  a3==saved PS, sp==exception frame.
    wsr     a3, EPS_4

    # Recompute Exception Stack Frame
    addi    a3, sp, -(ESAL_AR_STK_FRAME_SIZE + ESAL_AR_STK_SAVE_AREA_SIZE)

    # Restore EPC_4
    l32i    a2, a3, ESAL_AR_STK_OFFSET_PC
    wsr     a2, EPC_4
    rsync

    # Restore a0-a3 and return.
    ESAL_AR_STK_MIN_RESTORE

    # Ensure EPS and EPC have been written
    esync

    # return from interrupt
    rfi     4

#endif

#************************************************************************
#
# FUNCTION
#
#      ESAL_AR_ISR_Level5Handler
#
# DESCRIPTION
#
#
# CALLED BY
#
#      Level 4 Interrupt Handler
#
# CALLS
#
#      ESAL_AR_ISR_Common_Handler
#
# INPUTS
#
#      None
#
# OUTPUTS
#
#      None
#
#***********************************************************************

#if  XCHAL_EXCM_LEVEL >= 5

    .align  4
    .global ESAL_AR_ISR_Level5Handler
ESAL_AR_ISR_Level5Handler:

    # Compute interrupt stack frame
    addi    a3, sp, -(ESAL_AR_STK_FRAME_SIZE + ESAL_AR_STK_SAVE_AREA_SIZE)

    # save a0-a2
    ESAL_AR_STK_MIN_SAVE

    # we will treat every interrupt as level 1
    movi    a0, EXCCAUSE_LEVEL1_INTERRUPT

    # save fake EXCCAUSE on interrupt stack
    s32i    a0, a3, ESAL_AR_STK_OFFSET_EXCCAUSE

    rsr     a0, EPC_5
    rsr     a1, EXCSAVE_5
    s32i    a0, a3, ESAL_AR_STK_OFFSET_PC
    s32i    a1, a3, ESAL_AR_STK_OFFSET_A3
    rsr     a1, EPS_5
    s32i    a1, a3, ESAL_AR_STK_OFFSET_PS

    # PS value to use for C code, keep all the interrupts disabled
#if XCHAL_HAVE_WINDOWED
    movi    a2, PS_WOE + PS_UM + XCHAL_EXCM_LEVEL
#else
    movi    a2, PS_UM + XCHAL_EXCM_LEVEL
#endif

    # put masks of interrupts at this level in a1
    movi    a1, XCHAL_INTLEVEL5_MASK

    call0   ESAL_AR_ISR_Common_Handler

Level5Dispatch:

    #  a3==saved PS, sp==exception frame.
    wsr     a3, EPS_5

    # Recompute Exception Stack Frame
    addi    a3, sp, -(ESAL_AR_STK_FRAME_SIZE + ESAL_AR_STK_SAVE_AREA_SIZE)

    # Restore EPC_5
    l32i    a2, a3, ESAL_AR_STK_OFFSET_PC
    wsr     a2, EPC_5
    rsync

    # Restore a0-a3 and return.
    ESAL_AR_STK_MIN_RESTORE

    # Ensure EPS and EPC have been written
    esync

    # return from interrupt
    rfi     5

#endif

#************************************************************************
#
# FUNCTION
#
#      ESAL_AR_ISR_Level6Handler
#
# DESCRIPTION
#
#
# CALLED BY
#
#      Level 6 Interrupt Handler
#
# CALLS
#
#      ESAL_AR_ISR_Common_Handler
#
# INPUTS
#
#      None
#
# OUTPUTS
#
#      None
#
#***********************************************************************
#if  XCHAL_EXCM_LEVEL >= 6

    .align  4
    .global ESAL_AR_ISR_Level6Handler
ESAL_AR_ISR_Level6Handler:

    # Compute interrupt stack frame
    addi    a3, sp, -(ESAL_AR_STK_FRAME_SIZE + ESAL_AR_STK_SAVE_AREA_SIZE)

    # save a0-a2
    ESAL_AR_STK_MIN_SAVE

    # we will treat every interrupt as level 1
    movi    a0, EXCCAUSE_LEVEL1_INTERRUPT

    # save fake EXCCAUSE on interrupt stack
    s32i    a0, a3, ESAL_AR_STK_OFFSET_EXCCAUSE

    rsr     a0, EPC_6
    rsr     a1, EXCSAVE_6
    s32i    a0, a3, ESAL_AR_STK_OFFSET_PC
    s32i    a1, a3, ESAL_AR_STK_OFFSET_A3
    rsr     a1, EPS_6
    s32i    a1, a3, ESAL_AR_STK_OFFSET_PS

    # PS value to use for C code, keep all the interrupts disabled
#if XCHAL_HAVE_WINDOWED
    movi    a2, PS_WOE + PS_UM + XCHAL_EXCM_LEVEL
#else
    movi    a2, PS_UM + XCHAL_EXCM_LEVEL
#endif

    # put masks of interrupts at this level in a1
    movi    a1, XCHAL_INTLEVEL6_MASK

    call0   ESAL_AR_ISR_Common_Handler

Level6Dispatch:

    #  a3==saved PS, sp==exception frame.
    wsr     a3, EPS_6

    # Recompute Exception Stack Frame
    addi    a3, sp, -(ESAL_AR_STK_FRAME_SIZE + ESAL_AR_STK_SAVE_AREA_SIZE)

    # Restore EPC_6
    l32i    a2, a3, ESAL_AR_STK_OFFSET_PC
    wsr     a2, EPC_6
    rsync

    # Restore a0-a3 and return.
    ESAL_AR_STK_MIN_RESTORE

    # Ensure EPS and EPC have been written
    esync

    # return from interrupt
    rfi     6

#endif

#************************************************************************
#
# FUNCTION
#
#      ESAL_AR_ISR_Common_Handler
#
# DESCRIPTION
#
#
# CALLED BY
#
#
# CALLS
#
#      None
#
# INPUTS
#
#      None
#
# OUTPUTS
#
#      None
#
#***********************************************************************

    .align  4
    .global ESAL_AR_ISR_Common_Handler
ESAL_AR_ISR_Common_Handler:

    # save away a0 contains dispatch return PC
    s32i    a0, a3, ESAL_AR_STK_OFFSET_TYPE

    # save away a1 contains interrupts at this level
    s32i    a1, a3, ESAL_AR_STK_OFFSET_THISLEVEL

    # save away a2 contains value for PS needed to call C code.
    s32i    a2, a3, ESAL_AR_STK_OFFSET_PS_C

    # call to xthal_window_spill_nw may destory registers values

    # save a4-a15 and looping registers
    ESAL_AR_STK_FULL_SAVE

    # save SAR register
    rsr     a0, SAR
    s32i    a0, a3, ESAL_AR_STK_OFFSET_SAR

    # restore sp as required for to call xthal_window_spill_nw
    l32i    sp, a3, ESAL_AR_STK_OFFSET_A1

#if XCHAL_HAVE_WINDOWED
    # write (spill) any cached windows to stack
    movi    a0, xthal_window_spill_nw
    callx0  a0
#endif

    # a3 was clobbered recompute exception stack frame
    addi    a3, sp, -(ESAL_AR_STK_FRAME_SIZE + ESAL_AR_STK_SAVE_AREA_SIZE)

#if XCHAL_EXTRA_SA_SIZE > 0
    # Load the offset to the extra register base
    addi    a2, a3, ESAL_AR_STK_OFFSET_EXTRA

    # Save the extra registers on the stack
    movi    a0, xthal_save_extra_nw
    callx0  a0

    # a3 was clobbered (again) recompute exception stack frame
    addi    a3, sp, -(ESAL_AR_STK_FRAME_SIZE + ESAL_AR_STK_SAVE_AREA_SIZE)
#endif

    # mark base of calling stack (for backtraces)
    movi    a0, 0

    # Setup PS for calling C code
    l32i    a2, a3, ESAL_AR_STK_OFFSET_PS_C
    wsr     a2, PS
    rsync

    # setup first parameter vector id
#if XCHAL_HAVE_WINDOWED
    movi a6, ESAL_AR_LEVEL1_INT_VECTOR_ID
#else
    movi a2, ESAL_AR_LEVEL1_INT_VECTOR_ID
#endif

    # Get flag to show if ISR is executing already
    movi  a4, ESAL_GE_ISR_Executing
    l32i  a5, a4, 0

    # jump to nested interrupt handler if an interrupt is currently executing
    bnez  a5, ESAL_AR_ISR_Nested_Handler

    # put current stack pointer in second parameter register
#if XCHAL_HAVE_WINDOWED
    or a7, a1, a1
#else
    or a3, a1, a1
#endif

    # first isr - switch to the system stack
    movi  a5, ESAL_GE_STK_System_SP
    l32i  sp, a5, 0

    # Call OS non-nested ISR function
    movi  a5, ESAL_GE_ISR_OS_Entry
    l32i  a4, a5, 0
#if XCHAL_HAVE_WINDOWED
    callx4  a4
#else
    callx0  a4
#endif

    # Get value of flag used to show if context switch is required
    movi  a8, ESAL_GE_STK_Unsol_Switch_Req
    l32i    a9, a8,0

    # Branch to unsolicited switch if required
    bnez  a9, ESAL_AR_STK_Unsolicited_Switch

    # No switch is required, read thread sp from pointer to sp returned
#if XCHAL_HAVE_WINDOWED
    l32i    a3, a6, 0
#else
    l32i    a3, a2, 0
#endif

    or   sp, a3, a3

    # jump to common point of return from interrupt
    movi a0, ESAL_AR_ISR_Common_Return
    jx a0

#************************************************************************
#
# FUNCTION
#
#      ESAL_AR_ISR_Nested_Handler
#
# DESCRIPTION
#
#      Nested Interrupts Common Handler
#
# CALLED BY
#
#      ESAL_AR_ISR_Common_Handler
#
# INPUTS
#
#      None
#
# OUTPUTS
#
#      None
#
#***********************************************************************

    .align 4
    .global ESAL_AR_ISR_Nested_Handler
ESAL_AR_ISR_Nested_Handler:

    # Update sp to avoid stomping saved registers
    addi    sp, sp, -(ESAL_AR_STK_FRAME_SIZE + ESAL_AR_STK_SAVE_AREA_SIZE)

    # Call OS nested ISR function
    movi  a5, ESAL_GE_ISR_OS_Nested_Entry
    l32i  a4, a5, 0
#if XCHAL_HAVE_WINDOWED
    callx4  a4
#else
    callx0  a4
#endif

    # update stack pointer for restore
    addi    sp, sp, (ESAL_AR_STK_FRAME_SIZE + ESAL_AR_STK_SAVE_AREA_SIZE)

    # jump to common point of return from interrupt
    movi a0, ESAL_AR_ISR_Common_Return
    jx a0

#if XCHAL_CP_NUM > 0
#************************************************************************
#
# FUNCTION
#
#      ESAL_AR_ISR_CoprocHandler
#
# DESCRIPTION
#
#
# CALLED BY
#
#
# CALLS
#
#      None
#
# INPUTS
#
#      None
#
# OUTPUTS
#
#      None
#
#***********************************************************************
    .align 4
    .global ESAL_AR_ISR_CoprocHandler
ESAL_AR_ISR_CoprocHandler:
    # Save minimal registers for C call
    ESAL_AR_STK_FULL_SAVE

    # Save EPC
    rsr     a2, EPC_1
    s32i    a2, a3, ESAL_AR_STK_OFFSET_PC

    # Save Exception PS
    rsr     a2, PS
    s32i    a2, a3, ESAL_AR_STK_OFFSET_PS

    # PS value to use for C code, keep all the interrupts disabled
#if XCHAL_HAVE_WINDOWED
    movi    a2, PS_WOE + PS_UM + XCHAL_EXCM_LEVEL
#else
    movi    a2, PS_UM + XCHAL_EXCM_LEVEL
#endif
    wsr     a2, PS

    # pass the exception cause
#if XCHAL_HAVE_WINDOWED
    rsr     a6, EXCCAUSE
#else
    rsr     a2, EXCCAUSE
#endif
     
    # call c level coprocessor save handler
    movi    a4, ESAL_PR_ISR_Save_Coproc
#if XCHAL_HAVE_WINDOWED
    callx4  a4
#else
    callx0  a4
#endif
    
    # Recompute Exception Stack Frame
    addi    a3, sp, -(ESAL_AR_STK_FRAME_SIZE + ESAL_AR_STK_SAVE_AREA_SIZE)

    ESAL_AR_STK_FULL_RESTORE

    # Restore saved PS and EPC_1
    l32i    a2, a3, ESAL_AR_STK_OFFSET_PS
    wsr     a2, PS

    l32i    a2, a3, ESAL_AR_STK_OFFSET_PC
    wsr     a2, EPC_1
    rsync

    # Restore a0-a3 and return.
    ESAL_AR_STK_MIN_RESTORE

    esync                                   # Ensure EPC has been written

    rfe

#endif

#************************************************************************
#
# FUNCTION
#
#      ESAL_AR_ISR_Common_Return
#
# DESCRIPTION
#
#
# CALLED BY
#
#      ESAL_AR_ISR_Common_Handler
#
# CALLS
#
#      None
#
# INPUTS
#
#      None
#
# OUTPUTS
#
#      None
#
#***********************************************************************

    .align 4
    .global ESAL_AR_ISR_Common_Return
ESAL_AR_ISR_Common_Return:

    # Recompute Exception Stack Frame
    addi    a3, sp, -(ESAL_AR_STK_FRAME_SIZE + ESAL_AR_STK_SAVE_AREA_SIZE)

    l32i    a2, a3, ESAL_AR_STK_OFFSET_SAR
    wsr     a2, SAR

#if XCHAL_EXTRA_SA_SIZE > 0
    # Load the offset to the extra register base
    addi    a2,  a3, ESAL_AR_STK_OFFSET_EXTRA

    # Restore the extra registers
    movi    a0, xthal_restore_extra_nw
    callx0  a0

    # Restore call will destroy, recompute exception Stack Frame
    addi    a3, sp, -(ESAL_AR_STK_FRAME_SIZE + ESAL_AR_STK_SAVE_AREA_SIZE)
#endif

    # restore a4-a15 and looping registers
    ESAL_AR_STK_FULL_RESTORE

    l32i    a0, a3, ESAL_AR_STK_OFFSET_TYPE
    l32i    a3, a3, ESAL_AR_STK_OFFSET_PS

    # jump to corresponding dispatch routine
    jx      a0

#************************************************************************
#
# FUNCTION
#
#      ESAL_AR_ISR_Alloca_Handler
#
# DESCRIPTION
#
#      The ALLOCA handler is entered when user code executes the MOVSP
#      instruction and the caller's frame is not in the register file;
#      in this case the caller frame's a0..a3 are on the stack just below
#      sp (a1), and need to be moved atomically here.
#
#      This handler only works for "MOVSP sp,<as>", ie. where the
#      destination register is a1.  Other cases are not checked and may
#      cause unpredictable behavior.
#
# CALLED BY
#
#      Exception handler
#
# CALLS
#
#      None
#
# INPUTS
#
#      None
#
# OUTPUTS
#
#      None
#
#***********************************************************************

    .comm   reg_space, 16, 4

    .align  4
    .global ESAL_AR_ISR_Alloca_Handler
ESAL_AR_ISR_Alloca_Handler:

    movi    a3, reg_space
    s32i    a0, a3, 0                       # Save a0 to memory
    rsr     a0, EPC_1
    s32i    a2, a3, 4                       # Save a2 to memory
    addi    a0, a0, 3                       # Increment PC to skip MOVSP
    wsr     a0, EPC_1

    # extract the relevant instruction byte.  we must be careful to
    # read it using only a 32-bit load (in case it resides in IRAM
    # which only supports 32-bit accesses).

# Save a4 to memory
    s32i    a4, a3, 8
    # Save SAR, used below
    rsr     a4, SAR
    # Point to byte of MOVSP with reg. number
    addi    a0, a0, -2
    # Get bits 0 and 1 of address of this byte
    extui   a2, a0, 0, 2
    # Align address (down) to 32-bit boundary
    sub     a0, a0, a2
    # Get word containing byte
    l32i    a0, a0, 0

    #  NOTE: possible addition: verify dest register is indeed sp (a1).

#if XCHAL_HAVE_BE

    ssa8b   a2
    sll     a0, a0
    # Extract source register number
    extui   a0, a0, 28, 4

#else

    ssa8l   a2
    srl     a0, a0

    # Extract source register number
    extui   a0, a0, 0, 4
#endif

    # MOVSP source register number is in a0.
    # Use a jump table to move its contents to a2.

    movi    a2, jmptable

    # restore SAR
    wsr     a4, SAR
    addx8   a2, a0, a2
    jx      a2

    # Move the source register to a2:
    .align 8
jmptable:       l32i a2, a3, 0     ; j move_save_area   # MOVSP sp,a0
    .align 8 ;  mov  a2, sp        ; j move_save_area   # MOVSP sp,sp
    .align 8 ;  l32i a2, a3, 4     ; j move_save_area   # MOVSP sp,a2
    .align 8 ;  rsr  a2, EXCSAVE_1 ; j move_save_area   # MOVSP sp,a3
    .align 8 ;  l32i a2, a3, 8     ; j move_save_area   # MOVSP sp,a4
    .align 8 ;  mov  a2, a5        ; j move_save_area   # MOVSP sp,a5
    .align 8 ;  mov  a2, a6        ; j move_save_area   # MOVSP sp,a6
    .align 8 ;  mov  a2, a7        ; j move_save_area   # MOVSP sp,a7
    .align 8 ;  mov  a2, a8        ; j move_save_area   # MOVSP sp,a8
    .align 8 ;  mov  a2, a9        ; j move_save_area   # MOVSP sp,a9
    .align 8 ;  mov  a2, a10       ; j move_save_area   # MOVSP sp,a10
    .align 8 ;  mov  a2, a11       ; j move_save_area   # MOVSP sp,a11
    .align 8 ;  mov  a2, a12       ; j move_save_area   # MOVSP sp,a12
    .align 8 ;  mov  a2, a13       ; j move_save_area   # MOVSP sp,a13
    .align 8 ;  mov  a2, a14       ; j move_save_area   # MOVSP sp,a14
    .align 8 ;  mov  a2, a15       ; j move_save_area   # MOVSP sp,a15

    # Move register save area
    .align 4
move_save_area:
    addi    sp, sp, -16
    addi    a2, a2, -16

    l32i    a0, sp, 0
    l32i    a4, sp, 4
    s32i    a0, a2, 0
    s32i    a4, a2, 4
    l32i    a0, sp, 8
    l32i    a4, sp, 12
    s32i    a0, a2, 8
    s32i    a4, a2, 12

    # Set new sp
    addi    sp, a2, 16

    # Restore a0 from memory
    l32i    a0, a3, 0

    # Restore a2 from memory
    l32i    a2, a3, 4

    # Restore a4 from memory
    l32i    a4, a3, 8

    # Restore a3
    rsr     a3, EXCSAVE_1

    rfe

#************************************************************************
#
# FUNCTION
#
#      ESAL_AR_ISR_SYSTEM_PANIC
#
# DESCRIPTION
#
#      Called unrecoverable system failure occurs
#
# CALLED BY
#
#
#
# CALLS
#
#      None
#
# INPUTS
#
#      None
#
# OUTPUTS
#
#      None
#
#***********************************************************************
    .align  4
    .global ESAL_AR_ISR_SYSTEM_PANIC
ESAL_AR_ISR_SYSTEM_PANIC:
#if XCHAL_HAVE_WINDOWED
    entry   sp, 16
#endif

    # loop infinitely
ESAL_AR_ISR_Loop:
    j       ESAL_AR_ISR_Loop

#************************************************************************
#*
#*   FUNCTION
#*
#*       ESAL_AR_STK_Unsolicited_Restore
#*
#*   DESCRIPTION
#*
#*       This function restores the context of a stack frame as required
#*       by a given architecture (stack frame that contains all registers
#*       used by a given architecture)
#*
#*   CALLED BY
#*
#*       Operating System Services
#*
#*   CALLS
#*
#*       None
#*
#*   INPUTS
#*
#*       stack_ptr                           Stack pointer of
#*                                           stack frame to be restored
#*
#*   OUTPUTS
#*
#*       None
#*
#************************************************************************
#VOID    ESAL_AR_STK_Unsolicited_Restore(VOID *stack_ptr)
    .align 4
    .globl ESAL_AR_STK_Unsolicited_Restore
ESAL_AR_STK_Unsolicited_Restore:
#if XCHAL_HAVE_WINDOWED
    entry   sp, 32
#endif

    # Save passed in stack pointer
    wsr     a2, EXCSAVE_1

    # Disable Interrupts
    rsil    a0, XCHAL_EXCM_LEVEL

#if XCHAL_EXTRA_SA_SIZE > 0
    # Load the offset to the extra register base
    addi    a2,  a2, ESAL_AR_STK_OFFSET_EXTRA

    # Restore the extra registers
    movi    a0, xthal_restore_extra_nw
    callx0  a0
#endif

#if XCHAL_CP_NUM > 0
    movi    a0,TCD_Current_Thread       /* Get current thread */
    l32i    a0, a0, 0                   /* Dereference current thread */

    movi    a2,TCD_Execute_Task         /* Get current task */
    l32i    a2, a2, 0                   /* Dereference current task */

    bne     a0, a2, ESAL_AR_STK_SKIP_CP /* Determine if current thread is current task */

    addi    a0, a0, 0x28                /* Offset to tc_stack_end */
    l32i    a0, a0, 0                   /* Dereference tc_stack_end */

    addi    a0, a0, -4                  /* Point to CPENABLE value */
    l32i    a0, a0, 0                   /* Read saved CPENABLE value */
    wsr     a0, CPENABLE                /* Restore CPENABLE value */
ESAL_AR_STK_SKIP_CP:
#endif

#if XCHAL_HAVE_WINDOWED
    /* Reset windows start and base. */
    movi    a0, 1
    wsr     a0, WINDOWSTART
    movi    a0, 0
    wsr     a0, WINDOWBASE
    rsync
#endif

    # Restore passed in stack pointer to A3 for use by RESTORE macros
    rsr     a3, EXCSAVE_1

    ESAL_AR_STK_FULL_RESTORE

    l32i    a0, a3, ESAL_AR_STK_OFFSET_SAR
    wsr     a0, SAR

    /* Set PS.EXCM bit and clear PS.INTLEVEL field in the saved PS */
    l32i    a0, a3, ESAL_AR_STK_OFFSET_PS
    movi    a2, PS_EXCM
    or      a0, a0, a2
    movi    a2, ESAL_AR_STK_PS_INTLEVEL_CLEAR
    and     a0, a0, a2

    or      sp, a3, a3

    #  Here: a0==saved PS, sp==exception frame. Restore a0-a3 and return.
    wsr     a0, PS

    l32i    a0, a3, ESAL_AR_STK_OFFSET_PC
    wsr     a0, EPC_1
    rsync

    # Restore a0-a3 and return.
    ESAL_AR_STK_MIN_RESTORE

    esync                                   # Ensure EPC has been written

    rfe

#***********************************************************************
#*
#*   FUNCTION
#*
#*       ESAL_AR_STK_Unsolicited_Switch
#*
#*   DESCRIPTION
#*
#*       This function saves the entire architecture context on the
#*       given stack and passes control to the OS
#*
#*   CALLED BY
#*
#*       Operating System Services
#*
#*   CALLS
#*
#*       None
#*
#*   INPUTS
#*
#*       r3                                  Pointer to stack pointer
#*
#*   OUTPUTS
#*
#*       None
#*
#***********************************************************************/
#VOID  ESAL_AR_STK_Unsolicited_Switch(VOID **stack_ptr)

    .globl ESAL_AR_STK_Unsolicited_Switch
ESAL_AR_STK_Unsolicited_Switch:

    # Switch to the stack pointer at location passed in
#if XCHAL_HAVE_WINDOWED
    l32i    a3, a6, 0
#else
    l32i    a3, a2, 0
#endif

    # Recompute Exception Stack Frame
    addi    a3, a3, -(ESAL_AR_STK_FRAME_SIZE + ESAL_AR_STK_SAVE_AREA_SIZE)

    /* Save stack type on top of stack (1 = unsolicited stack frame) */

    movi    a12, 1
    s32i    a12, a3 , ESAL_AR_STK_OFFSET_TYPE

    # Save new stack pointer back into passed-in stack pointer address

#if XCHAL_HAVE_WINDOWED
    s32i    a3, a6, 0
#else
    s32i    a3, a2, 0
#endif

    /* Clear switch flag */

    movi    a9, 0
    movi    a10, ESAL_GE_STK_Unsol_Switch_Req
    s32i    a9, a10, 0

    /* Get address of OS switch function */
    movi    a8, ESAL_GE_STK_Unsol_Switch_OS_Entry
    l32i    a4, a8, 0

    /* Jump to OS switch function */

    callx0    a4


#************************************************************************
#*
#*  FUNCTION
#*
#*      ESAL_AR_STK_Startup_SP_Set
#*
#*  DESCRIPTION
#*
#*      This function sets the architecture stack pointer to an address
#*      that can be used during initialization.  This can include on-chip
#*      SRAM, available RAM not used by the application during
#*      initialization, etc.
#*
#*  CALLED BY
#*
#*      ESAL_Entry
#*
#*  CALLS
#*
#*      ESAL_TS_RTE_Lowlevel_Initialize
#*
#*  INPUTS
#*
#*      None
#*
#*  OUTPUTS
#*
#*      None
#*
#************************************************************************
#VOID    ESAL_AR_STK_Startup_SP_Set(VOID)

    .globl ESAL_AR_STK_Startup_SP_Set
ESAL_AR_STK_Startup_SP_Set:

    /* Initialize the stack pointer. */
    movi    sp, __stack

    /* The stack only needs 16-byte alignment. */
    addi    sp, sp, -16

    /*  Now that sp (a1) is set, we can set PS as per the application
       (user vector mode, enable interrupts, enable window exceptions if applicable).
    */
#if XCHAL_HAVE_WINDOWED
    movi    a2, PS_WOE_MASK | PS_PROGSTACK_MASK
#else
    movi    a2, PS_PROGSTACK_MASK
#endif
    wsr     a2, PS
    rsync

    /* After stack pointer is set, jump to toolset run-time environment
       setup function */
    movi    a4, ESAL_TS_RTE_Lowlevel_Initialize
    jx      a4

#************************************************************************
#*
#*   FUNCTION
#*
#*       ESAL_TS_RTE_Lowlevel_Initialize
#*
#*   DESCRIPTION
#*
#*       This function initializes registers and hardware as required
#*       for a run-time environment.  Many toolsets require a certain
#*       environment to be set-up to allow correct run-time execution
#*       in a C environment.  This may include initializing certain
#*       architecture registers (base registers, mode registers, etc)
#*       and anything else specified in the toolset documentation.
#*
#*   CALLED BY
#*
#*       ESAL_AR_STK_Startup_SP_Set
#*
#*   CALLS
#*
#*       OS_Init_Entry                          OS Entry Point
#*
#*   INPUTS
#*
#*       None
#*
#*   OUTPUTS
#*
#*       None
#*
#************************************************************************
#VOID    ESAL_TS_RTE_Lowlevel_Initialize(VOID)

    .globl ESAL_TS_RTE_Lowlevel_Initialize
ESAL_TS_RTE_Lowlevel_Initialize:

    # Mark Base of Calling Stack (For Backtraces)
    movi    a0, 0

    # Branch to the OS entry point
    movi    a4, OS_Init_Entry
#if XCHAL_HAVE_WINDOWED
    callx4  a4
#else
    callx0  a4
#endif

    nop

#************************************************************************
#*
#*  FUNCTION
#*
#*      ESAL_TS_STK_Solicited_Restore
#*
#*  DESCRIPTION
#*
#*      This function restores the context of a stack frame as required
#*      by a given toolset (stack frame that contains registers
#*      required to be saved across function call boundaries)
#*
#*  CALLED BY
#*
#*      Operating System Services
#*
#*  CALLS
#*
#*      None
#*
#*  INPUTS
#*
#*      stack_ptr                           Stack pointer of
#*                                          stack frame to be restored
#*
#*  OUTPUTS
#*
#*      None
#*
#************************************************************************
#VOID    ESAL_TS_STK_Solicited_Restore(VOID *stack_ptr)

    .globl ESAL_TS_STK_Solicited_Restore
ESAL_TS_STK_Solicited_Restore:

#if XCHAL_CP_NUM > 0
    movi    a3,TCD_Current_Thread       /* Get current thread */
    l32i    a3, a3, 0                   /* Dereference current thread */

    movi    a4,TCD_Execute_Task         /* Get current task */
    l32i    a4, a4, 0                   /* Dereference current task */

    bne     a3, a4, ESAL_TS_STK_SKIP_CP /* Determine if current thread is current task */

    addi    a3, a3, 0x28                /* Offset to tc_stack_end */
    l32i    a3, a3, 0                   /* Dereference tc_stack_end */

    addi    a3, a3, -4                  /* Point to CPENABLE value */
    l32i    a3, a3, 0                   /* Read saved CPENABLE value */
    wsr     a3, CPENABLE                /* Restore CPENABLE value */
ESAL_TS_STK_SKIP_CP:
#endif

    # store stack pointer to a0
#if XCHAL_HAVE_WINDOWED
    or    a0, a10,a10
#else
    or    a0, a2,a2
#endif

#if XCHAL_HAVE_WINDOWED
    # Spill windows before stack switch
    movi    a4, xthal_window_spill
    callx4  a4
#endif

    # Switch to the stack passed into this function
    or    sp, a0,a0

#if XCHAL_EXTRA_SA_SIZE > 0
    # Load the offset to the extra register base
    addi    a2, sp, ESAL_TS_STK_OFFSET_EXTRA

    # Restore the extra registers
    movi    a0, xthal_restore_extra_nw
    callx0  a0
#endif

    # Restore context of solicited stack frame (skipping stack type)

    l32i    a0, sp, ESAL_TS_STK_OFFSET_A0

#if !XCHAL_HAVE_WINDOWED
    # Restore a12 - a15
    l32i    a12, sp, ESAL_TS_STK_OFFSET_A12
    l32i    a13, sp, ESAL_TS_STK_OFFSET_A13
    l32i    a14, sp, ESAL_TS_STK_OFFSET_A14
    l32i    a15, sp, ESAL_TS_STK_OFFSET_A15
#endif

    # Return to caller

#if XCHAL_HAVE_WINDOWED
    retw
#else
    addi    sp, sp, ESAL_TS_STK_SIZE
    ret
#endif


#************************************************************************
#*
#*  FUNCTION
#*
#*      ESAL_TS_STK_Solicited_Switch
#*
#*  DESCRIPTION
#*
#*      This function saves the necessary registers, designated by a
#*      given toolset, to be preserved across a function call
#*      boundary.  Control is then transferred to the designated
#*      call-back function with the designated call-back parameter.
#*
#*  CALLED BY
#*
#*      Operating System Services
#*
#*  CALLS
#*
#*      <call back function>
#*
#*  INPUTS
#*
#*      call_back_param                     Parameter passed to callback
#*                                          function
#*      call_back                           Call back function pointer
#*      stack_ptr                           Pointer to stack pointer
#*                                          storage address
#*
#*  OUTPUTS
#*
#*      None
#*
#************************************************************************
#VOID    ESAL_TS_STK_Solicited_Switch(VOID *call_back_param,
#                                     VOID (*call_back)(VOID),
#                                     VOID **stack_ptr)
    .align  4
    .globl ESAL_TS_STK_Solicited_Switch
ESAL_TS_STK_Solicited_Switch:

#if XCHAL_HAVE_WINDOWED
    entry   sp, 16 + ESAL_TS_STK_SIZE
#else
    addi    sp, sp, -ESAL_TS_STK_SIZE
#endif

    # Save stack pointer to specified location
    s32i     sp, a4, 0

#if XCHAL_HAVE_WINDOWED
    # Spill windows before stack switch
    movi    a4, xthal_window_spill
    callx4  a4
#endif

    # Save stack type (0 = solicited stack frame)

    movi    a4, 0
    s32i    a4, sp, ESAL_TS_STK_OFFSET_TYPE

    # Save return address
    s32i    a0, sp, ESAL_TS_STK_OFFSET_A0

#if !XCHAL_HAVE_WINDOWED
    # Save a12 - a15
    s32i    a12, sp, ESAL_TS_STK_OFFSET_A12
    s32i    a13, sp, ESAL_TS_STK_OFFSET_A13
    s32i    a14, sp, ESAL_TS_STK_OFFSET_A14
    s32i    a15, sp, ESAL_TS_STK_OFFSET_A15
#endif

#if XCHAL_EXTRA_SA_SIZE > 0
    # Save a2, a3
    or      a4, a2, a2
    or      a10, a3, a3

    # Load the offset to the extra register base
    addi    a2, sp, ESAL_TS_STK_OFFSET_EXTRA

    # Save the extra registers on the stack
    movi    a0, xthal_save_extra_nw
    callx0  a0

    # Restore a2, a3
    or      a2, a4, a4
    or      a3, a10, a10
#endif

    # Switch to the system stack

    movi    a0,ESAL_GE_STK_System_SP
    l32i    a0, a0, 0
    or      sp, a0 ,a0

    # setup call back parameter
#if XCHAL_HAVE_WINDOWED
    or  a10, a2, a2
#else
    or  a2, a2, a2
#endif

    # setup call back parameter
    movi  a0, 0

    # Jump to call-back function
#if XCHAL_HAVE_WINDOWED
    callx8    a3
#else
    callx0    a3
#endif

