mirror of
https://github.com/espressif/esp-idf.git
synced 2024-10-05 20:47:46 -04:00
a77867d302
fix too many live lock
314 lines
9.6 KiB
ArmAsm
314 lines
9.6 KiB
ArmAsm
// Copyright 2020 Espressif Systems (Shanghai) PTE LTD
|
|
// All rights reserved.
|
|
|
|
#include <xtensa/coreasm.h>
|
|
#include <xtensa/corebits.h>
|
|
#include <xtensa/config/system.h>
|
|
#include "freertos/xtensa_context.h"
|
|
#include "sdkconfig.h"
|
|
#include "soc/soc.h"
|
|
|
|
/* Interrupt stack size, for C code.
|
|
* TODO: reduce and make configurable.
|
|
*/
|
|
#define L4_INTR_STACK_SIZE 4096
|
|
|
|
/* Save area for the CPU state:
|
|
* - 64 words for the general purpose registers
|
|
* - 7 words for some of the special registers:
|
|
* - WINDOWBASE, WINDOWSTART — only WINDOWSTART is truly needed
|
|
* - SAR, LBEG, LEND, LCOUNT — since the C code might use these
|
|
* - EPC1 — since the C code might cause window overflow exceptions
|
|
* This is not laid out a standard exception frame structure
|
|
* for simplicity of the save/restore code.
|
|
*/
|
|
#define REG_FILE_SIZE (64 * 4)
|
|
#define SPECREG_OFFSET REG_FILE_SIZE
|
|
#define SPECREG_SIZE (7 * 4)
|
|
#define REG_SAVE_AREA_SIZE (SPECREG_OFFSET + SPECREG_SIZE)
|
|
|
|
#if CONFIG_ESP32_ECO3_CACHE_LOCK_FIX
|
|
#include "soc/timer_group_reg.h"
|
|
|
|
.extern _l5_intr_livelock_counter
|
|
.extern _l5_intr_livelock_max
|
|
|
|
#define TIMG1_REG_OFFSET(reg) ((reg) - REG_TIMG_BASE(1))
|
|
#define TIMG1_WDTWPROTECT_OFFSET TIMG1_REG_OFFSET(TIMG_WDTWPROTECT_REG(1))
|
|
#define TIMG1_INT_CLR_OFFSET TIMG1_REG_OFFSET(TIMG_INT_CLR_TIMERS_REG(1))
|
|
#define TIMG1_WDTCONFIG0_OFFSET TIMG1_REG_OFFSET(TIMG_WDTCONFIG0_REG(1))
|
|
#define TIMG1_WDT_STG0_HOLD_OFFSET TIMG1_REG_OFFSET(TIMG_WDTCONFIG2_REG(1))
|
|
#define TIMG1_WDT_STG1_HOLD_OFFSET TIMG1_REG_OFFSET(TIMG_WDTCONFIG3_REG(1))
|
|
#define TIMG1_WDT_FEED_OFFSET TIMG1_REG_OFFSET(TIMG_WDTFEED_REG(1))
|
|
|
|
.macro livelock_wdt_reconf dev ticks
|
|
movi a2, \dev
|
|
movi a0, TIMG_WDT_WKEY_VALUE
|
|
s32i a0, a2, TIMG1_WDTWPROTECT_OFFSET /* disable write protect */
|
|
memw
|
|
|
|
l32i a0, a2, TIMG1_WDTCONFIG0_OFFSET
|
|
memw
|
|
movi a2, 0x7fffffff
|
|
and a0, a2, a0
|
|
movi a2, \dev
|
|
s32i a0, a2, TIMG1_WDTCONFIG0_OFFSET /* wdt disable */
|
|
memw
|
|
|
|
movi a2, \dev
|
|
movi a0, \ticks
|
|
s32i a0, a2, TIMG1_WDT_STG0_HOLD_OFFSET /* set timeout before interrupt */
|
|
memw
|
|
movi a0, (CONFIG_INT_WDT_TIMEOUT_MS<<2)
|
|
s32i a0, a2, TIMG1_WDT_STG1_HOLD_OFFSET /* set timeout before system reset */
|
|
memw
|
|
|
|
l32i a0, a2, TIMG1_WDTCONFIG0_OFFSET
|
|
memw
|
|
movi a2, 0x80000000
|
|
or a0, a2, a0
|
|
movi a2, \dev
|
|
s32i a0, a2, TIMG1_WDTCONFIG0_OFFSET /* wdt enable */
|
|
memw
|
|
|
|
movi a0, 0
|
|
s32i a0, a2, TIMG1_WDTWPROTECT_OFFSET /* enable write protect */
|
|
memw
|
|
.endm
|
|
|
|
.macro livelock_wdt_feed dev
|
|
movi a2, \dev
|
|
movi a3, TIMG_WDT_WKEY_VALUE
|
|
s32i a3, a2, TIMG1_WDTWPROTECT_OFFSET /* disable write protect */
|
|
memw
|
|
movi a1, _l5_intr_livelock_max
|
|
l32i a1, a1, 0
|
|
memw
|
|
addi a1, a1, 1
|
|
movi a3, (CONFIG_INT_WDT_TIMEOUT_MS<<1)
|
|
quou a3, a3, a1
|
|
s32i a3, a2, TIMG1_WDT_STG0_HOLD_OFFSET /* set timeout before interrupt */
|
|
memw
|
|
movi a3, (CONFIG_INT_WDT_TIMEOUT_MS<<2)
|
|
s32i a3, a2, TIMG1_WDT_STG1_HOLD_OFFSET /* set timeout before system reset */
|
|
memw
|
|
movi a3, 1
|
|
s32i a3, a2, TIMG1_WDT_FEED_OFFSET /* feed wdt */
|
|
memw
|
|
movi a3, 0
|
|
s32i a3, a2, TIMG1_WDTWPROTECT_OFFSET /* enable write protect */
|
|
memw
|
|
.endm
|
|
#endif
|
|
|
|
.data
|
|
_l4_intr_stack:
|
|
.space L4_INTR_STACK_SIZE
|
|
|
|
_l4_save_ctx:
|
|
.space REG_SAVE_AREA_SIZE
|
|
|
|
.section .iram1,"ax"
|
|
.global xt_highint4
|
|
.type xt_highint4,@function
|
|
.align 4
|
|
xt_highint4:
|
|
#if CONFIG_ESP32_ECO3_CACHE_LOCK_FIX
|
|
wsr a2, EXCVADDR /* use EXCVADDR as temp storage */
|
|
livelock_wdt_reconf TIMERG1 1
|
|
rsr a2, EXCVADDR /* restore a2 */
|
|
#endif
|
|
movi a0, _l4_save_ctx
|
|
/* save 4 lower registers */
|
|
s32i a1, a0, 4
|
|
s32i a2, a0, 8
|
|
s32i a3, a0, 12
|
|
rsr a2, EXCSAVE_4 /* holds the value of a0 */
|
|
s32i a2, a0, 0
|
|
|
|
/* Save special registers */
|
|
addi a0, a0, SPECREG_OFFSET
|
|
rsr a2, WINDOWBASE
|
|
s32i a2, a0, 0
|
|
rsr a2, WINDOWSTART
|
|
s32i a2, a0, 4
|
|
rsr a2, SAR
|
|
s32i a2, a0, 8
|
|
rsr a2, LBEG
|
|
s32i a2, a0, 12
|
|
rsr a2, LEND
|
|
s32i a2, a0, 16
|
|
rsr a2, LCOUNT
|
|
s32i a2, a0, 20
|
|
rsr a2, EPC1
|
|
s32i a2, a0, 24
|
|
|
|
#if CONFIG_ESP32_ECO3_CACHE_LOCK_FIX
|
|
livelock_wdt_feed TIMERG1
|
|
|
|
movi a3, 0
|
|
movi a2, _l5_intr_livelock_counter
|
|
s32i a3, a2, 0
|
|
memw
|
|
#endif
|
|
|
|
/* disable exception mode, window overflow */
|
|
movi a0, PS_INTLEVEL(5) | PS_EXCM /*TOCHECK*/
|
|
wsr a0, PS
|
|
rsync
|
|
|
|
/* Save the remaining physical registers.
|
|
* 4 registers are already saved, which leaves 60 registers to save.
|
|
* (FIXME: consider the case when the CPU is configured with physical 32 registers)
|
|
* These 60 registers are saved in 5 iterations, 12 registers at a time.
|
|
*/
|
|
movi a1, 5
|
|
movi a3, _l4_save_ctx + 4 * 4
|
|
|
|
/* This is repeated 5 times, each time the window is shifted by 12 registers.
|
|
* We come here with a1 = downcounter, a3 = save pointer, a2 and a0 unused.
|
|
*/
|
|
1:
|
|
s32i a4, a3, 0
|
|
s32i a5, a3, 4
|
|
s32i a6, a3, 8
|
|
s32i a7, a3, 12
|
|
s32i a8, a3, 16
|
|
s32i a9, a3, 20
|
|
s32i a10, a3, 24
|
|
s32i a11, a3, 28
|
|
s32i a12, a3, 32
|
|
s32i a13, a3, 36
|
|
s32i a14, a3, 40
|
|
s32i a15, a3, 44
|
|
|
|
/* We are about to rotate the window, so that a12-a15 will become the new a0-a3.
|
|
* Copy a0-a3 to a12-15 to still have access to these values.
|
|
* At the same time we can decrement the counter and adjust the save area pointer
|
|
*/
|
|
|
|
/* a0 is constant (_l4_save_ctx), no need to copy */
|
|
addi a13, a1, -1 /* copy and decrement the downcounter */
|
|
/* a2 is scratch so no need to copy */
|
|
addi a15, a3, 48 /* copy and adjust the save area pointer */
|
|
beqz a13, 2f /* have saved all registers ? */
|
|
rotw 3 /* rotate the window and go back */
|
|
j 1b
|
|
|
|
/* the loop is complete */
|
|
2:
|
|
rotw 4 /* this brings us back to the original window */
|
|
/* a0 still points to _l4_save_ctx */
|
|
|
|
/* Can clear WINDOWSTART now, all registers are saved */
|
|
rsr a2, WINDOWBASE
|
|
/* WINDOWSTART = (1 << WINDOWBASE) */
|
|
movi a3, 1
|
|
ssl a2
|
|
sll a3, a3
|
|
wsr a3, WINDOWSTART
|
|
|
|
_highint4_stack_switch:
|
|
movi a0, 0
|
|
movi sp, _l4_intr_stack + L4_INTR_STACK_SIZE - 16
|
|
s32e a0, sp, -12 /* For GDB: set null SP */
|
|
s32e a0, sp, -16 /* For GDB: set null PC */
|
|
movi a0, _highint4_stack_switch /* For GDB: cosmetics, for the frame where stack switch happened */
|
|
|
|
/* Set up PS for C, disable all interrupts except NMI and debug, and clear EXCM. */
|
|
movi a6, PS_INTLEVEL(4) | PS_UM | PS_WOE
|
|
wsr a6, PS
|
|
rsync
|
|
|
|
/* Call C handler */
|
|
mov a6, sp
|
|
call4 hli_c_handler
|
|
|
|
l32e sp, sp, -12 /* switch back to the original stack */
|
|
|
|
/* Done with C handler; re-enable exception mode, disabling window overflow */
|
|
movi a2, PS_INTLEVEL(5) | PS_EXCM /* TOCHECK */
|
|
wsr a2, PS
|
|
rsync
|
|
|
|
/* Restore the special registers.
|
|
* WINDOWSTART will be restored near the end.
|
|
*/
|
|
movi a0, _l4_save_ctx + SPECREG_OFFSET
|
|
l32i a2, a0, 8
|
|
wsr a2, SAR
|
|
l32i a2, a0, 12
|
|
wsr a2, LBEG
|
|
l32i a2, a0, 16
|
|
wsr a2, LEND
|
|
l32i a2, a0, 20
|
|
wsr a2, LCOUNT
|
|
l32i a2, a0, 24
|
|
wsr a2, EPC1
|
|
|
|
/* Restoring the physical registers.
|
|
* This is the reverse to the saving process above.
|
|
*/
|
|
|
|
/* Rotate back to the final window, then start loading 12 registers at a time,
|
|
* in 5 iterations.
|
|
* Again, a1 is the downcounter and a3 is the save area pointer.
|
|
* After each rotation, a1 and a3 are copied from a13 and a15.
|
|
* To simplify the loop, we put the initial values into a13 and a15.
|
|
*/
|
|
rotw -4
|
|
movi a15, _l4_save_ctx + 64 * 4 /* point to the end of the save area */
|
|
movi a13, 5
|
|
|
|
1:
|
|
/* Copy a1 and a3 from their previous location,
|
|
* at the same time decrementing and adjusting the save area pointer.
|
|
*/
|
|
addi a1, a13, -1
|
|
addi a3, a15, -48
|
|
|
|
/* Load 12 registers */
|
|
l32i a4, a3, 0
|
|
l32i a5, a3, 4
|
|
l32i a6, a3, 8
|
|
l32i a7, a3, 12
|
|
l32i a8, a3, 16
|
|
l32i a9, a3, 20
|
|
l32i a10, a3, 24
|
|
l32i a11, a3, 28 /* ensure PS and EPC written */
|
|
l32i a12, a3, 32
|
|
l32i a13, a3, 36
|
|
l32i a14, a3, 40
|
|
l32i a15, a3, 44
|
|
|
|
/* Done with the loop? */
|
|
beqz a1, 2f
|
|
/* If no, rotate the window and repeat */
|
|
rotw -3
|
|
j 1b
|
|
|
|
2:
|
|
/* Done with the loop. Only 4 registers (a0-a3 in the original window) remain
|
|
* to be restored. Also need to restore WINDOWSTART, since all the general
|
|
* registers are now in place.
|
|
*/
|
|
movi a0, _l4_save_ctx
|
|
|
|
l32i a2, a0, SPECREG_OFFSET + 4
|
|
wsr a2, WINDOWSTART
|
|
|
|
l32i a1, a0, 4
|
|
l32i a2, a0, 8
|
|
l32i a3, a0, 12
|
|
rsr a0, EXCSAVE_4 /* holds the value of a0 before the interrupt handler */
|
|
|
|
/* Return from the interrupt, restoring PS from EPS_4 */
|
|
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_hli_vectors_bt
|
|
ld_include_hli_vectors_bt:
|