Fixed interrupt watchdog error caused by live lock

This commit is contained in:
Li Shuai 2020-03-25 16:16:42 +08:00 committed by maojianxin
parent c2dac74cc5
commit fa0348b512
5 changed files with 176 additions and 58 deletions

View File

@ -6,6 +6,11 @@ menu "ESP32-specific"
default "y" if IDF_TARGET="esp32" default "y" if IDF_TARGET="esp32"
default "n" default "n"
config ESP32_ECO3_CACHE_LOCK_FIX
bool
default y
depends on !FREERTOS_UNICORE && SPIRAM_SUPPORT
choice ESP32_REV_MIN choice ESP32_REV_MIN
prompt "Minimum Supported ESP32 Revision" prompt "Minimum Supported ESP32 Revision"
default ESP32_REV_MIN_0 default ESP32_REV_MIN_0
@ -21,7 +26,7 @@ menu "ESP32-specific"
bool "Rev 2" bool "Rev 2"
config ESP32_REV_MIN_3 config ESP32_REV_MIN_3
bool "Rev 3" bool "Rev 3"
select INT_WDT if !FREERTOS_UNICORE && SPIRAM_SUPPORT select INT_WDT if ESP32_ECO3_CACHE_LOCK_FIX
endchoice endchoice
config ESP32_REV_MIN config ESP32_REV_MIN

View File

@ -389,8 +389,8 @@ void start_cpu0_default(void)
//Initialize the interrupt watch dog for CPU0. //Initialize the interrupt watch dog for CPU0.
esp_int_wdt_cpu_init(); esp_int_wdt_cpu_init();
#else #else
#if !defined(CONFIG_FREERTOS_UNICORE) && defined(CONFIG_SPIRAM_SUPPORT) #if CONFIG_ESP32_ECO3_CACHE_LOCK_FIX
assert(!soc_has_cache_lock_bug() && "Minimum Supported ESP32 Revision requires Rev 3"); assert(!soc_has_cache_lock_bug() && "ESP32 Rev 3 + Dual Core + PSRAM requires INT WDT enabled in project config!");
#endif #endif
#endif #endif
esp_cache_err_int_init(); esp_cache_err_int_init();

View File

