Merge branch 'feature/lp_core_intr_panic_v5_3' into 'release/v5.3'

feat(ulp): support interrupts and panic for C6/P4 LP core (v5.3)

See merge request espressif/esp-idf!31189
This commit is contained in:
Marius Vikhammer
2024-06-11 09:41:51 +08:00
51 changed files with 1272 additions and 28 deletions
@@ -12,6 +12,7 @@
#pragma once
#include <stdbool.h>
#include <stdint.h>
#include "soc/lpperi_struct.h"
#include "soc/pmu_struct.h"
#include "soc/lp_aon_struct.h"
@@ -125,6 +126,16 @@ static inline void lp_core_ll_request_sleep(void)
PMU.lp_ext.pwr1.sleep_req = 1;
}
/**
* @brief Get which interrupts have triggered on the LP core
*
* @return uint8_t bit mask of triggered LP interrupt sources
*/
static inline uint8_t lp_core_ll_get_triggered_interrupt_srcs(void)
{
return LPPERI.interrupt_source.lp_interrupt_source;
}
#ifdef __cplusplus
}
#endif
@@ -541,6 +541,16 @@ FORCE_INLINE_ATTR void pmu_ll_lp_clear_intsts_mask(pmu_dev_t *hw, uint32_t mask)
hw->lp_ext.int_clr.val = mask;
}
FORCE_INLINE_ATTR void pmu_ll_lp_clear_sw_intr_status(pmu_dev_t *hw)
{
hw->lp_ext.int_clr.sw_trigger = 1;
}
FORCE_INLINE_ATTR void pmu_ll_lp_enable_sw_intr(pmu_dev_t *hw, bool enable)
{
hw->lp_ext.int_ena.sw_trigger = enable;
}
FORCE_INLINE_ATTR void pmu_ll_lp_set_min_sleep_cycle(pmu_dev_t *hw, uint32_t slow_clk_cycle)
{
hw->wakeup.cntl3.lp_min_slp_val = slow_clk_cycle;
@@ -40,6 +40,15 @@ typedef enum {
RTCIO_LL_WAKEUP_HIGH_LEVEL = 0x5, /*!< GPIO interrupt type : input high level trigger */
} rtcio_ll_wake_type_t;
typedef enum {
RTCIO_INTR_DISABLE = 0, /*!< Disable GPIO interrupt */
RTCIO_INTR_POSEDGE = 1, /*!< GPIO interrupt type : rising edge */
RTCIO_INTR_NEGEDGE = 2, /*!< GPIO interrupt type : falling edge */
RTCIO_INTR_ANYEDGE = 3, /*!< GPIO interrupt type : both rising and falling edge */
RTCIO_INTR_LOW_LEVEL = 4, /*!< GPIO interrupt type : input low level trigger */
RTCIO_INTR_HIGH_LEVEL = 5, /*!< GPIO interrupt type : input high level trigger */
} rtcio_ll_intr_type_t;
typedef enum {
RTCIO_LL_OUTPUT_NORMAL = 0, /*!< RTCIO output mode is normal. */
RTCIO_LL_OUTPUT_OD = 0x1, /*!< RTCIO output mode is open-drain. */
@@ -316,6 +325,24 @@ static inline void rtcio_ll_wakeup_disable(int rtcio_num)
LP_IO.pin[rtcio_num].int_type = RTCIO_LL_WAKEUP_DISABLE;
}
/**
* Enable interrupt function and set interrupt type
*
* @param rtcio_num The index of rtcio. 0 ~ MAX(rtcio).
* @param type Interrupt type on high level or low level.
*/
static inline void rtcio_ll_intr_enable(int rtcio_num, rtcio_ll_intr_type_t type)
{
LP_IO.pin[rtcio_num].int_type = type;
/* Work around for HW issue,
need to also enable this clk, so that LP_IO.status.status_interrupt can get updated,
and trigger the interrupt on the LP Core
*/
LP_IO.date.clk_en = 1;
}
/**
* Enable rtc io output in deep sleep.
*
@@ -30,6 +30,16 @@ FORCE_INLINE_ATTR void pmu_ll_lp_clear_intsts_mask(pmu_dev_t *hw, uint32_t mask)
hw->lp_ext.int_clr.val = mask;
}
FORCE_INLINE_ATTR void pmu_ll_lp_clear_sw_intr_status(pmu_dev_t *hw)
{
hw->lp_ext.int_clr.hp_sw_trigger = 1;
}
FORCE_INLINE_ATTR void pmu_ll_lp_enable_sw_intr(pmu_dev_t *hw, bool enable)
{
hw->lp_ext.int_ena.hp_sw_trigger = enable;
}
FORCE_INLINE_ATTR void pmu_ll_hp_set_dig_power(pmu_dev_t *hw, pmu_hp_mode_t mode, uint32_t flag)
{
hw->hp_sys[mode].dig_power.val = flag;
+21 -1
View File
@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -39,6 +39,15 @@ typedef enum {
RTCIO_LL_WAKEUP_HIGH_LEVEL = 0x5, /*!< GPIO interrupt type : input high level trigger */
} rtcio_ll_wake_type_t;
typedef enum {
RTCIO_INTR_DISABLE = 0, /*!< Disable GPIO interrupt */
RTCIO_INTR_POSEDGE = 1, /*!< GPIO interrupt type : rising edge */
RTCIO_INTR_NEGEDGE = 2, /*!< GPIO interrupt type : falling edge */
RTCIO_INTR_ANYEDGE = 3, /*!< GPIO interrupt type : both rising and falling edge */
RTCIO_INTR_LOW_LEVEL = 4, /*!< GPIO interrupt type : input low level trigger */
RTCIO_INTR_HIGH_LEVEL = 5, /*!< GPIO interrupt type : input high level trigger */
} rtcio_ll_intr_type_t;
typedef enum {
RTCIO_LL_OUTPUT_NORMAL = 0, /*!< RTCIO output mode is normal. */
RTCIO_LL_OUTPUT_OD = 0x1, /*!< RTCIO output mode is open-drain. */
@@ -341,6 +350,17 @@ static inline void rtcio_ll_wakeup_disable(int rtcio_num)
LP_GPIO.pin[rtcio_num].int_type = RTCIO_LL_WAKEUP_DISABLE;
}
/**
* Enable interrupt function and set interrupt type
*
* @param rtcio_num The index of rtcio. 0 ~ MAX(rtcio).
* @param type Interrupt type on high level or low level.
*/
static inline void rtcio_ll_intr_enable(int rtcio_num, rtcio_ll_intr_type_t type)
{
LP_GPIO.pin[rtcio_num].int_type = type;
}
/**
* @brief Enable RTCIO output in deep sleep.
*
@@ -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
@@ -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 */
+16
View File
@@ -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
+3
View File
@@ -102,6 +102,7 @@ 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/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,8 @@ 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")
target_link_options(${ULP_APP_NAME} PRIVATE "-nostartfiles")
@@ -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")
@@ -63,6 +63,14 @@ esp_err_t ulp_lp_core_load_binary(const uint8_t* program_binary, size_t program_
*/
void ulp_lp_core_stop(void);
/**
* @brief Trigger a SW interrupt to the LP CPU from the PMU
*
* @note This is the same SW trigger that is used to wake up the LP CPU
*
*/
void ulp_lp_core_sw_intr_trigger(void);
#ifdef __cplusplus
}
#endif
+10 -2
View File
@@ -36,6 +36,8 @@ const static char* TAG = "ulp-lp-core";
#define WAKEUP_SOURCE_MAX_NUMBER 5
#define RESET_HANDLER_ADDR (intptr_t)(&_rtc_ulp_memory_start + 0x80 / 4) // Placed after the 0x80 byte long vector table
/* Maps the flags defined in ulp_lp_core.h e.g. ULP_LP_CORE_WAKEUP_SOURCE_HP_CPU to their actual HW values */
static uint32_t wakeup_src_sw_to_hw_flag_lookup[WAKEUP_SOURCE_MAX_NUMBER] = {
LP_CORE_LL_WAKEUP_SOURCE_HP_CPU,
@@ -68,7 +70,7 @@ esp_err_t ulp_lp_core_run(ulp_lp_core_cfg_t* cfg)
/* If we have a LP ROM we boot from it, before jumping to the app code */
intptr_t boot_addr;
if (cfg->skip_lp_rom_boot) {
boot_addr = (intptr_t)(&_rtc_ulp_memory_start);
boot_addr = RESET_HANDLER_ADDR;
} else {
boot_addr = SOC_LP_ROM_LOW;
/* Disable UART init in ROM, it defaults to XTAL clk src
@@ -80,7 +82,8 @@ esp_err_t ulp_lp_core_run(ulp_lp_core_cfg_t* cfg)
}
lp_core_ll_set_boot_address(boot_addr);
lp_core_ll_set_app_boot_address((intptr_t)(&_rtc_ulp_memory_start));
lp_core_ll_set_app_boot_address(RESET_HANDLER_ADDR);
#endif //ESP_ROM_HAS_LP_ROM
LP_CORE_RCC_ATOMIC() {
@@ -161,3 +164,8 @@ void ulp_lp_core_stop(void)
lp_core_ll_set_wakeup_source(0);
lp_core_ll_request_sleep();
}
void ulp_lp_core_sw_intr_trigger(void)
{
lp_core_ll_hp_wake_lp();
}
@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -10,6 +10,7 @@
extern "C" {
#endif
#include "soc/soc_caps.h"
#include "hal/gpio_types.h"
#include "hal/rtc_io_ll.h"
@@ -25,8 +26,27 @@ typedef enum {
LP_IO_NUM_5 = 5, /*!< GPIO5, input and output */
LP_IO_NUM_6 = 6, /*!< GPIO6, input and output */
LP_IO_NUM_7 = 7, /*!< GPIO7, input and output */
#if SOC_RTCIO_PIN_COUNT > 8
LP_IO_NUM_8 = 8, /*!< GPIO8, input and output */
LP_IO_NUM_9 = 9, /*!< GPIO9, input and output */
LP_IO_NUM_10 = 10, /*!< GPIO10, input and output */
LP_IO_NUM_11 = 11, /*!< GPIO11, input and output */
LP_IO_NUM_12 = 12, /*!< GPIO12, input and output */
LP_IO_NUM_13 = 13, /*!< GPIO13, input and output */
LP_IO_NUM_14 = 14, /*!< GPIO14, input and output */
LP_IO_NUM_15 = 15, /*!< GPIO15, input and output */
#endif
} lp_io_num_t;
typedef enum {
LP_IO_INTR_DISABLE = 0, /*!< Disable GPIO interrupt */
LP_IO_INTR_POSEDGE = 1, /*!< GPIO interrupt type : rising edge */
LP_IO_INTR_NEGEDGE = 2, /*!< GPIO interrupt type : falling edge */
LP_IO_INTR_ANYEDGE = 3, /*!< GPIO interrupt type : both rising and falling edge */
LP_IO_INTR_LOW_LEVEL = 4, /*!< GPIO interrupt type : input low level trigger */
LP_IO_INTR_HIGH_LEVEL = 5, /*!< GPIO interrupt type : input high level trigger */
} lp_io_intr_type_t;
/**
* @brief Initialize a rtcio pin
*
@@ -153,6 +173,26 @@ static inline void ulp_lp_core_gpio_pulldown_disable(lp_io_num_t lp_io_num)
rtcio_ll_pulldown_disable(lp_io_num);
}
/**
* @brief Enable interrupt for lp io pin
*
* @param lp_io_num The lp io pin to enable interrupt for
* @param intr_type The interrupt type to enable
*/
static inline void ulp_lp_core_gpio_intr_enable(lp_io_num_t lp_io_num, lp_io_intr_type_t intr_type)
{
rtcio_ll_intr_enable(lp_io_num, intr_type);
}
/**
* @brief Clear interrupt status for all lp io
*
*/
static inline void ulp_lp_core_gpio_clear_intr_status(void)
{
rtcio_ll_clear_interrupt_status();
}
#ifdef __cplusplus
}
#endif
@@ -0,0 +1,59 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include "sdkconfig.h"
#ifdef __cplusplus
extern "C" {
#endif
#if CONFIG_IDF_TARGET_ESP32C6
#define LP_CORE_ISR_ATTR // On C6 registers are saved by us before calling the ISR
#else
#define LP_CORE_ISR_ATTR __attribute__((interrupt))
#endif
/**
* Available interrupt handlers for the low power core are as follows:
*
* ulp_lp_core_lp_io_intr_handler(void);
* ulp_lp_core_lp_i2c_intr_handler(void);
* ulp_lp_core_lp_uart_intr_handler(void);
* ulp_lp_core_lp_timer_intr_handler(void);
* ulp_lp_core_lp_pmu_intr_handler(void);
* ulp_lp_core_lp_spi_intr_handler(void);
* ulp_lp_core_trng_intr_handler(void);
* ulp_lp_core_lp_adc_intr_handler(void);
* ulp_lp_core_lp_touch_intr_handler(void);
* ulp_lp_core_tsens_intr_handler(void);
* ulp_lp_core_efuse_intr_handler(void);
* ulp_lp_core_lp_sysreg_intr_handler(void);
* ulp_lp_core_lp_ana_peri_intr_handler(void);
* ulp_lp_core_mailbox_intr_handler(void);
* ulp_lp_core_lp_wdt_intr_handler(void);
* ulp_lp_core_lp_rtc_intr_handler(void);
* ulp_lp_core_sw_intr_handler(void);
*
* Not all handlers are available on all chips. Please refer to the Technical Reference Manual for your chip for more information.
*/
/**
* @brief Enables interrupts globally for the low power core
*
*/
void ulp_lp_core_intr_enable(void);
/**
* @brief Disables interrupts globally for the low power core
*
*/
void ulp_lp_core_intr_disable(void);
#ifdef __cplusplus
}
#endif
@@ -3,6 +3,8 @@
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include "sdkconfig.h"
/**
@@ -17,7 +19,7 @@
*/
#if CONFIG_ULP_ROM_PRINT_ENABLE
extern int ets_printf(const char* format, ...);
int (*lp_core_printf)(const char* format, ...) = ets_printf;
#define lp_core_printf ets_printf
#else
//TODO: Change return type from void to int in IDF 6.0
void lp_core_printf(const char* format, ...);
@@ -33,5 +35,5 @@ void lp_core_printf(const char* format, ...);
* powered down during sleep.
*/
extern void ets_install_uart_printf(void);
void (*lp_core_install_uart_printf)(void) = ets_install_uart_printf;
#define lp_core_install_uart_print ets_install_uart_printf
#endif /* CONFIG_ULP_ROM_PRINT_ENABLE */
@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -12,6 +12,7 @@ extern "C" {
#include <stdint.h>
#include <stdlib.h>
#include <stdbool.h>
/**
* @brief Traverse all possible wake-up sources and update the wake-up cause so that
@@ -72,6 +73,30 @@ __attribute__((__noreturn__)) void ulp_lp_core_halt(void);
*/
__attribute__((__noreturn__)) void ulp_lp_core_stop_lp_core(void);
/**
* @brief Enable the SW triggered interrupt from the PMU
*
* @note This is the same SW trigger interrupt that is used to wake up the LP CPU
*
* @param enable true to enable, false to disable
*
*/
void ulp_lp_core_sw_intr_enable(bool enable);
/**
* @brief Clear the interrupt status for the SW triggered interrupt from the PMU
*
*/
void ulp_lp_core_sw_intr_clear(void);
/**
* @brief Puts the CPU into a wait state until an interrupt is triggered
*
* @note The CPU will draw less power when in this state compared to actively running
*
*/
void ulp_lp_core_wait_for_intr(void);
#ifdef __cplusplus
}
#endif
@@ -0,0 +1,93 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdint.h>
#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*/
#define MIE_ALL_INTS_MASK (1 << 30)
#else
/* Enable all external interrupts routed to CPU, expect HP_INTR,
as this would trigger an LP core interrupt for every single interrupt
that triggers on HP Core.
*/
#define MIE_ALL_INTS_MASK 0x3FFF0888
#endif
void ulp_lp_core_intr_enable(void)
{
/* Enable interrupt globally */
RV_SET_CSR(mstatus, MSTATUS_MIE);
RV_SET_CSR(mie, MIE_ALL_INTS_MASK);
}
void ulp_lp_core_intr_disable(void)
{
RV_CLEAR_CSR(mie, MIE_ALL_INTS_MASK);
/* Disable interrupts globally */
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();
}
/* Default ISR handlers, intended to be overwritten by users */
void __attribute__((weak, alias("ulp_lp_core_default_intr_handler"))) ulp_lp_core_lp_io_intr_handler(void);
void __attribute__((weak, alias("ulp_lp_core_default_intr_handler"))) ulp_lp_core_lp_i2c_intr_handler(void);
void __attribute__((weak, alias("ulp_lp_core_default_intr_handler"))) ulp_lp_core_lp_uart_intr_handler(void);
void __attribute__((weak, alias("ulp_lp_core_default_intr_handler"))) ulp_lp_core_lp_timer_intr_handler(void);
void __attribute__((weak, alias("ulp_lp_core_default_intr_handler"))) ulp_lp_core_lp_pmu_intr_handler(void);
void __attribute__((weak, alias("ulp_lp_core_default_intr_handler"))) ulp_lp_core_lp_spi_intr_handler(void);
void __attribute__((weak, alias("ulp_lp_core_default_intr_handler"))) ulp_lp_core_trng_intr_handler(void);
void __attribute__((weak, alias("ulp_lp_core_default_intr_handler"))) ulp_lp_core_lp_adc_intr_handler(void);
void __attribute__((weak, alias("ulp_lp_core_default_intr_handler"))) ulp_lp_core_lp_touch_intr_handler(void);
void __attribute__((weak, alias("ulp_lp_core_default_intr_handler"))) ulp_lp_core_tsens_intr_handler(void);
void __attribute__((weak, alias("ulp_lp_core_default_intr_handler"))) ulp_lp_core_efuse_intr_handler(void);
void __attribute__((weak, alias("ulp_lp_core_default_intr_handler"))) ulp_lp_core_lp_sysreg_intr_handler(void);
void __attribute__((weak, alias("ulp_lp_core_default_intr_handler"))) ulp_lp_core_lp_ana_peri_intr_handler(void);
void __attribute__((weak, alias("ulp_lp_core_default_intr_handler"))) ulp_lp_core_mailbox_intr_handler(void);
void __attribute__((weak, alias("ulp_lp_core_default_intr_handler"))) ulp_lp_core_lp_wdt_intr_handler(void);
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);
#if CONFIG_IDF_TARGET_ESP32C6
static void* s_intr_handlers[] = {
ulp_lp_core_lp_io_intr_handler,
ulp_lp_core_lp_i2c_intr_handler,
ulp_lp_core_lp_uart_intr_handler,
ulp_lp_core_lp_timer_intr_handler,
0, // Reserved / Unused
ulp_lp_core_lp_pmu_intr_handler,
};
void __attribute__((weak)) ulp_lp_core_intr_handler(void)
{
uint8_t intr_source = lp_core_ll_get_triggered_interrupt_srcs();
for (int i = 0; i < sizeof(s_intr_handlers) / 4; i++) {
if (intr_source & (1 << i)) {
void (*handler)(void) = s_intr_handlers[i];
if (handler) {
handler();
}
}
}
}
#endif
@@ -0,0 +1,80 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "sdkconfig.h"
#include <stdint.h>
#include <stddef.h>
#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
@@ -4,6 +4,7 @@
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdarg.h>
#include "sdkconfig.h"
#include "ulp_lp_core_uart.h"
#if !CONFIG_ULP_ROM_PRINT_ENABLE
@@ -135,3 +135,18 @@ void __attribute__((noreturn)) abort(void)
while (1);
}
void ulp_lp_core_sw_intr_enable(bool enable)
{
pmu_ll_lp_enable_sw_intr(&PMU, enable);
}
void ulp_lp_core_sw_intr_clear(void)
{
pmu_ll_lp_clear_sw_intr_status(&PMU);
}
void ulp_lp_core_wait_for_intr(void)
{
asm volatile("wfi");
}
@@ -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
@@ -0,0 +1,49 @@
/*
* SPDX-FileCopyrightText: 2023-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
j _panic_handler
j _panic_handler
j _panic_handler
j ulp_lp_core_sw_intr_handler
j _panic_handler
j _panic_handler
j _panic_handler
j ulp_lp_core_lp_uart_intr_handler
j _panic_handler
j _panic_handler
j _panic_handler
j ulp_lp_core_lp_spi_intr_handler
j _panic_handler
j _panic_handler
j _panic_handler
j _panic_handler
j ulp_lp_core_trng_intr_handler
j ulp_lp_core_lp_i2c_intr_handler
j ulp_lp_core_lp_io_intr_handler
j ulp_lp_core_lp_adc_intr_handler
j ulp_lp_core_lp_touch_intr_handler
j ulp_lp_core_tsens_intr_handler
j ulp_lp_core_efuse_intr_handler
j ulp_lp_core_lp_sysreg_intr_handler
j ulp_lp_core_lp_ana_peri_intr_handler
j ulp_lp_core_lp_pmu_intr_handler
j ulp_lp_core_mailbox_intr_handler
j ulp_lp_core_lp_timer_intr_handler
j ulp_lp_core_lp_wdt_intr_handler
j ulp_lp_core_lp_rtc_intr_handler
j _panic_handler
j _panic_handler
.option pop
.size _vector_table, .-_vector_table
+5
View File
@@ -9,6 +9,11 @@
/* The reset vector, jumps to startup code */
reset_vector:
/* _vector_table: Only 256-byte aligned addresses are allowed */
la t0, _vector_table
csrw mtvec, t0
j __start
__start:
+138 -16
View File
@@ -1,24 +1,146 @@
/*
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
.section .init.vector,"ax"
/* This is the vector table. It is currently empty, but will be populated
* with exception and interrupt handlers when this is supported
*/
#include "riscv/rvruntime-frames.h"
#include "soc/soc_caps.h"
.align 0x4, 0xff
.global _vector_table
.type _vector_table, @function
_vector_table:
.option push
.option norvc
.equ SAVE_REGS, 32
.equ CONTEXT_SIZE, (SAVE_REGS * 4)
/* Macro which first allocates space on the stack to save general
* purpose registers, and then save them. GP register is excluded.
* The default size allocated on the stack is CONTEXT_SIZE, but it
* can be overridden. */
.macro save_general_regs cxt_size=CONTEXT_SIZE
addi sp, sp, -\cxt_size
sw ra, RV_STK_RA(sp)
sw tp, RV_STK_TP(sp)
sw t0, RV_STK_T0(sp)
sw t1, RV_STK_T1(sp)
sw t2, RV_STK_T2(sp)
sw s0, RV_STK_S0(sp)
sw s1, RV_STK_S1(sp)
sw a0, RV_STK_A0(sp)
sw a1, RV_STK_A1(sp)
sw a2, RV_STK_A2(sp)
sw a3, RV_STK_A3(sp)
sw a4, RV_STK_A4(sp)
sw a5, RV_STK_A5(sp)
sw a6, RV_STK_A6(sp)
sw a7, RV_STK_A7(sp)
sw s2, RV_STK_S2(sp)
sw s3, RV_STK_S3(sp)
sw s4, RV_STK_S4(sp)
sw s5, RV_STK_S5(sp)
sw s6, RV_STK_S6(sp)
sw s7, RV_STK_S7(sp)
sw s8, RV_STK_S8(sp)
sw s9, RV_STK_S9(sp)
sw s10, RV_STK_S10(sp)
sw s11, RV_STK_S11(sp)
sw t3, RV_STK_T3(sp)
sw t4, RV_STK_T4(sp)
sw t5, RV_STK_T5(sp)
sw t6, RV_STK_T6(sp)
.endm
.rept 32
nop
.endr
.macro save_mepc
csrr t0, mepc
sw t0, RV_STK_MEPC(sp)
.endm
.option pop
.size _vector_table, .-_vector_table
/* Restore the general purpose registers (excluding gp) from the context on
* the stack. The context is then deallocated. The default size is CONTEXT_SIZE
* but it can be overridden. */
.macro restore_general_regs cxt_size=CONTEXT_SIZE
lw ra, RV_STK_RA(sp)
lw tp, RV_STK_TP(sp)
lw t0, RV_STK_T0(sp)
lw t1, RV_STK_T1(sp)
lw t2, RV_STK_T2(sp)
lw s0, RV_STK_S0(sp)
lw s1, RV_STK_S1(sp)
lw a0, RV_STK_A0(sp)
lw a1, RV_STK_A1(sp)
lw a2, RV_STK_A2(sp)
lw a3, RV_STK_A3(sp)
lw a4, RV_STK_A4(sp)
lw a5, RV_STK_A5(sp)
lw a6, RV_STK_A6(sp)
lw a7, RV_STK_A7(sp)
lw s2, RV_STK_S2(sp)
lw s3, RV_STK_S3(sp)
lw s4, RV_STK_S4(sp)
lw s5, RV_STK_S5(sp)
lw s6, RV_STK_S6(sp)
lw s7, RV_STK_S7(sp)
lw s8, RV_STK_S8(sp)
lw s9, RV_STK_S9(sp)
lw s10, RV_STK_S10(sp)
lw s11, RV_STK_S11(sp)
lw t3, RV_STK_T3(sp)
lw t4, RV_STK_T4(sp)
lw t5, RV_STK_T5(sp)
lw t6, RV_STK_T6(sp)
addi sp,sp, \cxt_size
.endm
.macro restore_mepc
lw t0, RV_STK_MEPC(sp)
csrw mepc, t0
.endm
/* _panic_handler: handle all exception */
.section .text.handlers,"ax"
.global _panic_handler
.type _panic_handler, @function
_panic_handler:
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
_interrupt_handler:
/* save registers & mepc to stack */
save_general_regs
save_mepc
call ulp_lp_core_intr_handler
/* restore registers & mepc from stack */
restore_mepc
restore_general_regs
/* exit, this will also re-enable the interrupts */
mret
#endif // SOC_LP_CORE_SINGLE_INTERRUPT_VECTOR
@@ -26,6 +26,7 @@ set(lp_core_exp_dep_srcs ${app_sources})
ulp_embed_binary(lp_core_test_app "${lp_core_sources}" "${lp_core_exp_dep_srcs}")
ulp_embed_binary(lp_core_test_app_counter "${lp_core_sources_counter}" "${lp_core_exp_dep_srcs}")
ulp_embed_binary(lp_core_test_app_isr "lp_core/test_main_isr.c" "${lp_core_exp_dep_srcs}")
if(CONFIG_SOC_LP_TIMER_SUPPORTED)
ulp_embed_binary(lp_core_test_app_set_timer_wakeup "${lp_core_sources_set_timer_wakeup}" "${lp_core_exp_dep_srcs}")
@@ -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;
@@ -0,0 +1,42 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdint.h>
#include "ulp_lp_core_utils.h"
#include "ulp_lp_core_interrupts.h"
#include "ulp_lp_core_gpio.h"
#include "hal/pmu_ll.h"
volatile uint32_t io_isr_counter = 0;
volatile uint32_t pmu_isr_counter = 0;
volatile bool isr_test_started;
void LP_CORE_ISR_ATTR ulp_lp_core_lp_io_intr_handler(void)
{
ulp_lp_core_gpio_clear_intr_status();
io_isr_counter++;
}
void LP_CORE_ISR_ATTR ulp_lp_core_lp_pmu_intr_handler(void)
{
ulp_lp_core_sw_intr_clear();
pmu_isr_counter++;
}
int main(void)
{
ulp_lp_core_sw_intr_enable(true);
ulp_lp_core_intr_enable();
isr_test_started = true;
while (1) {
// Busy wait for the interrupts to occur
}
return 0;
}
@@ -11,6 +11,7 @@
#include "esp_rom_caps.h"
#include "lp_core_test_app.h"
#include "lp_core_test_app_counter.h"
#include "lp_core_test_app_isr.h"
#if SOC_LP_TIMER_SUPPORTED
#include "lp_core_test_app_set_timer_wakeup.h"
@@ -26,6 +27,10 @@
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "hal/lp_core_ll.h"
#include "hal/rtc_io_ll.h"
#include "driver/rtc_io.h"
extern const uint8_t lp_core_main_bin_start[] asm("_binary_lp_core_test_app_bin_start");
extern const uint8_t lp_core_main_bin_end[] asm("_binary_lp_core_test_app_bin_end");
@@ -38,6 +43,9 @@ extern const uint8_t lp_core_main_set_timer_wakeup_bin_end[] asm("_binary_lp_c
extern const uint8_t lp_core_main_gpio_bin_start[] asm("_binary_lp_core_test_app_gpio_bin_start");
extern const uint8_t lp_core_main_gpio_bin_end[] asm("_binary_lp_core_test_app_gpio_bin_end");
extern const uint8_t lp_core_main_isr_bin_start[] asm("_binary_lp_core_test_app_isr_bin_start");
extern const uint8_t lp_core_main_isr_bin_end[] asm("_binary_lp_core_test_app_isr_bin_end");
static void load_and_start_lp_core_firmware(ulp_lp_core_cfg_t* cfg, const uint8_t* firmware_start, const uint8_t* firmware_end)
{
TEST_ASSERT(ulp_lp_core_load_binary(firmware_start,
@@ -329,3 +337,49 @@ TEST_CASE("LP core gpio tests", "[ulp]")
}
#endif //SOC_LP_TIMER_SUPPORTED
#define ISR_TEST_ITERATIONS 100
#define IO_TEST_PIN 0
#include "lp_core_uart.h"
TEST_CASE("LP core ISR tests", "[ulp]")
{
lp_core_uart_cfg_t ucfg = LP_CORE_UART_DEFAULT_CONFIG();
ESP_ERROR_CHECK(lp_core_uart_init(&ucfg));
/* Load ULP firmware and start the coprocessor */
ulp_lp_core_cfg_t cfg = {
.wakeup_source = ULP_LP_CORE_WAKEUP_SOURCE_HP_CPU,
};
load_and_start_lp_core_firmware(&cfg, lp_core_main_isr_bin_start, lp_core_main_isr_bin_end);
while (!ulp_isr_test_started) {
}
for (int i = 0; i < ISR_TEST_ITERATIONS; i++) {
lp_core_ll_hp_wake_lp();
vTaskDelay(pdMS_TO_TICKS(10));
}
printf("ULP PMU ISR triggered %"PRIu32" times\n", ulp_pmu_isr_counter);
TEST_ASSERT_EQUAL(ISR_TEST_ITERATIONS, ulp_pmu_isr_counter);
/* Test LP IO interrupt */
rtc_gpio_init(IO_TEST_PIN);
rtc_gpio_set_direction(IO_TEST_PIN, RTC_GPIO_MODE_INPUT_ONLY);
TEST_ASSERT_EQUAL(0, ulp_io_isr_counter);
for (int i = 0; i < ISR_TEST_ITERATIONS; i++) {
#if CONFIG_IDF_TARGET_ESP32C6
LP_IO.status_w1ts.val = 0x00000001; // Set GPIO 0 intr status to high
#else
LP_GPIO.status_w1ts.val = 0x00000001; // Set GPIO 0 intr status to high
#endif
vTaskDelay(pdMS_TO_TICKS(10));
}
printf("ULP LP IO ISR triggered %"PRIu32" times\n", ulp_io_isr_counter);
TEST_ASSERT_EQUAL(ISR_TEST_ITERATIONS, ulp_io_isr_counter);
}
@@ -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