/****************************************************************************
* arch/xtensa/src/common/xtensa_macros.S
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
****************************************************************************/
.file "xtensa_macros.S"
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#include <arch/irq.h>
#include <arch/chip/core-isa.h>
#include <arch/xtensa/xtensa_abi.h>
#include <arch/xtensa/xtensa_specregs.h>
/****************************************************************************
* Name: setintstack
*
* Description:
* Set the current stack pointer to the "top" the interrupt stack.
* Single CPU case.
* Must be provided by MCU-specific logic in the SMP case.
*
****************************************************************************/
#if !defined(CONFIG_SMP) && CONFIG_ARCH_INTERRUPTSTACK > 15
.macro setintstack tmp1 tmp2
/* Load g_intstacktop (the start of the interrupt stack) */
movi \tmp1, g_intstacktop
/* If a1 < g_intstackalloc (outside the interrupt stack boundary),
* set a1 (sp) to g_intstacktop (switch to the interrupt stack).
*/
movi \tmp2, g_intstackalloc /* Load the end (low address) of the interrupt stack */
sub \tmp2, a1, \tmp2
movltz a1, \tmp1, \tmp2
/* If a1 >= g_intstacktop, sp is outside the interrupt stack boundaries */
movi \tmp2, g_intstacktop /* Load the start (high address) of the interrupt stack */
sub \tmp2, a1, \tmp2
movgez a1, \tmp1, \tmp2
/* If neither movltz and movgez moved g_intstacktop (on /tmp1) to a1,
* it means that the stack pointer was already pointing to the interrupt
* stack and no action is required.
*/
.endm
#endif
/****************************************************************************
* Macro: ps_setup
*
* Description:
* Set up PS for C, enable interrupts above this level and clear EXCM.
*
* Entry Conditions:
* level - interrupt level
* tmp - scratch register
*
* Side Effects:
* PS and scratch register modified
*
* Assumptions:
* - PS.EXCM = 1, C calling disabled
*
****************************************************************************/
.macro ps_setup level tmp
#if 0 /* Nested interrupts no yet supported */
# ifdef __XTENSA_CALL0_ABI__
/* Disable interrupts at level and below */
movi \tmp, PS_INTLEVEL(\level) | PS_UM
# else
movi \tmp, PS_INTLEVEL(\level) | PS_UM | PS_WOE
# endif
#else
# ifdef __XTENSA_CALL0_ABI__
/* Disable all low- and medium-priority interrupts. Nested are not yet
* supported.
*/
.ifgt \level - XCHAL_EXCM_LEVEL
movi \tmp, PS_INTLEVEL(\level) | PS_UM
.else
movi \tmp, PS_INTLEVEL(XCHAL_EXCM_LEVEL) | PS_UM
.endif
# else
.ifgt \level - XCHAL_EXCM_LEVEL
movi \tmp, PS_INTLEVEL(\level) | PS_UM | PS_WOE
.else
movi \tmp, PS_INTLEVEL(XCHAL_EXCM_LEVEL) | PS_UM | PS_WOE
.endif
# endif
#endif
wsr \tmp, PS
rsync
.endm
/****************************************************************************
* Name: ps_excp_read
*
* Description:
* Read from the correct PS register corresponding to the current interrupt
* level.
*
****************************************************************************/
.macro ps_excp_read tmp level
.ifeq \level - 1
rsr \tmp, PS
.else
rsr \tmp, EPS_2 + \level - 2
.endif
.endm
/****************************************************************************
* Name: ps_excp_write
*
* Description:
* Write to the correct PS register corresponding to the current interrupt
* level.
*
****************************************************************************/
.macro ps_excp_write tmp level
.ifeq \level - 1
wsr \tmp, PS
.else
wsr \tmp, EPS_2 + \level - 2
.endif
.endm
/****************************************************************************
* Name: exception_backtrace
*
* Description:
* Populate the base save area with the pre-exception A0 and SP to be able
* to backtrace from it.
*
****************************************************************************/
.macro exception_backtrace sa level
#if !defined(__XTENSA_CALL0_ABI__) && defined(CONFIG_XTENSA_INTBACKTRACE)
l32i a3, \sa, (4 * REG_A0) /* Copy pre-exception a0 (return address) */
s32e a3, sp, -16
l32i a3, \sa, (4 * REG_A1) /* Copy pre-exception a1 (stack pointer) */
s32e a3, sp, -12
/* 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 for debug backtrace */
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 to simulate a call4 size */
#endif
.endm
/****************************************************************************
* Name: exception_entry
*
* Description:
* Create an interrupt frame and save level specific registers. The rest of
* registers will be saved by xtensa_context_save.
*
****************************************************************************/
.macro exception_entry level
mov a0, sp /* Save SP in A0 */
addi sp, sp, -XCPTCONTEXT_SIZE /* Allocate interrupt stack frame */
s32i a0, sp, (4 * REG_A1) /* Save pre-interrupt SP */
ps_excp_read a0 \level /* Save interruptee's PS */
s32i a0, sp, (4 * REG_PS)
rsr a0, EPC_1 + \level - 1 /* Save interruptee's PC */
s32i a0, sp, (4 * REG_PC)
#ifdef CONFIG_XTENSA_HAVE_GENERAL_EXCEPTION_HOOKS
/* Perform chip-specific exception entry operations */
exception_entry_hook \level sp a0
#endif
rsr a0, EXCSAVE_1 + \level - 1 /* Save interruptee's a0 */
s32i a0, sp, (4 * REG_A0)
s32i a2, sp, (4 * REG_A2)
.endm
/****************************************************************************
* Name: exception_exit
*
* Description:
* Restore level specific registers, the rest should have been restored
* before calling this macro.
*
****************************************************************************/
.macro exception_exit level
l32i a0, a2, (4 * REG_PS) /* Retrieve interruptee's PS */
ps_excp_write a0 \level
l32i a0, a2, (4 * REG_PC) /* Retrieve interruptee's PC */
wsr a0, EPC_1 + \level - 1
#ifdef CONFIG_XTENSA_HAVE_GENERAL_EXCEPTION_HOOKS
/* Perform chip-specific exception exit operations */
exception_exit_hook \level a2 a0 a1
#endif
l32i a0, a2, (4 * REG_A0) /* Retrieve interruptee's A0 */
l32i sp, a2, (4 * REG_A1) /* Retrieve interrupt stack frame */
l32i a2, a2, (4 * REG_A2) /* Retrieve interruptee's A2 */
rsync /* Ensure PS and EPC written */
.endm