@ -39,9 +39,9 @@ Interrupt , a high-priority interrupt, is used for several things:
#define L5_INTR_A4_OFFSET 8 #define L5_INTR_A4_OFFSET 8
.data .data
_l5_intr_stack: _l5_intr_stack:
.space L5_INTR_STACK_SIZE .space L5_INTR_STACK_SIZE*portNUM_PROCESSORS
#if !defined(CONFIG_FREERTOS_UNICORE) && defined(CONFIG_SPIRAM_SUPPORT) #if CONFIG_ESP32_ECO3_CACHE_LOCK_FIX
.global _l4_intr_livelock_counter .global _l4_intr_livelock_counter
.global _l4_intr_livelock_max .global _l4_intr_livelock_max
.align 16 .align 16
@ -49,6 +49,10 @@ _l4_intr_livelock_counter:
.word 0 .word 0
_l4_intr_livelock_max: _l4_intr_livelock_max:
.word 0 .word 0
_l4_intr_livelock_sync:
.word 0, 0
_l4_intr_livelock_app:
.word 0
#endif #endif
.section .iram1,"ax" .section .iram1,"ax"
@ -64,15 +68,12 @@ xt_highint5:
bnez a0, .handle_dport_access_int bnez a0, .handle_dport_access_int
#endif // CONFIG_FREERTOS_UNICORE #endif // CONFIG_FREERTOS_UNICORE
#if !defined(CONFIG_FREERTOS_UNICORE) && defined(CONFIG_SPIRAM_SUPPORT) #if CONFIG_ESP32_ECO3_CACHE_LOCK_FIX
/* See if we're here for the tg1 watchdog interrupt */ /* See if we're here for the tg1 watchdog interrupt */
rsr a0, INTERRUPT rsr a0, INTERRUPT
extui a0, a0, ETS_T1_WDT_INUM, 1 extui a0, a0, ETS_T1_WDT_INUM, 1
beqz a0, 1f beqz a0, 1f
getcoreid a0
bnez a0, 1f /* App cpu (Core 1) jump bypass */
/* Pro cpu (Core 0) can execute to here. */ /* Pro cpu (Core 0) can execute to here. */
wsr a5, depc /* use DEPC as temp storage */ wsr a5, depc /* use DEPC as temp storage */
movi a0, _l4_intr_livelock_counter movi a0, _l4_intr_livelock_counter
@ -161,19 +162,115 @@ xt_highint5:
rfi 5 rfi 5
#if !defined(CONFIG_FREERTOS_UNICORE) && defined(CONFIG_SPIRAM_SUPPORT) #if CONFIG_ESP32_ECO3_CACHE_LOCK_FIX
/*
--------------------------------------------------------------------------------
Macro intr_matrix_map - Attach an CPU interrupt to a hardware source.
Input : "addr" - Interrupt map configuration base address
Input : "src" - Interrupt source.
Input : "inum" - Interrupt number.
--------------------------------------------------------------------------------
*/
.macro intr_matrix_map addr src inum
movi a2, \src
slli a2, a2, 2
movi a3, \addr
add a3, a3, a2
movi a2, \inum
s32i a2, a3, 0
.endm
/*
--------------------------------------------------------------------------------
Macro wdt_clr_intr_status - Clear the WDT interrupt status.
Macro wdt_feed - Feed the WDT.
Input : "dev" - Beginning address of the peripheral registers
--------------------------------------------------------------------------------
*/
.macro wdt_clr_intr_status dev
movi a2, \dev
movi a3, TIMG_WDT_WKEY_VALUE
memw
s32i a3, a2, 100 /* disable write protect */
l32i a4, a2, 164
movi a3, 4
or a3, a4, a3
memw
s32i a3, a2, 164 /* clear 1st stage timeout interrupt */
movi a3, 0
s32i a3, a2, 100 /* enable write protect */
.endm
.macro wdt_feed dev
movi a2, \dev
movi a3, TIMG_WDT_WKEY_VALUE
memw
s32i a3, a2, 100 /* disable write protect */
movi a4, _l4_intr_livelock_max
l32i a4, a4, 0
addi a4, a4, 1
movi a3, (CONFIG_INT_WDT_TIMEOUT_MS<<1)
quou a3, a3, a4
memw
s32i a3, a2, 80 /* set timeout before interrupt */
movi a3, (CONFIG_INT_WDT_TIMEOUT_MS<<2)
memw
s32i a3, a2, 84 /* set timeout before system reset */
movi a3, 1
s32i a3, a2, 96 /* feed wdt */
memw
movi a3, 0
s32i a3, a2, 100 /* enable write protect */
memw
.endm
.align 4 .align 4
.handle_livelock_int: .handle_livelock_int:
getcoreid a5
/* Save A2, A3, A4 so we can use those registers */ /* Save A2, A3, A4 so we can use those registers */
movi a0, L4_INTR_STACK_SIZE
mull a5, a5, a0
movi a0, _l4_intr_stack movi a0, _l4_intr_stack
add a0, a0, a5
s32i a2, a0, L4_INTR_A2_OFFSET s32i a2, a0, L4_INTR_A2_OFFSET
s32i a3, a0, L4_INTR_A3_OFFSET s32i a3, a0, L4_INTR_A3_OFFSET
s32i a4, a0, L4_INTR_A4_OFFSET s32i a4, a0, L4_INTR_A4_OFFSET
rsil a0, CONFIG_ESP32_DPORT_DIS_INTERRUPT_LVL /* disable nested iterrupt */ /* Here, we can use a0, a2, a3, a4, a5 registers */
getcoreid a5
beqz a5, 1f
movi a2, _l4_intr_livelock_app
l32i a3, a2, 0
addi a3, a3, 1
s32i a3, a2, 0
/* Dual core synchronization, ensuring that both cores enter interrupts */
1: movi a4, 0x1
movi a2, _l4_intr_livelock_sync
addx4 a3, a5, a2
s32i a4, a3, 0
1: movi a2, _l4_intr_livelock_sync
movi a3, 1
addx4 a3, a3, a2
l32i a2, a2, 0
l32i a3, a3, 0
and a2, a2, a3
beqz a2, 1b
rsil a0, CONFIG_ESP32_DPORT_DIS_INTERRUPT_LVL /* disable nested interrupt */
beqz a5, 1f /* Pro cpu (Core 0) jump bypass */
movi a2, _l4_intr_livelock_app
l32i a2, a2, 0
bnei a2, 2, 1f
movi a2, _l4_intr_livelock_counter /* _l4_intr_livelock_counter++ */ movi a2, _l4_intr_livelock_counter /* _l4_intr_livelock_counter++ */
l32i a3, a2, 0 l32i a3, a2, 0
addi a3, a3, 1 addi a3, a3, 1
@ -192,8 +289,10 @@ xt_highint5:
When flash is DOUT/DIO read, N = 2. When flash is DOUT/DIO read, N = 2.
When flash is QOUT/QIO read, N = 4. When flash is QOUT/QIO read, N = 4.
*/ */
rsr.ccount a2 1: rsr.ccount a2
movi a4, g_ticks_per_us_pro movi a3, g_ticks_per_us_pro
movi a4, g_ticks_per_us_app
moveqz a4, a3, a5
l32i a4, a4, 0 l32i a4, a4, 0
#if defined(CONFIG_FLASHMODE_QIO) || defined(CONFIG_FLASHMODE_QOUT) #if defined(CONFIG_FLASHMODE_QIO) || defined(CONFIG_FLASHMODE_QOUT)
# if defined(CONFIG_ESPTOOLPY_FLASHFREQ_80M) && defined(CONFIG_SPIRAM_SPEED_80M) # if defined(CONFIG_ESPTOOLPY_FLASHFREQ_80M) && defined(CONFIG_SPIRAM_SPEED_80M)
@ -221,43 +320,71 @@ xt_highint5:
# endif # endif
#endif #endif
mull a3, a3, a4 mull a3, a3, a4
1: rsr.ccount a4 /* delay_us(N) */ 2: rsr.ccount a4 /* delay_us(N) */
sub a4, a4, a2 sub a4, a4, a2
bltu a4, a3, 1b bltu a4, a3, 2b
/* Feed watchdog and clear tg1 1st stage timeout interrupt */ beqz a5, 2f
movi a2, TIMERG1 movi a2, _l4_intr_livelock_app
movi a3, TIMG_WDT_WKEY_VALUE l32i a2, a2, 0
memw beqi a2, 2, 8f
s32i a3, a2, 100 /* disable tg1 write protect */
movi a3, 40 2: movi a2, _l4_intr_livelock_sync
memw movi a4, 1
s32i a3, a2, 80 /* set timeout before interrupt */ addx4 a3, a4, a2
movi a3, 4000 l32i a2, a2, 0
memw l32i a3, a3, 0
s32i a3, a2, 84 /* set timeout before system reset */ and a2, a2, a3
movi a3, 1 beqz a2, 4f
s32i a3, a2, 96 /* feed wdt */
memw beqz a5, 3f
/*
Pro cpu (Core 0) jump bypass, continue waiting, App cpu (Core 1)
can execute to here, unmap itself tg1 1st stage timeout interrupt
then restore registers and exit highint4.
*/
intr_matrix_map DPORT_APP_MAC_INTR_MAP_REG, ETS_TG1_WDT_LEVEL_INTR_SOURCE, 16
j 9f
3: j 1b
/* /*
The vector number of the interrupt watchdog is ETS_T1_WDT_INUM (24), which is a Here, App cpu (Core 1) has exited isr, Pro cpu (Core 0) help the
Level-Triggered interrupt and needs to be cleared at the peripheral. App cpu map tg1 1st stage timeout interrupt clear tg1 interrupt.
*/ */
l32i a4, a2, 164 4: intr_matrix_map DPORT_APP_MAC_INTR_MAP_REG, ETS_TG1_WDT_LEVEL_INTR_SOURCE, ETS_T1_WDT_INUM
movi a3, 4
or a3, a4, a3
memw
s32i a3, a2, 164 /* clear tg1 1st stage timeout interrupt */
movi a3, 0 1: movi a2, _l4_intr_livelock_sync
s32i a3, a2, 100 /* enable tg1 write protect */ movi a4, 1
memw addx4 a3, a4, a2
l32i a2, a2, 0
l32i a3, a3, 0
and a2, a2, a3
beqz a2, 1b /* Wait for App cpu to enter highint4 again */
wsr a0, PS /* restore iterrupt level */ wdt_clr_intr_status TIMERG1
j 9f
/* Feed watchdog */
8: wdt_feed TIMERG1
9: wsr a0, PS /* restore iterrupt level */
movi a0, 0
beqz a5, 1f
movi a2, _l4_intr_livelock_app
l32i a3, a2, 0
bnei a3, 2, 1f
s32i a0, a2, 0
1: movi a2, _l4_intr_livelock_sync
addx4 a2, a5, a2
s32i a0, a2, 0
/* Done. Restore registers and return. */ /* Done. Restore registers and return. */
movi a0, L4_INTR_STACK_SIZE
mull a5, a5, a0
movi a0, _l4_intr_stack movi a0, _l4_intr_stack
add a0, a0, a5
l32i a2, a0, L4_INTR_A2_OFFSET l32i a2, a0, L4_INTR_A2_OFFSET
l32i a3, a0, L4_INTR_A3_OFFSET l32i a3, a0, L4_INTR_A3_OFFSET
l32i a4, a0, L4_INTR_A4_OFFSET l32i a4, a0, L4_INTR_A4_OFFSET

