Break out high-level interrupts so a component can override them

This commit is contained in:
Jeroen Domburg 2017-04-18 17:14:32 +08:00
parent 4678d81c83
commit 1d748db209
8 changed files with 489 additions and 307 deletions

View file

@ -25,12 +25,17 @@ ifndef CONFIG_SPI_FLASH_ROM_DRIVER_PATCH
LINKER_SCRIPTS += esp32.rom.spiflash.ld
endif
#ld_include_panic_highint_hdl is added as an undefined symbol because otherwise the
#linker will ignore panic_highint_hdl.S as it has no other files depending on any
#symbols in it.
COMPONENT_ADD_LDFLAGS := -lesp32 \
$(COMPONENT_PATH)/libhal.a \
-L$(COMPONENT_PATH)/lib \
$(addprefix -l,$(LIBS)) \
-L $(COMPONENT_PATH)/ld \
-T esp32_out.ld \
-u ld_include_panic_highint_hdl \
-u ld_include_dport_highint_hdl \
$(addprefix -T ,$(LINKER_SCRIPTS))
ALL_LIB_FILES := $(patsubst %,$(COMPONENT_PATH)/lib/lib%.a,$(LIBS))

View file

@ -0,0 +1,93 @@
#include <xtensa/coreasm.h>
#include <xtensa/corebits.h>
#include <xtensa/config/system.h>
#include <xtensa/simcall.h>
#include "freertos/xtensa_context.h"
#include "esp_panic.h"
#include "sdkconfig.h"
#include "soc/soc.h"
#include "soc/dport_reg.h"
#define L5_INTR_STACK_SIZE 8
#define L5_INTR_A2_OFFSET 0
#define L5_INTR_A3_OFFSET 4
.data
_l5_intr_stack:
.space L5_INTR_STACK_SIZE
.section .iram1,"ax"
.global xt_highint5
.type xt_highint5,@function
.align 4
xt_highint5:
/* This section is for access dport register protection */
/* Allocate exception frame and save minimal context. */
/* Because the interrupt cause code have protection that only
allow one cpu enter in L5 interrupt at one time, so
there needn't have two _l5_intr_stack for each cpu */
movi a0, _l5_intr_stack
s32i a2, a0, L5_INTR_A2_OFFSET
s32i a3, a0, L5_INTR_A3_OFFSET
/* Check interrupt */
rsr a0, INTERRUPT
extui a0, a0, ETS_DPORT_INUM, 1 /* get dport int bit */
beqz a0, 1f
/* handle dport interrupt */
/* get CORE_ID */
getcoreid a0
beqz a0, 2f
/* current cpu is 1 */
movi a0, DPORT_CPU_INTR_FROM_CPU_3_REG
movi a2, 0
s32i a2, a0, 0 /* clear intr */
movi a0, 0 /* other cpu id */
j 3f
2:
/* current cpu is 0 */
movi a0, DPORT_CPU_INTR_FROM_CPU_2_REG
movi a2, 0
s32i a2, a0, 0 /* clear intr */
movi a0, 1 /* other cpu id */
3:
/* set and wait flag */
movi a2, dport_access_start
addx4 a2, a0, a2
movi a3, 1
s32i a3, a2, 0
memw
movi a2, dport_access_end
addx4 a2, a0, a2
.check_dport_access_end:
l32i a3, a2, 0
beqz a3, .check_dport_access_end
1:
movi a0, _l5_intr_stack
l32i a2, a0, L5_INTR_A2_OFFSET
l32i a3, a0, L5_INTR_A3_OFFSET
rsync /* ensure register restored */
rsr a0, EXCSAVE_5 /* restore a0 */
rfi 5
.align 4
.L_xt_highint5_exit:
rsr a0, EXCSAVE_5 /* restore a0 */
rfi 5
/* The linker has no reason to link in this file; all symbols it exports are already defined
(weakly!) in the default int handler. Define a symbol here so we can use it to have the
linker inspect this anyway. */
.global ld_include_dport_highint_hdl
ld_include_dport_highint_hdl:

View file

