mirror of
https://github.com/espressif/esp-idf.git
synced 2024-10-05 20:47:46 -04:00
feat(ulp): support interrupts for C6/P4 LP core
Closes https://github.com/espressif/esp-idf/issues/13059
This commit is contained in:
parent
73190dd04e
commit
c5a513cf49
@ -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,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.
|
||||
*
|
||||
|
@ -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;
|
||||
|
@ -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.
|
||||
*
|
||||
|
@ -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")
|
||||
|
@ -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
|
||||
|
@ -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,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
|
||||
|
92
components/ulp/lp_core/lp_core/lp_core_interrupt.c
Normal file
92
components/ulp/lp_core/lp_core/lp_core_interrupt.c
Normal file
@ -0,0 +1,92 @@
|
||||
/*
|
||||
* 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"
|
||||
|
||||
#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
|
@ -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,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);
|
||||
}
|
||||
|
138
components/ulp/lp_core/lp_core/port/esp32c6/vector.S
Normal file
138
components/ulp/lp_core/lp_core/port/esp32c6/vector.S
Normal file
@ -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
|
59
components/ulp/lp_core/lp_core/port/esp32p4/vector.S
Normal file
59
components/ulp/lp_core/lp_core/port/esp32p4/vector.S
Normal file
@ -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 */
|
@ -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:
|
||||
|
@ -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
|
@ -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}")
|
||||
|
@ -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,
|
||||
@ -325,3 +333,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);
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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 \
|
||||
|
@ -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 \
|
||||
|
@ -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 <ulp/lp_core/lp_core/include/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
|
||||
|
@ -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
|
||||
|
8
examples/system/ulp/lp_core/interrupt/CMakeLists.txt
Normal file
8
examples/system/ulp/lp_core/interrupt/CMakeLists.txt
Normal file
@ -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)
|
19
examples/system/ulp/lp_core/interrupt/README.md
Normal file
19
examples/system/ulp/lp_core/interrupt/README.md
Normal file
@ -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
|
||||
```
|
25
examples/system/ulp/lp_core/interrupt/main/CMakeLists.txt
Normal file
25
examples/system/ulp/lp_core/interrupt/main/CMakeLists.txt
Normal file
@ -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}")
|
30
examples/system/ulp/lp_core/interrupt/main/lp_core/main.c
Normal file
30
examples/system/ulp/lp_core/interrupt/main/lp_core/main.c
Normal file
@ -0,0 +1,30 @@
|
||||
/*
|
||||
* 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"
|
||||
|
||||
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;
|
||||
}
|
@ -0,0 +1,53 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#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);
|
||||
|
||||
}
|
11
examples/system/ulp/lp_core/interrupt/pytest_lp_core_intr.py
Normal file
11
examples/system/ulp/lp_core/interrupt/pytest_lp_core_intr.py
Normal file
@ -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')
|
4
examples/system/ulp/lp_core/interrupt/sdkconfig.defaults
Normal file
4
examples/system/ulp/lp_core/interrupt/sdkconfig.defaults
Normal file
@ -0,0 +1,4 @@
|
||||
# Enable ULP
|
||||
CONFIG_ULP_COPROC_ENABLED=y
|
||||
CONFIG_ULP_COPROC_TYPE_LP_CORE=y
|
||||
CONFIG_ULP_COPROC_RESERVE_MEM=4096
|
Loading…
Reference in New Issue
Block a user