View File

@ -39,7 +39,7 @@
// #define WDT_INT_NUM 24 // #define WDT_INT_NUM 24
#define WDT_INT_NUM ETS_T1_WDT_INUM #define WDT_INT_NUM ETS_T1_WDT_INUM
#if !defined(CONFIG_FREERTOS_UNICORE) && defined(CONFIG_SPIRAM_SUPPORT) #if CONFIG_ESP32_ECO3_CACHE_LOCK_FIX
/* /*
* This parameter is indicates the response time of tg1 watchdog to identify the * This parameter is indicates the response time of tg1 watchdog to identify the
* live lock, Too large values may affect BT and Wifi modules. * live lock, Too large values may affect BT and Wifi modules.
@ -61,7 +61,7 @@ static void IRAM_ATTR tick_hook(void) {
//Only feed wdt if app cpu also ticked. //Only feed wdt if app cpu also ticked.
if (int_wdt_app_cpu_ticked) { if (int_wdt_app_cpu_ticked) {
TIMERG1.wdt_wprotect=TIMG_WDT_WKEY_VALUE; TIMERG1.wdt_wprotect=TIMG_WDT_WKEY_VALUE;
#if !defined(CONFIG_FREERTOS_UNICORE) && defined(CONFIG_SPIRAM_SUPPORT) #if CONFIG_ESP32_ECO3_CACHE_LOCK_FIX
_l4_intr_livelock_counter = 0; _l4_intr_livelock_counter = 0;
TIMERG1.wdt_config2=CONFIG_INT_WDT_TIMEOUT_MS*2/(_l4_intr_livelock_max+1); //Set timeout before interrupt TIMERG1.wdt_config2=CONFIG_INT_WDT_TIMEOUT_MS*2/(_l4_intr_livelock_max+1); //Set timeout before interrupt
#else #else
@ -115,13 +115,9 @@ void esp_int_wdt_cpu_init()
* We found a live lock issue on ESP32 ECO3, This problem will cause the cache busy and then * We found a live lock issue on ESP32 ECO3, This problem will cause the cache busy and then
* the CPU to stop executing instructions. In order to solve this problem, we need to use * the CPU to stop executing instructions. In order to solve this problem, we need to use
* tg1 1st stage timeout interrupt to interrupt the cache busy state of the live lock. * tg1 1st stage timeout interrupt to interrupt the cache busy state of the live lock.
* Here we only bind this interrupt to the Pro cpu (Core 0), when the tg1 1st stage timeout
* interrupt caused by the live lock occurs, only the Pro cpu (Core 0) execution path switched
* to level 4 ISR to unlock the cache busy status and resume system.
*/ */
if (xPortGetCoreID() == PRO_CPU_NUM) { intr_matrix_set(xPortGetCoreID(), ETS_TG1_WDT_LEVEL_INTR_SOURCE, WDT_INT_NUM);
intr_matrix_set(xPortGetCoreID(), ETS_TG1_WDT_LEVEL_INTR_SOURCE, WDT_INT_NUM); #if CONFIG_ESP32_ECO3_CACHE_LOCK_FIX
#if !defined(CONFIG_FREERTOS_UNICORE) && defined(CONFIG_SPIRAM_SUPPORT)
_l4_intr_livelock_max = 0; _l4_intr_livelock_max = 0;
if (soc_has_cache_lock_bug()) { if (soc_has_cache_lock_bug()) {
assert(((1000/CONFIG_FREERTOS_HZ)<<1) <= TG1_WDT_LIVELOCK_TIMEOUT_MS); assert(((1000/CONFIG_FREERTOS_HZ)<<1) <= TG1_WDT_LIVELOCK_TIMEOUT_MS);
@ -129,7 +125,6 @@ void esp_int_wdt_cpu_init()
_l4_intr_livelock_max = CONFIG_INT_WDT_TIMEOUT_MS/TG1_WDT_LIVELOCK_TIMEOUT_MS - 1; _l4_intr_livelock_max = CONFIG_INT_WDT_TIMEOUT_MS/TG1_WDT_LIVELOCK_TIMEOUT_MS - 1;
} }
#endif #endif
}
//We do not register a handler for the interrupt because it is interrupt level 4 which //We do not register a handler for the interrupt because it is interrupt level 4 which
//is not servicable from C. Instead, xtensa_vectors.S has a call to the panic handler for //is not servicable from C. Instead, xtensa_vectors.S has a call to the panic handler for
//this interrupt. //this interrupt.

View File

@ -240,15 +240,6 @@ void panicHandler(XtExcFrame *frame)
} }
#if !CONFIG_FREERTOS_UNICORE #if !CONFIG_FREERTOS_UNICORE
/*
* When the real Interrupt watchdog occurs (_l4_intr_livelock_counter >= _l4_intr_livelock_max),
* do not clear the wdt interrupt, help the App cpu (Core 1) map tg1 1st stage timeout
* interrupt, trigger the App cpu (Core 1) to respond to the wdt interrupt.
*/
if (core_id == PRO_CPU_NUM) {
intr_matrix_set(APP_CPU_NUM, ETS_TG1_WDT_LEVEL_INTR_SOURCE, ETS_T1_WDT_INUM);
}
//Save frame for other core. //Save frame for other core.
if ((frame->exccause == PANIC_RSN_INTWDT_CPU0 && core_id == 1) || (frame->exccause == PANIC_RSN_INTWDT_CPU1 && core_id == 0)) { if ((frame->exccause == PANIC_RSN_INTWDT_CPU0 && core_id == 1) || (frame->exccause == PANIC_RSN_INTWDT_CPU1 && core_id == 0)) {
other_core_frame = frame; other_core_frame = frame;