@ -0,0 +1,128 @@
// Copyright 2015-2017 Espressif Systems (Shanghai) PTE LTD
//
// Licensed 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.
#include <xtensa/coreasm.h>
#include <xtensa/corebits.h>
#include <xtensa/config/system.h>
#include <xtensa/simcall.h>
#include "freertos/xtensa_context.h"
#include "esp_panic.h"
#include "sdkconfig.h"
#include "soc/soc.h"
/*
In some situations, the panic handler needs to be invoked even when (low/medium priority) interrupts
are disabled. In that case, we use a high interrupt to panic anyway. This is the high-level interrupt
handler for such a situation. We use interrupt level 4 for this.
*/
.section .iram1,"ax"
.global xt_highint4
.type xt_highint4,@function
.align 4
xt_highint4:
/* On the ESP32, this level is used for panic events that are detected by hardware and should
also panic when interrupts are disabled. At the moment, these are the interrupt watchdog
as well as the cache invalid access interrupt. (24 and 25) */
/* 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_4 /* 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 vaddr into exception frame */
rsr a0, EXCVADDR
s32i a0, sp, XT_STK_EXCVADDR
/* Figure out reason, save into EXCCAUSE reg */
rsr a0, INTERRUPT
extui a0, a0, ETS_CACHEERR_INUM, 1 /* get cacheerr int bit */
beqz a0, 1f
/* Kill this interrupt; we cannot reset it. */
rsr a0, INTENABLE
movi a4, ~(1<<ETS_CACHEERR_INUM)
and a0, a4, a0
wsr a0, INTENABLE
movi a0, PANIC_RSN_CACHEERR
j 9f
1:
#if CONFIG_INT_WDT_CHECK_CPU1
/* Check if the cause is the app cpu failing to tick.*/
movi a0, int_wdt_app_cpu_ticked
l32i a0, a0, 0
bnez a0, 2f
/* It is. Modify cause. */
movi a0,PANIC_RSN_INTWDT_CPU1
j 9f
2:
#endif
/* Set EXCCAUSE to reflect cause of the wdt int trigger */
movi a0,PANIC_RSN_INTWDT_CPU0
9:
/* Found the reason, now save it. */
s32i a0, sp, XT_STK_EXCCAUSE
/* _xt_context_save seems to save the current a0, but we need the interuptees a0. Fix this. */
rsr a0, EXCSAVE_4 /* 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
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_4
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 */
rsr a0, EXCSAVE_4 /* restore a0 */
rfi 4
/* The linker has no reason to link in this file; all symbols it exports are already defined
(weakly!) in the default int handler. Define a symbol here so we can use it to have the
linker inspect this anyway. */
.global ld_include_panic_highint_hdl
ld_include_panic_highint_hdl:

View file

@ -0,0 +1,20 @@
/*
Tests for the interrupt watchdog
*/
#include <esp_types.h>
#include <stdio.h>
#include "rom/ets_sys.h"
#include "unity.h"
#include "soc/dport_reg.h"
#include "soc/io_mux_reg.h"
#include "esp_intr_alloc.h"
#include "freertos/FreeRTOS.h"
TEST_CASE("Int wdt test", "[esp32][ignore]")
{
portENTER_CRITICAL_NESTED();
while(1);
}

View file

