From 7f9b5deae1fad1b2e69a09ad5d0629c22c2ec419 Mon Sep 17 00:00:00 2001 From: Marius Vikhammer Date: Fri, 19 Apr 2024 14:03:29 +0800 Subject: [PATCH] feat(ulp): support interrupts for C6/P4 LP core Closes https://github.com/espressif/esp-idf/issues/13059 --- .../hal/esp32c6/include/hal/lp_core_ll.h | 11 ++ components/hal/esp32c6/include/hal/pmu_ll.h | 10 ++ .../hal/esp32c6/include/hal/rtc_io_ll.h | 21 +++ components/hal/esp32p4/include/hal/pmu_ll.h | 10 ++ .../hal/esp32p4/include/hal/rtc_io_ll.h | 22 ++- components/ulp/cmake/CMakeLists.txt | 3 +- components/ulp/lp_core/include/ulp_lp_core.h | 8 + components/ulp/lp_core/lp_core.c | 12 +- .../lp_core/include/ulp_lp_core_gpio.h | 42 +++++- .../lp_core/include/ulp_lp_core_interrupts.h | 59 ++++++++ .../lp_core/include/ulp_lp_core_print.h | 6 +- .../lp_core/include/ulp_lp_core_utils.h | 19 ++- .../ulp/lp_core/lp_core/lp_core_interrupt.c | 92 ++++++++++++ .../ulp/lp_core/lp_core/lp_core_print.c | 1 + .../ulp/lp_core/lp_core/lp_core_utils.c | 10 ++ .../ulp/lp_core/lp_core/port/esp32c6/vector.S | 138 ++++++++++++++++++ .../ulp/lp_core/lp_core/port/esp32p4/vector.S | 59 ++++++++ components/ulp/lp_core/lp_core/start.S | 5 + components/ulp/lp_core/lp_core/vector.S | 24 --- .../ulp/test_apps/lp_core/main/CMakeLists.txt | 1 + .../lp_core/main/lp_core/test_main_isr.c | 42 ++++++ .../ulp/test_apps/lp_core/main/test_lp_core.c | 54 +++++++ docs/component_info_ignore_file.txt | 1 + docs/doxygen/Doxyfile_esp32c6 | 1 + docs/doxygen/Doxyfile_esp32p4 | 1 + docs/en/api-reference/system/ulp-lp-core.rst | 20 +++ examples/system/.build-test-rules.yml | 6 + .../ulp/lp_core/interrupt/CMakeLists.txt | 8 + .../system/ulp/lp_core/interrupt/README.md | 19 +++ .../ulp/lp_core/interrupt/main/CMakeLists.txt | 25 ++++ .../ulp/lp_core/interrupt/main/lp_core/main.c | 30 ++++ .../interrupt/main/lp_interrupts_main.c | 53 +++++++ .../lp_core/interrupt/pytest_lp_core_intr.py | 11 ++ .../ulp/lp_core/interrupt/sdkconfig.defaults | 4 + 34 files changed, 796 insertions(+), 32 deletions(-) create mode 100644 components/ulp/lp_core/lp_core/include/ulp_lp_core_interrupts.h create mode 100644 components/ulp/lp_core/lp_core/lp_core_interrupt.c create mode 100644 components/ulp/lp_core/lp_core/port/esp32c6/vector.S create mode 100644 components/ulp/lp_core/lp_core/port/esp32p4/vector.S delete mode 100644 components/ulp/lp_core/lp_core/vector.S create mode 100644 components/ulp/test_apps/lp_core/main/lp_core/test_main_isr.c create mode 100644 examples/system/ulp/lp_core/interrupt/CMakeLists.txt create mode 100644 examples/system/ulp/lp_core/interrupt/README.md create mode 100644 examples/system/ulp/lp_core/interrupt/main/CMakeLists.txt create mode 100644 examples/system/ulp/lp_core/interrupt/main/lp_core/main.c create mode 100644 examples/system/ulp/lp_core/interrupt/main/lp_interrupts_main.c create mode 100644 examples/system/ulp/lp_core/interrupt/pytest_lp_core_intr.py create mode 100644 examples/system/ulp/lp_core/interrupt/sdkconfig.defaults diff --git a/components/hal/esp32c6/include/hal/lp_core_ll.h b/components/hal/esp32c6/include/hal/lp_core_ll.h index 7c6c5667ef..7ac68a9632 100644 --- a/components/hal/esp32c6/include/hal/lp_core_ll.h +++ b/components/hal/esp32c6/include/hal/lp_core_ll.h @@ -12,6 +12,7 @@ #pragma once #include +#include #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 diff --git a/components/hal/esp32c6/include/hal/pmu_ll.h b/components/hal/esp32c6/include/hal/pmu_ll.h index 2470f73247..2d47479166 100644 --- a/components/hal/esp32c6/include/hal/pmu_ll.h +++ b/components/hal/esp32c6/include/hal/pmu_ll.h @@ -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; diff --git a/components/hal/esp32c6/include/hal/rtc_io_ll.h b/components/hal/esp32c6/include/hal/rtc_io_ll.h index 550d6accf0..987c2b94b7 100644 --- a/components/hal/esp32c6/include/hal/rtc_io_ll.h +++ b/components/hal/esp32c6/include/hal/rtc_io_ll.h @@ -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,18 @@ 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; +} + /** * Enable rtc io output in deep sleep. * diff --git a/components/hal/esp32p4/include/hal/pmu_ll.h b/components/hal/esp32p4/include/hal/pmu_ll.h index 057428e132..9534196f0c 100644 --- a/components/hal/esp32p4/include/hal/pmu_ll.h +++ b/components/hal/esp32p4/include/hal/pmu_ll.h @@ -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; diff --git a/components/hal/esp32p4/include/hal/rtc_io_ll.h b/components/hal/esp32p4/include/hal/rtc_io_ll.h index 763e8f2eee..a23bb2ead0 100644 --- a/components/hal/esp32p4/include/hal/rtc_io_ll.h +++ b/components/hal/esp32p4/include/hal/rtc_io_ll.h @@ -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. * diff --git a/components/ulp/cmake/CMakeLists.txt b/components/ulp/cmake/CMakeLists.txt index dceac3f78d..9b35eb75ba 100644 --- a/components/ulp/cmake/CMakeLists.txt +++ b/components/ulp/cmake/CMakeLists.txt @@ -101,7 +101,7 @@ 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/vector.S" + "${IDF_PATH}/components/ulp/lp_core/lp_core/port/${IDF_TARGET}/vector.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 +111,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_interrupt.c" "${IDF_PATH}/components/ulp/lp_core/lp_core/lp_core_i2c.c") target_link_options(${ULP_APP_NAME} PRIVATE "-nostartfiles") diff --git a/components/ulp/lp_core/include/ulp_lp_core.h b/components/ulp/lp_core/include/ulp_lp_core.h index 9a17cd5c80..447c814dda 100644 --- a/components/ulp/lp_core/include/ulp_lp_core.h +++ b/components/ulp/lp_core/include/ulp_lp_core.h @@ -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 diff --git a/components/ulp/lp_core/lp_core.c b/components/ulp/lp_core/lp_core.c index 71aefe2429..e578e077ac 100644 --- a/components/ulp/lp_core/lp_core.c +++ b/components/ulp/lp_core/lp_core.c @@ -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(); +} diff --git a/components/ulp/lp_core/lp_core/include/ulp_lp_core_gpio.h b/components/ulp/lp_core/lp_core/include/ulp_lp_core_gpio.h index 42745dc186..ffae326114 100644 --- a/components/ulp/lp_core/lp_core/include/ulp_lp_core_gpio.h +++ b/components/ulp/lp_core/lp_core/include/ulp_lp_core_gpio.h @@ -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 diff --git a/components/ulp/lp_core/lp_core/include/ulp_lp_core_interrupts.h b/components/ulp/lp_core/lp_core/include/ulp_lp_core_interrupts.h new file mode 100644 index 0000000000..f757711974 --- /dev/null +++ b/components/ulp/lp_core/lp_core/include/ulp_lp_core_interrupts.h @@ -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 diff --git a/components/ulp/lp_core/lp_core/include/ulp_lp_core_print.h b/components/ulp/lp_core/lp_core/include/ulp_lp_core_print.h index 56401145d7..6544ba7201 100644 --- a/components/ulp/lp_core/lp_core/include/ulp_lp_core_print.h +++ b/components/ulp/lp_core/lp_core/include/ulp_lp_core_print.h @@ -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 */ diff --git a/components/ulp/lp_core/lp_core/include/ulp_lp_core_utils.h b/components/ulp/lp_core/lp_core/include/ulp_lp_core_utils.h index bee3d6c574..8984525220 100644 --- a/components/ulp/lp_core/lp_core/include/ulp_lp_core_utils.h +++ b/components/ulp/lp_core/lp_core/include/ulp_lp_core_utils.h @@ -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 #include +#include /** * @brief Traverse all possible wake-up sources and update the wake-up cause so that @@ -72,6 +73,22 @@ __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); + #ifdef __cplusplus } #endif diff --git a/components/ulp/lp_core/lp_core/lp_core_interrupt.c b/components/ulp/lp_core/lp_core/lp_core_interrupt.c new file mode 100644 index 0000000000..114973b9cb --- /dev/null +++ b/components/ulp/lp_core/lp_core/lp_core_interrupt.c @@ -0,0 +1,92 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +#include "sdkconfig.h" +#include "hal/lp_core_ll.h" +#include "riscv/rv_utils.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); +} + +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); + +void ulp_lp_core_panic_handler(void) +{ + abort(); +} + +#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 diff --git a/components/ulp/lp_core/lp_core/lp_core_print.c b/components/ulp/lp_core/lp_core/lp_core_print.c index 785d59e6c4..a14a91bcc6 100644 --- a/components/ulp/lp_core/lp_core/lp_core_print.c +++ b/components/ulp/lp_core/lp_core/lp_core_print.c @@ -4,6 +4,7 @@ * SPDX-License-Identifier: Apache-2.0 */ #include +#include "sdkconfig.h" #include "ulp_lp_core_uart.h" #if !CONFIG_ULP_ROM_PRINT_ENABLE diff --git a/components/ulp/lp_core/lp_core/lp_core_utils.c b/components/ulp/lp_core/lp_core/lp_core_utils.c index 6c48a4ea28..7fa9657c7f 100644 --- a/components/ulp/lp_core/lp_core/lp_core_utils.c +++ b/components/ulp/lp_core/lp_core/lp_core_utils.c @@ -135,3 +135,13 @@ 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); +} diff --git a/components/ulp/lp_core/lp_core/port/esp32c6/vector.S b/components/ulp/lp_core/lp_core/port/esp32c6/vector.S new file mode 100644 index 0000000000..32331e71b4 --- /dev/null +++ b/components/ulp/lp_core/lp_core/port/esp32c6/vector.S @@ -0,0 +1,138 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "riscv/rvruntime-frames.h" + + .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 + +.macro save_mepc + csrr t0, mepc + sw t0, RV_STK_MEPC(sp) +.endm + +/* 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 + + + .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 */ + + +/* 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 diff --git a/components/ulp/lp_core/lp_core/port/esp32p4/vector.S b/components/ulp/lp_core/lp_core/port/esp32p4/vector.S new file mode 100644 index 0000000000..3853c6df50 --- /dev/null +++ b/components/ulp/lp_core/lp_core/port/esp32p4/vector.S @@ -0,0 +1,59 @@ +/* + * 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 + + +/* _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/start.S b/components/ulp/lp_core/lp_core/start.S index f13a8819bd..b4665c9a3c 100644 --- a/components/ulp/lp_core/lp_core/start.S +++ b/components/ulp/lp_core/lp_core/start.S @@ -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: diff --git a/components/ulp/lp_core/lp_core/vector.S b/components/ulp/lp_core/lp_core/vector.S deleted file mode 100644 index 76c41a81b1..0000000000 --- a/components/ulp/lp_core/lp_core/vector.S +++ /dev/null @@ -1,24 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2023 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 - */ - - .align 0x4, 0xff - .global _vector_table - .type _vector_table, @function -_vector_table: - .option push - .option norvc - - .rept 32 - nop - .endr - - .option pop - .size _vector_table, .-_vector_table diff --git a/components/ulp/test_apps/lp_core/main/CMakeLists.txt b/components/ulp/test_apps/lp_core/main/CMakeLists.txt index d539efc8fb..d9084ea2bf 100644 --- a/components/ulp/test_apps/lp_core/main/CMakeLists.txt +++ b/components/ulp/test_apps/lp_core/main/CMakeLists.txt @@ -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}") diff --git a/components/ulp/test_apps/lp_core/main/lp_core/test_main_isr.c b/components/ulp/test_apps/lp_core/main/lp_core/test_main_isr.c new file mode 100644 index 0000000000..c3cc33c910 --- /dev/null +++ b/components/ulp/test_apps/lp_core/main/lp_core/test_main_isr.c @@ -0,0 +1,42 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#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; +} diff --git a/components/ulp/test_apps/lp_core/main/test_lp_core.c b/components/ulp/test_apps/lp_core/main/test_lp_core.c index c1ebaaaf0e..a1dddc91ca 100644 --- a/components/ulp/test_apps/lp_core/main/test_lp_core.c +++ b/components/ulp/test_apps/lp_core/main/test_lp_core.c @@ -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); +} diff --git a/docs/component_info_ignore_file.txt b/docs/component_info_ignore_file.txt index 32bb50d4eb..21b0fd9c0c 100644 --- a/docs/component_info_ignore_file.txt +++ b/docs/component_info_ignore_file.txt @@ -6,6 +6,7 @@ components/ulp/lp_core/lp_core/include/ulp_lp_core_i2c.h components/ulp/lp_core/lp_core/include/ulp_lp_core_print.h components/ulp/lp_core/lp_core/include/ulp_lp_core_uart.h components/ulp/lp_core/lp_core/include/ulp_lp_core_utils.h +components/ulp/lp_core/lp_core/include/ulp_lp_core_interrupts.h # ESSL headers do not belong to any IDF component, in a user project it will come from a managed component components/driver/test_apps/components/esp_serial_slave_link/include/esp_serial_slave_link/essl_sdio.h components/driver/test_apps/components/esp_serial_slave_link/include/esp_serial_slave_link/essl_spi.h diff --git a/docs/doxygen/Doxyfile_esp32c6 b/docs/doxygen/Doxyfile_esp32c6 index c99dfba77b..ddd75aad3c 100644 --- a/docs/doxygen/Doxyfile_esp32c6 +++ b/docs/doxygen/Doxyfile_esp32c6 @@ -7,6 +7,7 @@ INPUT += \ $(PROJECT_PATH)/components/ulp/lp_core/lp_core/include/ulp_lp_core_print.h \ $(PROJECT_PATH)/components/ulp/lp_core/lp_core/include/ulp_lp_core_uart.h \ $(PROJECT_PATH)/components/ulp/lp_core/lp_core/include/ulp_lp_core_utils.h \ + $(PROJECT_PATH)/components/ulp/lp_core/lp_core/include/ulp_lp_core_interrupts.h \ $(PROJECT_PATH)/components/bt/include/esp32c6/include/esp_bt.h \ $(PROJECT_PATH)/components/esp_phy/include/esp_phy_init.h \ $(PROJECT_PATH)/components/esp_phy/include/esp_phy_cert_test.h \ diff --git a/docs/doxygen/Doxyfile_esp32p4 b/docs/doxygen/Doxyfile_esp32p4 index 7c914430b3..70c98b4c05 100644 --- a/docs/doxygen/Doxyfile_esp32p4 +++ b/docs/doxygen/Doxyfile_esp32p4 @@ -7,6 +7,7 @@ INPUT += \ $(PROJECT_PATH)/components/ulp/lp_core/lp_core/include/ulp_lp_core_print.h \ $(PROJECT_PATH)/components/ulp/lp_core/lp_core/include/ulp_lp_core_uart.h \ $(PROJECT_PATH)/components/ulp/lp_core/lp_core/include/ulp_lp_core_utils.h \ + $(PROJECT_PATH)/components/ulp/lp_core/lp_core/include/ulp_lp_core_interrupts.h \ $(PROJECT_PATH)/components/ulp/ulp_common/include/ulp_common.h \ $(PROJECT_PATH)/components/usb/include/usb/usb_helpers.h \ $(PROJECT_PATH)/components/usb/include/usb/usb_host.h \ diff --git a/docs/en/api-reference/system/ulp-lp-core.rst b/docs/en/api-reference/system/ulp-lp-core.rst index d53d6438d8..4d80c88c60 100644 --- a/docs/en/api-reference/system/ulp-lp-core.rst +++ b/docs/en/api-reference/system/ulp-lp-core.rst @@ -178,6 +178,24 @@ 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 +---------------------- + +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.` + +For example, to override the handler for the LP IO interrupt, you can define the following function in your ULP LP-Core code: + +.. code-block:: c + + void LP_CORE_ISR_ATTR ulp_lp_core_lp_io_intr_handler(void) + { + // Handle the interrupt and clear the interrupt source + } + +:c:macro:`LP_CORE_ISR_ATTR` is a macro that is used to define the interrupt handler function. This macro ensures that registers are saved and restored correctly when the interrupt handler is called. + +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. + Application Examples -------------------- @@ -185,6 +203,7 @@ Application Examples * :example:`system/ulp/lp_core/lp_i2c` reads external I2C ambient light sensor (BH1750) while the main CPU is in Deep-sleep and wakes up the main CPU once a threshold is met. * :example:`system/ulp/lp_core/lp_uart/lp_uart_echo` reads data written to a serial console and echoes it back. This example demonstrates the usage of the LP UART driver running on the LP core. * :example:`system/ulp/lp_core/lp_uart/lp_uart_print` shows how to print various statements from a program running on the LP core. +* :example:`system/ulp/lp_core/interrupt` shows how to register an interrupt handler on the LP core to receive an interrupt triggered by the main CPU. API Reference ------------- @@ -204,3 +223,4 @@ LP Core API Reference .. include-build-file:: inc/ulp_lp_core_i2c.inc .. 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 diff --git a/examples/system/.build-test-rules.yml b/examples/system/.build-test-rules.yml index 5bc2392b62..bcfa80fbd3 100644 --- a/examples/system/.build-test-rules.yml +++ b/examples/system/.build-test-rules.yml @@ -264,6 +264,12 @@ examples/system/ulp/lp_core/gpio: depends_components: - ulp +examples/system/ulp/lp_core/interrupt: + enable: + - if: SOC_LP_CORE_SUPPORTED == 1 + depends_components: + - ulp + examples/system/ulp/lp_core/lp_i2c: enable: - if: SOC_LP_I2C_SUPPORTED == 1 diff --git a/examples/system/ulp/lp_core/interrupt/CMakeLists.txt b/examples/system/ulp/lp_core/interrupt/CMakeLists.txt new file mode 100644 index 0000000000..de93ae527f --- /dev/null +++ b/examples/system/ulp/lp_core/interrupt/CMakeLists.txt @@ -0,0 +1,8 @@ +# For more information about build system see +# https://docs.espressif.com/projects/esp-idf/en/latest/api-guides/build-system.html +# The following five lines of boilerplate have to be in your project's +# CMakeLists in this exact order for cmake to work correctly +cmake_minimum_required(VERSION 3.16) + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +project(interrupts) diff --git a/examples/system/ulp/lp_core/interrupt/README.md b/examples/system/ulp/lp_core/interrupt/README.md new file mode 100644 index 0000000000..b508bd913d --- /dev/null +++ b/examples/system/ulp/lp_core/interrupt/README.md @@ -0,0 +1,19 @@ +| Supported Targets | ESP32-C6 | ESP32-P4 | +| ----------------- | -------- | -------- | + +# LP-Core example with interrupt triggered from HP-Core: + +This example demonstrates how to program the ULP coprocessor to receive an interrupt triggered by the HP-Core + +ULP program written in C can be found across `lp_core/main.c`. The build system compiles and links this program, converts it into binary format, and embeds it into the .rodata section of the ESP-IDF application. + +At runtime, the application running inside the main CPU loads ULP program into the `RTC_SLOW_MEM` memory region using `ulp_lp_core_load_binary` function. The main code then configures the ULP and starts the coprocessor by using `ulp_lp_core_run`. Once the ULP program is started, it runs continuously, waiting for interrupts. The main program will periodically trigger interrupts on the LP-Core. + +After triggering a certain amount of interrupts, the main core will read and print the number of interrupts received as reported by the LP-Core. + +## Example output + +``` +LP core loaded with firmware and running successfully +Triggered 10 interrupts on the LP-Core, LP-Core received 10 interrupts +``` \ No newline at end of file diff --git a/examples/system/ulp/lp_core/interrupt/main/CMakeLists.txt b/examples/system/ulp/lp_core/interrupt/main/CMakeLists.txt new file mode 100644 index 0000000000..6eb2981339 --- /dev/null +++ b/examples/system/ulp/lp_core/interrupt/main/CMakeLists.txt @@ -0,0 +1,25 @@ +# Register the component +idf_component_register(SRCS "lp_interrupts_main.c" + INCLUDE_DIRS "" + REQUIRES ulp) + +# +# ULP support additions to component CMakeLists.txt. +# +# 1. The LP Core app name must be unique (if multiple components use LP Core). +set(ulp_app_name lp_core_${COMPONENT_NAME}) +# +# 2. Specify all C files. +# Files should be placed into a separate directory (in this case, lp_core/), +# which should not be added to COMPONENT_SRCS. +set(ulp_lp_core_sources "lp_core/main.c") + +# +# 3. List all the component source files which include automatically +# generated LP Core export file, ${ulp_app_name}.h: +set(ulp_exp_dep_srcs "lp_interrupts_main.c") + +# +# 4. Call function to build ULP binary and embed in project using the argument +# values above. +ulp_embed_binary(${ulp_app_name} "${ulp_lp_core_sources}" "${ulp_exp_dep_srcs}") diff --git a/examples/system/ulp/lp_core/interrupt/main/lp_core/main.c b/examples/system/ulp/lp_core/interrupt/main/lp_core/main.c new file mode 100644 index 0000000000..c97f8e4f98 --- /dev/null +++ b/examples/system/ulp/lp_core/interrupt/main/lp_core/main.c @@ -0,0 +1,30 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include "ulp_lp_core_utils.h" +#include "ulp_lp_core_interrupts.h" + +uint32_t lp_core_pmu_intr_count = 0; + +/* Add LP_CORE_ISR_ATTR to ensure registers are saved and restored */ +void LP_CORE_ISR_ATTR ulp_lp_core_lp_pmu_intr_handler(void) +{ + ulp_lp_core_sw_intr_clear(); + lp_core_pmu_intr_count++; +} + +int main (void) +{ + ulp_lp_core_intr_enable(); + ulp_lp_core_sw_intr_enable(true); + + while(1) { + /* Wait forever, handling interrupts */ + asm volatile("wfi"); + } + return 0; +} diff --git a/examples/system/ulp/lp_core/interrupt/main/lp_interrupts_main.c b/examples/system/ulp/lp_core/interrupt/main/lp_interrupts_main.c new file mode 100644 index 0000000000..274b1b00b2 --- /dev/null +++ b/examples/system/ulp/lp_core/interrupt/main/lp_interrupts_main.c @@ -0,0 +1,53 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include "esp_sleep.h" +#include "esp_err.h" +#include "lp_core_main.h" +#include "ulp_lp_core.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" + +extern const uint8_t lp_core_main_bin_start[] asm("_binary_lp_core_main_bin_start"); +extern const uint8_t lp_core_main_bin_end[] asm("_binary_lp_core_main_bin_end"); + + +static void lp_core_init(void) +{ + /* Set LP core wakeup source as the HP CPU */ + ulp_lp_core_cfg_t cfg = { + .wakeup_source = ULP_LP_CORE_WAKEUP_SOURCE_HP_CPU, + }; + + /* Load LP core firmware */ + ESP_ERROR_CHECK(ulp_lp_core_load_binary(lp_core_main_bin_start, (lp_core_main_bin_end - lp_core_main_bin_start))); + + /* Run LP core */ + ESP_ERROR_CHECK(ulp_lp_core_run(&cfg)); + + // Give the LP core time to start up + vTaskDelay(pdMS_TO_TICKS(100)); + + printf("LP core loaded with firmware and running successfully\n"); +} + +#define INTERRUPT_COUNT 10 + +void app_main(void) +{ + /* Load LP Core binary and start the coprocessor */ + lp_core_init(); + + for (int i = 0; i < INTERRUPT_COUNT; i++) { + /* In addition to waking the LP source up, the HP-LP communication bit can also be used to trigger a PMU interrupt on the LP Core */ + ulp_lp_core_sw_intr_trigger(); + vTaskDelay(pdMS_TO_TICKS(100)); + } + + printf("Triggered %d interrupts on the LP-Core, LP-Core received %"PRIu32" interrupts\n", INTERRUPT_COUNT, ulp_lp_core_pmu_intr_count); + +} diff --git a/examples/system/ulp/lp_core/interrupt/pytest_lp_core_intr.py b/examples/system/ulp/lp_core/interrupt/pytest_lp_core_intr.py new file mode 100644 index 0000000000..a9f96aba6c --- /dev/null +++ b/examples/system/ulp/lp_core/interrupt/pytest_lp_core_intr.py @@ -0,0 +1,11 @@ +# SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD +# SPDX-License-Identifier: CC0-1.0 +import pytest +from pytest_embedded import Dut + + +@pytest.mark.esp32c6 +@pytest.mark.esp32p4 +@pytest.mark.generic +def test_lp_core_intr(dut: Dut) -> None: + dut.expect('Triggered 10 interrupts on the LP-Core, LP-Core received 10 interrupts') diff --git a/examples/system/ulp/lp_core/interrupt/sdkconfig.defaults b/examples/system/ulp/lp_core/interrupt/sdkconfig.defaults new file mode 100644 index 0000000000..2e829c6faa --- /dev/null +++ b/examples/system/ulp/lp_core/interrupt/sdkconfig.defaults @@ -0,0 +1,4 @@ +# Enable ULP +CONFIG_ULP_COPROC_ENABLED=y +CONFIG_ULP_COPROC_TYPE_LP_CORE=y +CONFIG_ULP_COPROC_RESERVE_MEM=4096