From 87d4172ee5bbdebb7df017c723391864b213c2df Mon Sep 17 00:00:00 2001 From: Marius Vikhammer Date: Fri, 19 Apr 2024 14:03:29 +0800 Subject: [PATCH] feat(ulp): add lp core panic handler --- .../esp32c6/include/soc/Kconfig.soc_caps.in | 4 + components/soc/esp32c6/include/soc/soc_caps.h | 4 + components/ulp/Kconfig | 16 ++++ components/ulp/cmake/CMakeLists.txt | 4 +- .../ulp/cmake/toolchain-lp-core-riscv.cmake | 6 +- .../ulp/lp_core/lp_core/lp_core_interrupt.c | 11 +-- .../ulp/lp_core/lp_core/lp_core_panic.c | 80 +++++++++++++++++++ .../lp_core/port/esp32c6/vector_table.S | 22 +++++ .../port/esp32p4/{vector.S => vector_table.S} | 10 --- .../lp_core/{port/esp32c6 => }/vector.S | 62 +++++++------- .../lp_core/main/lp_core/test_main_gpio.c | 3 + .../ulp/test_apps/lp_core/sdkconfig.defaults | 3 +- docs/en/api-reference/system/ulp-lp-core.rst | 20 ++++- .../lp_uart/lp_uart_echo/sdkconfig.defaults | 2 +- 14 files changed, 198 insertions(+), 49 deletions(-) create mode 100644 components/ulp/lp_core/lp_core/lp_core_panic.c create mode 100644 components/ulp/lp_core/lp_core/port/esp32c6/vector_table.S rename components/ulp/lp_core/lp_core/port/esp32p4/{vector.S => vector_table.S} (84%) rename components/ulp/lp_core/lp_core/{port/esp32c6 => }/vector.S (76%) diff --git a/components/soc/esp32c6/include/soc/Kconfig.soc_caps.in b/components/soc/esp32c6/include/soc/Kconfig.soc_caps.in index 8b522a023e..f6a4d61f67 100644 --- a/components/soc/esp32c6/include/soc/Kconfig.soc_caps.in +++ b/components/soc/esp32c6/include/soc/Kconfig.soc_caps.in @@ -1466,3 +1466,7 @@ config SOC_PHY_COMBO_MODULE config SOC_CAPS_NO_RESET_BY_ANA_BOD bool default y + +config SOC_LP_CORE_SINGLE_INTERRUPT_VECTOR + bool + default y diff --git a/components/soc/esp32c6/include/soc/soc_caps.h b/components/soc/esp32c6/include/soc/soc_caps.h index d9fdc588ce..facb0cabec 100644 --- a/components/soc/esp32c6/include/soc/soc_caps.h +++ b/components/soc/esp32c6/include/soc/soc_caps.h @@ -583,3 +583,7 @@ /*------------------------------------- No Reset CAPS -------------------------------------*/ #define SOC_CAPS_NO_RESET_BY_ANA_BOD (1) + + +/*------------------------------------- ULP CAPS -------------------------------------*/ +#define SOC_LP_CORE_SINGLE_INTERRUPT_VECTOR (1) /*!< LP Core interrupts all map to a single entry in vector table */ diff --git a/components/ulp/Kconfig b/components/ulp/Kconfig index 1c7f7c2913..94167bd5df 100644 --- a/components/ulp/Kconfig +++ b/components/ulp/Kconfig @@ -92,4 +92,20 @@ menu "Ultra Low Power (ULP) Co-processor" Note: For LP ROM prints to work properly, make sure that the LP core boots from the LP ROM. + menu "ULP Debugging Options" + config ULP_PANIC_OUTPUT_ENABLE + depends on ULP_COPROC_TYPE_LP_CORE && SOC_ULP_LP_UART_SUPPORTED + bool + prompt "Enable panic handler which outputs over LP UART" + default "y" if IDF_TARGET_ESP32P4 + help + Set this option to enable panic handler functionality. If this option is + enabled then the LP Core will output a panic dump over LP UART, + similar to what the main core does. Output depends on LP UART already being + initialized and configured. + Disabling this option will reduce the LP core binary size by not + linking in panic handler functionality. + + endmenu + endmenu # Ultra Low Power (ULP) Co-processor diff --git a/components/ulp/cmake/CMakeLists.txt b/components/ulp/cmake/CMakeLists.txt index 9b35eb75ba..05bddaa68d 100644 --- a/components/ulp/cmake/CMakeLists.txt +++ b/components/ulp/cmake/CMakeLists.txt @@ -101,7 +101,8 @@ if(ULP_COCPU_IS_RISCV) elseif(ULP_COCPU_IS_LP_CORE) list(APPEND ULP_S_SOURCES "${IDF_PATH}/components/ulp/lp_core/lp_core/start.S" - "${IDF_PATH}/components/ulp/lp_core/lp_core/port/${IDF_TARGET}/vector.S" + "${IDF_PATH}/components/ulp/lp_core/lp_core/vector.S" + "${IDF_PATH}/components/ulp/lp_core/lp_core/port/${IDF_TARGET}/vector_table.S" "${IDF_PATH}/components/ulp/lp_core/shared/ulp_lp_core_memory_shared.c" "${IDF_PATH}/components/ulp/lp_core/shared/ulp_lp_core_lp_timer_shared.c" "${IDF_PATH}/components/ulp/lp_core/lp_core/lp_core_startup.c" @@ -111,6 +112,7 @@ elseif(ULP_COCPU_IS_LP_CORE) "${IDF_PATH}/components/hal/uart_hal.c" "${IDF_PATH}/components/ulp/lp_core/lp_core/lp_core_uart.c" "${IDF_PATH}/components/ulp/lp_core/lp_core/lp_core_print.c" + "${IDF_PATH}/components/ulp/lp_core/lp_core/lp_core_panic.c" "${IDF_PATH}/components/ulp/lp_core/lp_core/lp_core_interrupt.c" "${IDF_PATH}/components/ulp/lp_core/lp_core/lp_core_i2c.c") diff --git a/components/ulp/cmake/toolchain-lp-core-riscv.cmake b/components/ulp/cmake/toolchain-lp-core-riscv.cmake index 9b9ce09e8b..eb3a0623be 100644 --- a/components/ulp/cmake/toolchain-lp-core-riscv.cmake +++ b/components/ulp/cmake/toolchain-lp-core-riscv.cmake @@ -5,11 +5,11 @@ set(CMAKE_C_COMPILER "riscv32-esp-elf-gcc") set(CMAKE_CXX_COMPILER "riscv32-esp-elf-g++") set(CMAKE_ASM_COMPILER "riscv32-esp-elf-gcc") -set(CMAKE_C_FLAGS "-Os -march=rv32imac_zicsr_zifencei -mdiv -fdata-sections -ffunction-sections" +set(CMAKE_C_FLAGS "-Os -ggdb -march=rv32imac_zicsr_zifencei -mdiv -fdata-sections -ffunction-sections" CACHE STRING "C Compiler Base Flags") -set(CMAKE_CXX_FLAGS "-Os -march=rv32imac_zicsr_zifencei -mdiv -fdata-sections -ffunction-sections" +set(CMAKE_CXX_FLAGS "-Os -ggdb -march=rv32imac_zicsr_zifencei -mdiv -fdata-sections -ffunction-sections" CACHE STRING "C++ Compiler Base Flags") -set(CMAKE_ASM_FLAGS "-march=rv32imac_zicsr_zifencei -x assembler-with-cpp" +set(CMAKE_ASM_FLAGS "-Os -ggdb -march=rv32imac_zicsr_zifencei -x assembler-with-cpp" CACHE STRING "Assembler Base Flags") set(CMAKE_EXE_LINKER_FLAGS "-march=rv32imac_zicsr_zifencei --specs=nano.specs --specs=nosys.specs" CACHE STRING "Linker Base Flags") diff --git a/components/ulp/lp_core/lp_core/lp_core_interrupt.c b/components/ulp/lp_core/lp_core/lp_core_interrupt.c index 114973b9cb..a24b7953e1 100644 --- a/components/ulp/lp_core/lp_core/lp_core_interrupt.c +++ b/components/ulp/lp_core/lp_core/lp_core_interrupt.c @@ -9,6 +9,7 @@ #include "sdkconfig.h" #include "hal/lp_core_ll.h" #include "riscv/rv_utils.h" +#include "riscv/rvruntime-frames.h" #if CONFIG_IDF_TARGET_ESP32C6 /* Enable interrupt 30, which all external interrupts are routed to*/ @@ -36,6 +37,11 @@ void ulp_lp_core_intr_disable(void) RV_CLEAR_CSR(mstatus, MSTATUS_MIE); } +void __attribute__((weak)) ulp_lp_core_panic_handler(RvExcFrame *frame, int exccause) +{ + abort(); +} + static void ulp_lp_core_default_intr_handler(void) { abort(); @@ -60,11 +66,6 @@ void __attribute__((weak, alias("ulp_lp_core_default_intr_handler"))) ulp_lp_cor void __attribute__((weak, alias("ulp_lp_core_default_intr_handler"))) ulp_lp_core_lp_rtc_intr_handler(void); void __attribute__((weak, alias("ulp_lp_core_default_intr_handler"))) ulp_lp_core_sw_intr_handler(void); -void ulp_lp_core_panic_handler(void) -{ - abort(); -} - #if CONFIG_IDF_TARGET_ESP32C6 static void* s_intr_handlers[] = { diff --git a/components/ulp/lp_core/lp_core/lp_core_panic.c b/components/ulp/lp_core/lp_core/lp_core_panic.c new file mode 100644 index 0000000000..70fac05483 --- /dev/null +++ b/components/ulp/lp_core/lp_core/lp_core_panic.c @@ -0,0 +1,80 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include "sdkconfig.h" + +#include +#include +#include "ulp_lp_core_print.h" +#include "riscv/rvruntime-frames.h" + +#if CONFIG_ULP_PANIC_OUTPUT_ENABLE + +static void dump_stack(RvExcFrame *frame, int exccause) +{ + uint32_t i = 0; + uint32_t sp = frame->sp; + lp_core_printf("\n\nStack memory:\n"); + const int per_line = 8; + for (i = 0; i < 1024; i += per_line * sizeof(uint32_t)) { + uint32_t *spp = (uint32_t *)(sp + i); + lp_core_printf("%08x: ", sp + i); + for (int y = 0; y < per_line; y++) { + lp_core_printf("0x%08x%c", spp[y], y == per_line - 1 ? '\n' : ' '); + } + } + lp_core_printf("\n"); +} + +static const char *desc[] = { + "MEPC ", "RA ", "SP ", "GP ", "TP ", "T0 ", "T1 ", "T2 ", + "S0/FP ", "S1 ", "A0 ", "A1 ", "A2 ", "A3 ", "A4 ", "A5 ", + "A6 ", "A7 ", "S2 ", "S3 ", "S4 ", "S5 ", "S6 ", "S7 ", + "S8 ", "S9 ", "S10 ", "S11 ", "T3 ", "T4 ", "T5 ", "T6 ", + "MSTATUS ", "MTVEC ", "MCAUSE ", "MTVAL ", "MHARTID " +}; + +static const char *reason[] = { + NULL, + NULL, + "Illegal instruction", + "Breakpoint", + "Load address misaligned", + "Load access fault", + "Store address misaligned", + "Store access fault", +}; + +void ulp_lp_core_panic_handler(RvExcFrame *frame, int exccause) +{ +#define DIM(arr) (sizeof(arr)/sizeof(*arr)) + + const char *exccause_str = "Unhandled interrupt/Unknown cause"; + + if (exccause < DIM(reason) && reason[exccause] != NULL) { + exccause_str = reason[exccause]; + } + + lp_core_printf("Guru Meditation Error: LP Core panic'ed (%s)\n", exccause_str); + lp_core_printf("Core 0 register dump:\n"); + + uint32_t* frame_ints = (uint32_t*) frame; + for (int x = 0; x < DIM(desc); x++) { + if (desc[x][0] != 0) { + const int not_last = (x + 1) % 4; + lp_core_printf("%-8s: 0x%08x %c", desc[x], frame_ints[x], not_last ? ' ' : '\n'); + } + } + + dump_stack(frame, exccause); + + /* idf-monitor uses this string to mark the end of a panic dump */ + lp_core_printf("ELF file SHA256: No SHA256 Embedded\n"); + + while (1) { + } +} + +#endif //#if CONFIG_ULP_PANIC_OUTPUT_ENABLE diff --git a/components/ulp/lp_core/lp_core/port/esp32c6/vector_table.S b/components/ulp/lp_core/lp_core/port/esp32c6/vector_table.S new file mode 100644 index 0000000000..a1309e3cf8 --- /dev/null +++ b/components/ulp/lp_core/lp_core/port/esp32c6/vector_table.S @@ -0,0 +1,22 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + + .section .init.vector,"ax" + + .global _vector_table + .type _vector_table, @function +_vector_table: + .option push + .option norvc + + .rept 30 + j _panic_handler + .endr + j _interrupt_handler // All interrupts are routed to mtvec + 4*30, i.e. the 31st entry + j _panic_handler + + .option pop + .size _vector_table, .-_vector_table diff --git a/components/ulp/lp_core/lp_core/port/esp32p4/vector.S b/components/ulp/lp_core/lp_core/port/esp32p4/vector_table.S similarity index 84% rename from components/ulp/lp_core/lp_core/port/esp32p4/vector.S rename to components/ulp/lp_core/lp_core/port/esp32p4/vector_table.S index 3853c6df50..c5e7ca9fee 100644 --- a/components/ulp/lp_core/lp_core/port/esp32p4/vector.S +++ b/components/ulp/lp_core/lp_core/port/esp32p4/vector_table.S @@ -47,13 +47,3 @@ _vector_table: .option pop .size _vector_table, .-_vector_table - - -/* _panic_handler: handle all exception */ - .section .text.handlers,"ax" - .global _panic_handler - .type _panic_handler, @function -_panic_handler: - call ulp_lp_core_panic_handler -_end: - j _end /* loop forever */ diff --git a/components/ulp/lp_core/lp_core/port/esp32c6/vector.S b/components/ulp/lp_core/lp_core/vector.S similarity index 76% rename from components/ulp/lp_core/lp_core/port/esp32c6/vector.S rename to components/ulp/lp_core/lp_core/vector.S index 32331e71b4..b62cbc9ec3 100644 --- a/components/ulp/lp_core/lp_core/port/esp32c6/vector.S +++ b/components/ulp/lp_core/lp_core/vector.S @@ -5,6 +5,7 @@ */ #include "riscv/rvruntime-frames.h" +#include "soc/soc_caps.h" .equ SAVE_REGS, 32 .equ CONTEXT_SIZE, (SAVE_REGS * 4) @@ -92,47 +93,54 @@ .endm - .section .init.vector,"ax" - - .global _vector_table - .type _vector_table, @function -_vector_table: - .option push - .option norvc - - .rept 30 - j _panic_handler - .endr - j _interrupt_handler // All interrupts are routed to mtvec + 4*30, i.e. the 31st entry - j _panic_handler - - .option pop - .size _vector_table, .-_vector_table - - /* _panic_handler: handle all exception */ .section .text.handlers,"ax" .global _panic_handler .type _panic_handler, @function _panic_handler: - call ulp_lp_core_panic_handler -_end: - j _end /* loop forever */ + save_general_regs RV_STK_FRMSZ + save_mepc + addi t0, sp, RV_STK_FRMSZ /* restore sp with the value when trap happened */ + + /* Save CSRs */ + sw t0, RV_STK_SP(sp) + csrr t0, mstatus + sw t0, RV_STK_MSTATUS(sp) + csrr t0, mcause + sw t0, RV_STK_MCAUSE(sp) + csrr t0, mtvec + sw t0, RV_STK_MTVEC(sp) + csrr t0, mhartid + sw t0, RV_STK_MHARTID(sp) + csrr t0, mtval + sw t0, RV_STK_MTVAL(sp) + + csrr a1, mcause /* exception cause */ + + mv a0, sp /* RvExcFrame *regs */ + call ulp_lp_core_panic_handler +_end: + j _end /* loop forever */ + + +#if SOC_LP_CORE_SINGLE_INTERRUPT_VECTOR /* interrupt_handler: handle all interrupt */ - .section .text.handlers,"ax" - .global _interrupt_handler - .type _interrupt_handler, @function + .section .text.handlers,"ax" + .global _interrupt_handler + .type _interrupt_handler, @function _interrupt_handler: /* save registers & mepc to stack */ save_general_regs save_mepc - call ulp_lp_core_intr_handler + call ulp_lp_core_intr_handler - /* restore registers & mepc from stack */ + /* restore registers & mepc from stack */ restore_mepc restore_general_regs /* exit, this will also re-enable the interrupts */ - mret + mret + +#endif // SOC_LP_CORE_SINGLE_INTERRUPT_VECTOR diff --git a/components/ulp/test_apps/lp_core/main/lp_core/test_main_gpio.c b/components/ulp/test_apps/lp_core/main/lp_core/test_main_gpio.c index 4d5b09f7b6..53e71fc19c 100644 --- a/components/ulp/test_apps/lp_core/main/lp_core/test_main_gpio.c +++ b/components/ulp/test_apps/lp_core/main/lp_core/test_main_gpio.c @@ -20,12 +20,15 @@ int main(void) ulp_lp_core_gpio_output_enable(LP_IO_NUM_0); ulp_lp_core_gpio_set_level(LP_IO_NUM_0, 0); + ulp_lp_core_delay_us(100); gpio_test_succeeded = (ulp_lp_core_gpio_get_level(LP_IO_NUM_0) == 0); ulp_lp_core_gpio_set_level(LP_IO_NUM_0, 1); + ulp_lp_core_delay_us(100); gpio_test_succeeded &= (ulp_lp_core_gpio_get_level(LP_IO_NUM_0) == 1); ulp_lp_core_gpio_set_level(LP_IO_NUM_0, 0); + ulp_lp_core_delay_us(100); gpio_test_succeeded &= (ulp_lp_core_gpio_get_level(LP_IO_NUM_0) == 0); gpio_test_finished = 1; diff --git a/components/ulp/test_apps/lp_core/sdkconfig.defaults b/components/ulp/test_apps/lp_core/sdkconfig.defaults index b1dc7fcf8c..c7626f4733 100644 --- a/components/ulp/test_apps/lp_core/sdkconfig.defaults +++ b/components/ulp/test_apps/lp_core/sdkconfig.defaults @@ -2,4 +2,5 @@ CONFIG_ESP_TASK_WDT_INIT=n CONFIG_ULP_COPROC_ENABLED=y CONFIG_ULP_COPROC_TYPE_LP_CORE=y -CONFIG_ULP_COPROC_RESERVE_MEM=4096 +CONFIG_ULP_COPROC_RESERVE_MEM=12000 +CONFIG_ULP_PANIC_OUTPUT_ENABLE=y diff --git a/docs/en/api-reference/system/ulp-lp-core.rst b/docs/en/api-reference/system/ulp-lp-core.rst index 4d80c88c60..b2cc75909e 100644 --- a/docs/en/api-reference/system/ulp-lp-core.rst +++ b/docs/en/api-reference/system/ulp-lp-core.rst @@ -178,7 +178,7 @@ To enhance the capabilities of the ULP LP-Core coprocessor, it has access to per Since these functions are already present in LP-ROM no matter what, using these in your program allows you to reduce the RAM footprint of your ULP application. -ULP LP-Core interrupts +ULP LP-Core Interrupts ---------------------- The LP-Core coprocessor can be configured to handle interrupts from various sources. Examples of such interrupts could be LP IO low/high or LP timer interrupts. To register a handler for an interrupt simply override any of the weak handlers provided by IDF. A complete list of handlers can be found in :component_file:`ulp_lp_core_interrupts.h `. For details on which interrupts are available on a specific target, please consult the Low Power CPU chapter in the Technical Reference Manual.` @@ -196,6 +196,22 @@ For example, to override the handler for the LP IO interrupt, you can define the In addition to configuring the interrupt related registers for the interrupt source you want to handle, you also need to enable the interrupts globally in the LP-Core interrupt controller. This can be done using the :cpp:func:`ulp_lp_core_intr_enable` function. +Debugging ULP LP-Core Applications +---------------------------------- + +When programming the LP-Core, it can sometimes be challenging to figure out why the program is not behaving as expected. Here are some strategies to help you debug your LP-Core program: + + * Use the LP-UART to print: the LP-Core has access to the LP-UART peripheral, which can be used for printing information independently of the main CPU sleep state. See :example:`system/ulp/lp_core/lp_uart/lp_uart_print` for an example of how to use this driver. + + * Share program state through shared variables: as described in :ref:`ulp-lp-core-access-variables`, both the main CPU and the ULP core can easily access global variables in RTC memory. Writing state information to such a variable from the ULP and reading it from the main CPU can help you discern what is happening on the ULP core. The downside of this approach is that it requires the main CPU to be awake, which will not always be the case. Keeping the main CPU awake might even, in some cases, mask problems, as some issues may only occur when certain power domains are powered down. + + * Panic handler: the LP-Core has a panic handler that can dump the state of the LP-Core registers to the LP-UART when an exception is detected. To enable the panic handler, set the :ref:`CONFIG_ULP_PANIC_OUTPUT_ENABLE` option to ``y``. This option can be kept disabled to reduce LP-RAM usage by the LP-Core application. To recover a backtrace from the panic dump it is possible to use esp-idf-monitor_., e.g.: + +.. code-block:: bash + + python -m esp_idf_monitor --toolchain-prefix riscv32-esp-elf- --target {IDF_TARGET_NAME} --decode-panic backtrace PATH_TO_ULP_ELF_FILE + + Application Examples -------------------- @@ -224,3 +240,5 @@ LP Core API Reference .. include-build-file:: inc/ulp_lp_core_uart.inc .. include-build-file:: inc/ulp_lp_core_print.inc .. include-build-file:: inc/ulp_lp_core_interrupts.inc + +.. _esp-idf-monitor: https://github.com/espressif/esp-idf-monitor diff --git a/examples/system/ulp/lp_core/lp_uart/lp_uart_echo/sdkconfig.defaults b/examples/system/ulp/lp_core/lp_uart/lp_uart_echo/sdkconfig.defaults index 53ae7d35bf..f018081b4d 100644 --- a/examples/system/ulp/lp_core/lp_uart/lp_uart_echo/sdkconfig.defaults +++ b/examples/system/ulp/lp_core/lp_uart/lp_uart_echo/sdkconfig.defaults @@ -1,4 +1,4 @@ # Enable LP Core CONFIG_ULP_COPROC_ENABLED=y CONFIG_ULP_COPROC_TYPE_LP_CORE=y -CONFIG_ULP_COPROC_RESERVE_MEM=4096 +CONFIG_ULP_COPROC_RESERVE_MEM=8192