@ -0,0 +1,158 @@
// Copyright 2015-2017 Espressif Systems (Shanghai) PTE LTD
//
// Licensed 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.
#include "xtensa_rtos.h"
#include "esp_panic.h"
#include "sdkconfig.h"
#include "soc/soc.h"
/*
This file contains the default handlers for the high interrupt levels as well as some specialized exceptions.
The default behaviour is to just exit the interrupt or call the panic handler on the exceptions
*/
#if XCHAL_HAVE_DEBUG
.global xt_debugexception
.weak xt_debugexception
.set xt_debugexception, _xt_debugexception
.section .iram1,"ax"
.type _xt_debugexception,@function
.align 4
_xt_debugexception:
movi a0,PANIC_RSN_DEBUGEXCEPTION
wsr a0,EXCCAUSE
call0 _xt_panic /* does not return */
rsr a0, EXCSAVE+XCHAL_DEBUGLEVEL
rfi XCHAL_DEBUGLEVEL
#endif /* Debug exception */
#if XCHAL_NUM_INTLEVELS >=2 && XCHAL_EXCM_LEVEL <2 && XCHAL_DEBUGLEVEL !=2
.global xt_highint2
.weak xt_highint2
.set xt_highint2, _xt_highint2
.section .iram1,"ax"
.type _xt_highint2,@function
.align 4
_xt_highint2:
/* Default handler does nothing; just returns */
.align 4
.L_xt_highint2_exit:
rsr a0, EXCSAVE_2 /* restore a0 */
rfi 2
#endif /* Level 2 */
#if XCHAL_NUM_INTLEVELS >=3 && XCHAL_EXCM_LEVEL <3 && XCHAL_DEBUGLEVEL !=3
.global xt_highint3
.weak xt_highint3
.set xt_highint3, _xt_highint3
.section .iram1,"ax"
.type _xt_highint3,@function
.align 4
_xt_highint3:
/* Default handler does nothing; just returns */
.align 4
.L_xt_highint3_exit:
rsr a0, EXCSAVE_3 /* restore a0 */
rfi 3
#endif /* Level 3 */
#if XCHAL_NUM_INTLEVELS >=4 && XCHAL_EXCM_LEVEL <4 && XCHAL_DEBUGLEVEL !=4
.global xt_highint4
.weak xt_highint4
.set xt_highint4, _xt_highint4
.section .iram1,"ax"
.type _xt_highint4,@function
.align 4
_xt_highint4:
/* Default handler does nothing; just returns */
.align 4
.L_xt_highint4_exit:
rsr a0, EXCSAVE_4 /* restore a0 */
rfi 4
#endif /* Level 4 */
#if XCHAL_NUM_INTLEVELS >=5 && XCHAL_EXCM_LEVEL <5 && XCHAL_DEBUGLEVEL !=5
.global xt_highint5
.weak xt_highint5
.set xt_highint5, _xt_highint5
.section .iram1,"ax"
.type _xt_highint5,@function
.align 4
_xt_highint5:
/* Default handler does nothing; just returns */
.align 4
.L_xt_highint5_exit:
rsr a0, EXCSAVE_5 /* restore a0 */
rfi 5
#endif /* Level 5 */
#if XCHAL_NUM_INTLEVELS >=6 && XCHAL_EXCM_LEVEL <6 && XCHAL_DEBUGLEVEL !=6
.global _xt_highint6
.global xt_highint6
.weak xt_highint6
.set xt_highint6, _xt_highint6
.section .iram1,"ax"
.type _xt_highint6,@function
.align 4
_xt_highint6:
/* Default handler does nothing; just returns */
.align 4
.L_xt_highint6_exit:
rsr a0, EXCSAVE_6 /* restore a0 */
rfi 6
#endif /* Level 6 */
#if XCHAL_HAVE_NMI
.global _xt_nmi
.global xt_nmi
.weak xt_nmi
.set xt_nmi, _xt_nmi
.section .iram1,"ax"
.type _xt_nmi,@function
.align 4
_xt_nmi:
/* Default handler does nothing; just returns */
.align 4
.L_xt_nmi_exit:
rsr a0, EXCSAVE + XCHAL_NMILEVEL /* restore a0 */
rfi XCHAL_NMILEVEL
#endif /* NMI */

View file

@ -471,28 +471,10 @@ Debug Exception.
.section .DebugExceptionVector.text, "ax"
.global _DebugExceptionVector
.align 4
.global xt_debugexception
_DebugExceptionVector:
#ifdef XT_SIMULATOR
/*
In the simulator, let the debugger (if any) handle the debug exception,
or simply stop the simulation:
*/
wsr a2, EXCSAVE+XCHAL_DEBUGLEVEL /* save a2 where sim expects it */
movi a2, SYS_gdb_enter_sktloop
simcall /* have ISS handle debug exc. */
#elif 0 /* change condition to 1 to use the HAL minimal debug handler */
wsr a3, EXCSAVE+XCHAL_DEBUGLEVEL
movi a3, xthal_debugexc_defhndlr_nw /* use default debug handler */
jx a3
#else
wsr a0, EXCSAVE+XCHAL_DEBUGLEVEL /* save original a0 somewhere */
movi a0,PANIC_RSN_DEBUGEXCEPTION
wsr a0,EXCCAUSE
call0 _xt_panic /* does not return */
rfi XCHAL_DEBUGLEVEL /* make a0 point here not later */
#endif
wsr a0, EXCSAVE+XCHAL_DEBUGLEVEL /* preserve a0 */
call0 xt_debugexception /* load exception handler */
.end literal_prefix
@ -1060,9 +1042,6 @@ locking.
/* Co-processor exception occurred outside a thread (not supported). */
.L_xt_coproc_invalid:
#if XCHAL_HAVE_DEBUG
break 1, 1 /* unhandled user exception */
#endif
movi a0,PANIC_RSN_COPROCEXCEPTION
wsr a0,EXCCAUSE
call0 _xt_panic /* not in a thread (invalid) */
@ -1531,9 +1510,9 @@ the minimum necessary before jumping to the handler in the .text section.
*******************************************************************************/
/*
Currently only shells for high priority interrupt handlers are provided
here. However a template and example can be found in the Cadence Design Systems tools
documentation: "Microprocessor Programmer's Guide".
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
@ -1542,37 +1521,14 @@ documentation: "Microprocessor Programmer's Guide".
.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 */
call0 xt_highint2 /* load interrupt handler */
.end literal_prefix
.section .iram1,"ax"
.type _xt_highint2,@function
.align 4
_xt_highint2:
#ifdef XT_INTEXC_HOOKS
/* Call interrupt hook if present to (pre)handle interrupts. */
movi a0, _xt_intexc_hooks
l32i a0, a0, 2<<2
beqz a0, 1f
.Ln_xt_highint2_call_hook:
callx0 a0 /* must NOT disturb stack! */
1:
#endif
/* USER_EDIT:
ADD HIGH PRIORITY LEVEL 2 INTERRUPT HANDLER CODE HERE.
*/
.align 4
.L_xt_highint2_exit:
rsr a0, EXCSAVE_2 /* restore a0 */
rfi 2
#endif /* Level 2 */
#if XCHAL_NUM_INTLEVELS >=3 && XCHAL_EXCM_LEVEL <3 && XCHAL_DEBUGLEVEL !=3
@ -1581,38 +1537,15 @@ _xt_highint2:
.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 */
call0 xt_highint3 /* load interrupt handler */
/* never returns here - call0 is used as a jump (see note at top) */
.end literal_prefix
.section .iram1,"ax"
.type _xt_highint3,@function
.align 4
_xt_highint3:
#ifdef XT_INTEXC_HOOKS
/* Call interrupt hook if present to (pre)handle interrupts. */
movi a0, _xt_intexc_hooks
l32i a0, a0, 3<<2
beqz a0, 1f
.Ln_xt_highint3_call_hook:
callx0 a0 /* must NOT disturb stack! */
1:
#endif
/* USER_EDIT:
ADD HIGH PRIORITY LEVEL 3 INTERRUPT HANDLER CODE HERE.
*/
.align 4
.L_xt_highint3_exit:
rsr a0, EXCSAVE_3 /* restore a0 */
rfi 3
#endif /* Level 3 */
#if XCHAL_NUM_INTLEVELS >=4 && XCHAL_EXCM_LEVEL <4 && XCHAL_DEBUGLEVEL !=4
@ -1621,110 +1554,15 @@ _xt_highint3:
.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 */
call0 xt_highint4 /* load interrupt handler */
/* never returns here - call0 is used as a jump (see note at top) */
.end literal_prefix
.section .iram1,"ax"
.type _xt_highint4,@function
.align 4
_xt_highint4:
#ifdef XT_INTEXC_HOOKS
/* Call interrupt hook if present to (pre)handle interrupts. */
movi a0, _xt_intexc_hooks
l32i a0, a0, 4<<2
beqz a0, 1f
.Ln_xt_highint4_call_hook:
callx0 a0 /* must NOT disturb stack! */
1:
#endif
/* On the ESP32, this level is used for panic events that are detected by hardware and should
also panic when interrupts are disabled. At the moment, these are the interrupt watchdog
as well as the cache invalid access interrupt. (24 and 25) */
/* 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_4 /* 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 vaddr into exception frame */
rsr a0, EXCVADDR
s32i a0, sp, XT_STK_EXCVADDR
/* Figure out reason, save into EXCCAUSE reg */
rsr a0, INTERRUPT
extui a0, a0, ETS_CACHEERR_INUM, 1 /* get cacheerr int bit */
beqz a0, 1f
/* Kill this interrupt; we cannot reset it. */
rsr a0, INTENABLE
movi a4, ~(1<<ETS_CACHEERR_INUM)
and a0, a4, a0
wsr a0, INTENABLE
movi a0, PANIC_RSN_CACHEERR
j 9f
1:
#if CONFIG_INT_WDT_CHECK_CPU1
/* Check if the cause is the app cpu failing to tick.*/
movi a0, int_wdt_app_cpu_ticked
l32i a0, a0, 0
bnez a0, 2f
/* It is. Modify cause. */
movi a0,PANIC_RSN_INTWDT_CPU1
j 9f
2:
#endif
/* Set EXCCAUSE to reflect cause of the wdt int trigger */
movi a0,PANIC_RSN_INTWDT_CPU0
9:
/* Found the reason, now save it. */
s32i a0, sp, XT_STK_EXCCAUSE
/* _xt_context_save seems to save the current a0, but we need the interuptees a0. Fix this. */
rsr a0, EXCSAVE_4 /* 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
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_4
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 */
rsr a0, EXCSAVE_4 /* restore a0 */
rfi 4
#endif /* Level 4 */
#if XCHAL_NUM_INTLEVELS >=5 && XCHAL_EXCM_LEVEL <5 && XCHAL_DEBUGLEVEL !=5
@ -1733,96 +1571,15 @@ _xt_highint4:
.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 */
call0 xt_highint5 /* load interrupt handler */
/* never returns here - call0 is used as a jump (see note at top) */
.end literal_prefix
#define L5_INTR_STACK_SIZE 8
#define L5_INTR_A2_OFFSET 0
#define L5_INTR_A3_OFFSET 4
.data
_l5_intr_stack:
.space L5_INTR_STACK_SIZE
.section .iram1,"ax"
.type _xt_highint5,@function
.align 4
_xt_highint5:
#ifdef XT_INTEXC_HOOKS
/* Call interrupt hook if present to (pre)handle interrupts. */
movi a0, _xt_intexc_hooks
l32i a0, a0, 5<<2
beqz a0, 1f
.Ln_xt_highint5_call_hook:
callx0 a0 /* must NOT disturb stack! */
1:
#endif
/* This section is for access dport register protection */
/* Allocate exception frame and save minimal context. */
/* Because the interrupt cause code have protection that only
allow one cpu enter in L5 interrupt at one time, so
there needn't have two _l5_intr_stack for each cpu */
movi a0, _l5_intr_stack
s32i a2, a0, L5_INTR_A2_OFFSET
s32i a3, a0, L5_INTR_A3_OFFSET
/* Check interrupt */
rsr a0, INTERRUPT
extui a0, a0, ETS_DPORT_INUM, 1 /* get dport int bit */
beqz a0, 1f
/* handle dport interrupt */
/* get CORE_ID */
getcoreid a0
beqz a0, 2f
/* current cpu is 1 */
movi a0, DPORT_CPU_INTR_FROM_CPU_3_REG
movi a2, 0
s32i a2, a0, 0 /* clear intr */
movi a0, 0 /* other cpu id */
j 3f
2:
/* current cpu is 0 */
movi a0, DPORT_CPU_INTR_FROM_CPU_2_REG
movi a2, 0
s32i a2, a0, 0 /* clear intr */
movi a0, 1 /* other cpu id */
3:
/* set and wait flag */
movi a2, dport_access_start
addx4 a2, a0, a2
movi a3, 1
s32i a3, a2, 0
memw
movi a2, dport_access_end
addx4 a2, a0, a2
.check_dport_access_end:
l32i a3, a2, 0
beqz a3, .check_dport_access_end
1:
movi a0, _l5_intr_stack
l32i a2, a0, L5_INTR_A2_OFFSET
l32i a3, a0, L5_INTR_A3_OFFSET
rsync /* ensure register restored */
rsr a0, EXCSAVE_5 /* restore a0 */
rfi 5
.align 4
.L_xt_highint5_exit:
rsr a0, EXCSAVE_5 /* restore a0 */
rfi 5
#endif /* Level 5 */
#if XCHAL_NUM_INTLEVELS >=6 && XCHAL_EXCM_LEVEL <6 && XCHAL_DEBUGLEVEL !=6
@ -1831,38 +1588,15 @@ _xt_highint5:
.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 */
call0 xt_highint6 /* load interrupt handler */
/* never returns here - call0 is used as a jump (see note at top) */
.end literal_prefix
.section .iram1,"ax"
.type _xt_highint6,@function
.align 4
_xt_highint6:
#ifdef XT_INTEXC_HOOKS
/* Call interrupt hook if present to (pre)handle interrupts. */
movi a0, _xt_intexc_hooks
l32i a0, a0, 6<<2
beqz a0, 1f
.Ln_xt_highint6_call_hook:
callx0 a0 /* must NOT disturb stack! */
1:
#endif
/* USER_EDIT:
ADD HIGH PRIORITY LEVEL 6 INTERRUPT HANDLER CODE HERE.
*/
.align 4
.L_xt_highint6_exit:
rsr a0, EXCSAVE_6 /* restore a0 */
rfi 6
#endif /* Level 6 */
#if XCHAL_HAVE_NMI
@ -1871,38 +1605,15 @@ _xt_highint6:
.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 */
call0 xt_nmi /* load interrupt handler */
/* never returns here - call0 is used as a jump (see note at top) */
.end literal_prefix
.section .iram1,"ax"
.type _xt_nmi,@function
.align 4
_xt_nmi:
#ifdef XT_INTEXC_HOOKS
/* Call interrupt hook if present to (pre)handle interrupts. */
movi a0, _xt_intexc_hooks
l32i a0, a0, XCHAL_NMILEVEL<<2
beqz a0, 1f
.Ln_xt_nmi_call_hook:
callx0 a0 /* must NOT disturb stack! */
1:
#endif
/* USER_EDIT:
ADD HIGH PRIORITY NON-MASKABLE INTERRUPT (NMI) HANDLER CODE HERE.
*/
.align 4
.L_xt_nmi_exit:
rsr a0, EXCSAVE + XCHAL_NMILEVEL /* restore a0 */
rfi XCHAL_NMILEVEL
#endif /* NMI */

View file

@ -10,6 +10,7 @@ API Guides
ESP32 Core Dump <core_dump>
Partition Tables <partition-tables>
Flash Encryption <../security/flash-encryption>
High Level Interrupts <hlinterrupts>
Secure Boot <../security/secure-boot>
Deep Sleep Wake Stubs <deep-sleep-stub>
ULP Coprocessor <ulp>

66
docs/hlinterrupts.rst Normal file
View file

@ -0,0 +1,66 @@
High-Level Interrupts
=====================
.. toctree::
:maxdepth: 1
The Xtensa architecture has support for 32 interrupts, divided over 8 levels, plus an assortment of exceptions. On the ESP32, the interrupt
mux allows most interrupt sources to be routed to these interrupts using the :doc:`interrupt allocator <api/system/intr_alloc>`. Normally,
interrupts will be written in C, but ESP-IDF allows high-level interrupts to be written in assembly as well, allowing for very low interrupt
latencies.
Interrupt Levels
----------------
===== ================= ====================================================
Level Symbol Remark
===== ================= ====================================================
1 N/A Exception and level 0 interrupts. Handled by ESP-IDF
2-3 N/A Medium level interrupts. Handled by ESP-IDF
4 xt_highint4 Normally used by ESP-IDF debug logic
5 xt_highint5 Free to use
NMI xt_nmi Free to use
dbg xt_debugexception Debug exception. Called on e.g. a BREAK instruction.
===== ================= ====================================================
Using these symbols is done by creating an assembly file (suffix .S) and defining the named symbols, like this::
.section .iram1,"ax"
.global xt_highint5
.type xt_highint5,@function
.align 4
xt_highint5:
... your code here
rsr a0, EXCSAVE_5
rfi 5
For a real-life example, see the components/esp32/panic_highint_hdl.S file; the panic handler iunterrupt is implemented there.
Notes
-----
- Do not call C code from a high-level interrupt; because these interupts still run in critical sections, this can cause crashes.
(The panic handler interrupt does call normal C code, but this is OK because there is no intention of returning to the normal code
flow afterwards.)
- Make sure your assembly code gets linked in. If the interrupt handler symbol is the only symbol the rest of the code uses from this
file, the linker will take the default ISR instead and not link the assembly file into the final project. To get around this, in the
assembly file, define a symbol, like this::
.global ld_include_my_isr_file
ld_include_my_isr_file:
(The symbol is called ``ld_include_my_isr_file`` here but can have any arbitrary name not defined anywhere else.)
Then, in the component.mk, add this file as an unresolved symbol to the ld command line arguments::
COMPONENT_ADD_LDFLAGS := -u ld_include_my_isr_file
This should cause the linker to always include a file defining ``ld_include_my_isr_file``, causing the ISR to always be linked in.
- High-level interrupts can be routed and handled using esp_intr_alloc and associated functions. The handler and handler arguments
to esp_intr_alloc must be NULL, however.
- In theory, medium priority interrupts could also be handled in this way. For now, ESP-IDF does not support this.