From 1792aba1dcc5ae7cf8aead79663d244787a2bbc2 Mon Sep 17 00:00:00 2001 From: Armando Date: Thu, 26 Sep 2024 16:18:30 +0800 Subject: [PATCH] feat(vad): lp vad driver and wakeup feature --- components/esp_driver_i2s/CMakeLists.txt | 4 + .../include/driver/lp_i2s_vad.h | 153 ++++++++++++++++++ .../include/esp_private/lp_i2s_private.h | 27 ++++ components/esp_driver_i2s/lp_i2s.c | 13 ++ components/esp_driver_i2s/lp_i2s_vad.c | 112 +++++++++++++ .../hal/esp32p4/include/hal/lp_core_ll.h | 2 +- .../hal/esp32p4/include/hal/lp_i2s_ll.h | 106 +++++++++++- .../esp32p4/include/soc/Kconfig.soc_caps.in | 12 ++ components/soc/esp32p4/include/soc/soc_caps.h | 5 + components/ulp/CMakeLists.txt | 4 + components/ulp/cmake/IDFULPProject.cmake | 1 + components/ulp/lp_core/include/ulp_lp_core.h | 1 + components/ulp/lp_core/lp_core.c | 5 +- .../ulp/lp_core/lp_core/lp_core_utils.c | 11 ++ .../include/ulp_lp_core_lp_vad_shared.h | 121 ++++++++++++++ .../shared/ulp_lp_core_lp_vad_shared.c | 65 ++++++++ 16 files changed, 635 insertions(+), 7 deletions(-) create mode 100644 components/esp_driver_i2s/include/driver/lp_i2s_vad.h create mode 100644 components/esp_driver_i2s/include/esp_private/lp_i2s_private.h create mode 100644 components/esp_driver_i2s/lp_i2s_vad.c create mode 100644 components/ulp/lp_core/shared/include/ulp_lp_core_lp_vad_shared.h create mode 100644 components/ulp/lp_core/shared/ulp_lp_core_lp_vad_shared.c diff --git a/components/esp_driver_i2s/CMakeLists.txt b/components/esp_driver_i2s/CMakeLists.txt index cc39943ae6..2498fa6126 100644 --- a/components/esp_driver_i2s/CMakeLists.txt +++ b/components/esp_driver_i2s/CMakeLists.txt @@ -30,6 +30,10 @@ if(CONFIG_SOC_LP_I2S_SUPPORTED) list(APPEND srcs "lp_i2s.c" "lp_i2s_std.c" "lp_i2s_pdm.c") endif() +if(CONFIG_SOC_LP_I2S_SUPPORT_VAD) + list(APPEND srcs "lp_i2s_vad.c") +endif() + idf_component_register(SRCS ${srcs} INCLUDE_DIRS ${include} PRIV_REQUIRES esp_driver_gpio esp_pm esp_mm diff --git a/components/esp_driver_i2s/include/driver/lp_i2s_vad.h b/components/esp_driver_i2s/include/driver/lp_i2s_vad.h new file mode 100644 index 0000000000..ce52370cde --- /dev/null +++ b/components/esp_driver_i2s/include/driver/lp_i2s_vad.h @@ -0,0 +1,153 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include +#include +#include "soc/soc_caps.h" +#include "esp_err.h" +#include "driver/i2s_types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief State Machine + ┌──────────────────────────────────┐ + │ │ + ┌─────────────┤ speak-activity-listening-state │ ◄───────────────┐ + │ │ │ │ + │ └──────────────────────────────────┘ │ + │ ▲ │ + │ │ │ + │ │ │ + │ │ │ + │ │ │ +detected speak activity │ │ detected speak activity │ detected speak activity + >= │ │ >= │ >= +'speak_activity_thresh' │ │ 'min_speak_activity_thresh' │ 'max_speak_activity_thresh' + │ │ │ + │ │ && │ + │ │ │ + │ │ detected non-speak activity │ + │ │ < │ + │ │ 'non_speak_activity_thresh' │ + │ │ │ + │ │ │ + │ │ │ + │ │ │ + │ │ │ + │ ┌───────────┴─────────────────────┐ │ + │ │ │ │ + └───────────► │ speak-activity-detected-state ├─────────────────┘ + │ │ + └─┬───────────────────────────────┘ + │ + │ ▲ + │ │ + │ │ + │ │ detected speak activity + │ │ >= + │ │ 'min_speak_activity_thresh' + │ │ + │ │ && + │ │ + │ │ detected non-speak activity + │ │ < + └─────────────────────┘ 'non_speak_activity_thresh' +*/ + +/** + * @brief LP VAD peripheral + */ +typedef uint32_t lp_vad_t; + +/** + * @brief Type of VAD unit handle + */ +typedef struct vad_unit_ctx_t *vad_unit_handle_t; + +/** + * @brief LP VAD configurations + */ +typedef struct { + int init_frame_num; /**< Number of init frames that are used for VAD to denoise, this helps the VAD to decrease the accidental trigger ratio. + Note too big values may lead to voice activity miss */ + int min_energy_thresh; ///< Min energy threshold. + bool skip_band_energy_thresh; ///< Skip band energy threshold or not + + int speak_activity_thresh; /**< When in speak-activity-listening-state, if number of the detected speak activity is higher than this value, VAD runs into speak-activity-detected-state */ + + int non_speak_activity_thresh; /**< When in speak-activity-detected-state, if the number of the detected speak activity is higher than this value, but lower than `max_speak_activity_thresh`: + - if the number of the detected non-speak activity is higher than this value, VAD runs into speak-activity-listening-state + - if the number of the detected non-speak activity is lower than this value, VAD keeps in speak-activity-detected-state */ + + int min_speak_activity_thresh; /**< When in speak-activity-detected-state, if the number of the detected speak activity is higher than this value, but lower than `max_speak_activity_thresh`, + then the VAD state machine will depends on the value of `non_speak_activity_thresh` */ + + int max_speak_activity_thresh; /**< When in speak-activity-detected-state, if the number of the detected speak activity is higher than this value, VAD runs into speak-activity-listening-state */ +} lp_vad_config_t; + +typedef struct { + lp_i2s_chan_handle_t lp_i2s_chan; ///< LP I2S channel handle + lp_vad_config_t vad_config; ///< LP VAD config +} lp_vad_init_config_t; + +/** + * @brief New LP VAD unit + * @param[in] vad_id VAD id + * @param[in] init_config Initial configurations + * @param[out] ret_unit Unit handle + * + * @return + * - ESP_OK: On success + * - ESP_ERR_INVALID_ARG: Invalid argument + * - ESP_ERR_INVALID_STATE: Driver state is invalid, you shouldn't call this API at this moment + */ +esp_err_t lp_i2s_vad_new_unit(lp_vad_t vad_id, const lp_vad_init_config_t *init_config, vad_unit_handle_t *ret_unit); + +/** + * @brief Enable LP VAD + * + * @param[in] unit VAD handle + * @param[in] init_config Initial configurations + * + * @return + * - ESP_OK: On success + * - ESP_ERR_INVALID_ARG: Invalid argument + * - ESP_ERR_INVALID_STATE: Driver state is invalid, you shouldn't call this API at this moment + */ +esp_err_t lp_i2s_vad_enable(vad_unit_handle_t unit); + +/** + * @brief Disable LP VAD + * + * @param[in] unit VAD handle + * @param[in] init_config Initial configurations + * + * @return + * - ESP_OK: On success + * - ESP_ERR_INVALID_ARG: Invalid argument + * - ESP_ERR_INVALID_STATE: Driver state is invalid, you shouldn't call this API at this moment + */ +esp_err_t lp_i2s_vad_disable(vad_unit_handle_t unit); + +/** + * @brief Delete LP VAD unit + * @param[in] unit VAD handle + * + * @return + * - ESP_OK: On success + * - ESP_ERR_INVALID_ARG: Invalid argument + * - ESP_ERR_INVALID_STATE: Driver state is invalid, you shouldn't call this API at this moment + */ +esp_err_t lp_i2s_vad_del_unit(vad_unit_handle_t unit); + +#ifdef __cplusplus +} +#endif diff --git a/components/esp_driver_i2s/include/esp_private/lp_i2s_private.h b/components/esp_driver_i2s/include/esp_private/lp_i2s_private.h new file mode 100644 index 0000000000..2785ca0263 --- /dev/null +++ b/components/esp_driver_i2s/include/esp_private/lp_i2s_private.h @@ -0,0 +1,27 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include "hal/lp_i2s_hal.h" +#include "driver/i2s_types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Get LP I2S soc handle + * + * @param[in] chan LP I2S channel handle + * + * @return LP I2S soc handle + */ +lp_i2s_soc_handle_t lp_i2s_get_soc_handle(lp_i2s_chan_handle_t chan); + +#ifdef __cplusplus +} +#endif diff --git a/components/esp_driver_i2s/lp_i2s.c b/components/esp_driver_i2s/lp_i2s.c index 7084fc8947..0d5e43dd8d 100644 --- a/components/esp_driver_i2s/lp_i2s.c +++ b/components/esp_driver_i2s/lp_i2s.c @@ -25,6 +25,7 @@ #include "driver/lp_i2s.h" #include "esp_private/periph_ctrl.h" #include "esp_private/i2s_platform.h" +#include "esp_private/lp_i2s_private.h" #include "i2s_private.h" #include "soc/i2s_periph.h" @@ -329,3 +330,15 @@ static void IRAM_ATTR s_i2s_default_isr(void *arg) portYIELD_FROM_ISR(); } } + +/*--------------------------------------------------------------- + HELPERS +---------------------------------------------------------------*/ +lp_i2s_soc_handle_t lp_i2s_get_soc_handle(lp_i2s_chan_handle_t chan) +{ + if (!chan) { + return NULL; + } + + return chan->ctlr->hal.dev; +} diff --git a/components/esp_driver_i2s/lp_i2s_vad.c b/components/esp_driver_i2s/lp_i2s_vad.c new file mode 100644 index 0000000000..f1cc63559e --- /dev/null +++ b/components/esp_driver_i2s/lp_i2s_vad.c @@ -0,0 +1,112 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include "soc/soc_caps.h" +#include "stdatomic.h" +#if SOC_LP_VAD_SUPPORTED +#include "esp_check.h" +#include "esp_err.h" +#include "driver/lp_i2s_vad.h" +#include "esp_heap_caps.h" +#include "hal/lp_i2s_ll.h" +#include "hal/lp_i2s_hal.h" +#include "esp_private/lp_i2s_private.h" + +#define LP_VAD_MEM_ALLOC_CAPS (MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT) + +static const char *LP_VAD_TAG = "LP_VAD"; + +typedef enum { + VAD_FSM_INIT, + VAD_FSM_ENABLE, +} vad_fsm_t; + +typedef struct vad_unit_ctx_t { + lp_i2s_soc_handle_t hw; + lp_vad_t vad_id; + vad_fsm_t fsm; +} vad_unit_ctx_t; + +static atomic_bool s_vad_id_claimed[SOC_ADC_PERIPH_NUM] = {ATOMIC_VAR_INIT(false)}; + +static bool s_vad_claim(lp_vad_t vad_id) +{ + bool false_var = false; + return atomic_compare_exchange_strong(&s_vad_id_claimed[vad_id], &false_var, true); +} + +static bool s_vad_free(lp_vad_t vad_id) +{ + bool true_var = true; + return atomic_compare_exchange_strong(&s_vad_id_claimed[vad_id], &true_var, false); +} + +esp_err_t lp_i2s_vad_new_unit(lp_vad_t vad_id, const lp_vad_init_config_t *init_config, vad_unit_handle_t *ret_unit) +{ + esp_err_t ret = ESP_OK; + ESP_RETURN_ON_FALSE(init_config, ESP_ERR_INVALID_ARG, LP_VAD_TAG, "invalid arg"); + ESP_RETURN_ON_FALSE(init_config->lp_i2s_chan, ESP_ERR_INVALID_ARG, LP_VAD_TAG, "LP I2S not initialised"); + ESP_RETURN_ON_FALSE(init_config->vad_config.init_frame_num >= LP_VAD_LL_INIT_FRAME_MIN && init_config->vad_config.init_frame_num <= LP_VAD_LL_INIT_FRAME_MAX, ESP_ERR_INVALID_ARG, LP_VAD_TAG, "invalid init frame num"); + + bool success_claim = s_vad_claim(vad_id); + ESP_RETURN_ON_FALSE(success_claim, ESP_ERR_NOT_FOUND, LP_VAD_TAG, "vad%"PRId32" is already in use", vad_id); + + vad_unit_ctx_t *unit = heap_caps_calloc(1, sizeof(vad_unit_ctx_t), LP_VAD_MEM_ALLOC_CAPS); + ESP_GOTO_ON_FALSE(unit, ESP_ERR_NO_MEM, err, LP_VAD_TAG, "no mem for unit"); + + unit->hw = lp_i2s_get_soc_handle(init_config->lp_i2s_chan); + ESP_LOGD(LP_VAD_TAG, "unit->hw: %p", unit->hw); + lp_vad_ll_set_init_frame_num(unit->hw, init_config->vad_config.init_frame_num); + lp_vad_ll_set_init_min_energy(unit->hw, init_config->vad_config.min_energy_thresh); + lp_vad_ll_set_speak_activity_thresh(unit->hw, init_config->vad_config.speak_activity_thresh); + lp_vad_ll_set_non_speak_activity_thresh(unit->hw, init_config->vad_config.non_speak_activity_thresh); + lp_vad_ll_set_min_speak_activity_thresh(unit->hw, init_config->vad_config.min_speak_activity_thresh); + lp_vad_ll_set_max_speak_activity_thresh(unit->hw, init_config->vad_config.max_speak_activity_thresh); + lp_vad_ll_skip_band_energy(unit->hw, init_config->vad_config.skip_band_energy_thresh); + unit->fsm = VAD_FSM_INIT; + *ret_unit = unit; + + return ESP_OK; +err: + bool success_free = s_vad_free(vad_id); + assert(success_free); + + return ret; +} + +esp_err_t lp_i2s_vad_enable(vad_unit_handle_t unit) +{ + ESP_RETURN_ON_FALSE(unit, ESP_ERR_INVALID_ARG, LP_VAD_TAG, "invalid arg"); + ESP_RETURN_ON_FALSE(unit->fsm == VAD_FSM_INIT, ESP_ERR_INVALID_STATE, LP_VAD_TAG, "The driver is enabled already"); + + lp_vad_ll_enable(unit->hw, true); + unit->fsm = VAD_FSM_ENABLE; + return ESP_OK; +} + +esp_err_t lp_i2s_vad_disable(vad_unit_handle_t unit) +{ + ESP_RETURN_ON_FALSE(unit, ESP_ERR_INVALID_ARG, LP_VAD_TAG, "invalid arg"); + ESP_RETURN_ON_FALSE(unit->fsm == VAD_FSM_ENABLE, ESP_ERR_INVALID_STATE, LP_VAD_TAG, "The driver is not enabled yet"); + + lp_vad_ll_enable(unit->hw, false); + unit->fsm = VAD_FSM_INIT; + return ESP_OK; +} + +esp_err_t lp_i2s_vad_del_unit(vad_unit_handle_t unit) +{ + ESP_RETURN_ON_FALSE(unit, ESP_ERR_INVALID_ARG, LP_VAD_TAG, "invalid arg"); + ESP_RETURN_ON_FALSE(unit->fsm == VAD_FSM_INIT, ESP_ERR_INVALID_STATE, LP_VAD_TAG, "The driver is still in enabled state"); + + bool success_free = s_vad_free(unit->vad_id); + ESP_RETURN_ON_FALSE(success_free, ESP_ERR_NOT_FOUND, LP_VAD_TAG, "vad%"PRId32" isn't in use", unit->vad_id); + + free(unit); + return ESP_OK; +} +#endif /* SOC_LP_VAD_SUPPORTED */ diff --git a/components/hal/esp32p4/include/hal/lp_core_ll.h b/components/hal/esp32p4/include/hal/lp_core_ll.h index 27a9a0997f..eb2c57be31 100644 --- a/components/hal/esp32p4/include/hal/lp_core_ll.h +++ b/components/hal/esp32p4/include/hal/lp_core_ll.h @@ -28,7 +28,7 @@ extern "C" { #define LP_CORE_LL_WAKEUP_SOURCE_LP_BOD BIT(14) #define LP_CORE_LL_WAKEUP_SOURCE_ETM BIT(17) #define LP_CORE_LL_WAKEUP_SOURCE_LP_TIMER_1 BIT(18) -#define LP_CORE_LL_WAKEUP_SOURCE_LP_I2S BIT(19) +#define LP_CORE_LL_WAKEUP_SOURCE_LP_VAD BIT(19) #define LP_CORE_LL_WAKEUP_SOURCE_HP_CPU BIT(22) /* Use lp timer 1 as the normal wakeup timer, timer 0 is used by deep sleep */ diff --git a/components/hal/esp32p4/include/hal/lp_i2s_ll.h b/components/hal/esp32p4/include/hal/lp_i2s_ll.h index c9f0d5611b..23e81cd2a5 100644 --- a/components/hal/esp32p4/include/hal/lp_i2s_ll.h +++ b/components/hal/esp32p4/include/hal/lp_i2s_ll.h @@ -32,6 +32,8 @@ extern "C" { #define LP_I2S_LL_EVENT_RX_DONE_INT (1<<0) #define LP_I2S_LL_EVENT_RX_HUNG_INT_INT (1<<1) #define LP_I2S_LL_EVENT_RX_FIFOMEM_UDF_INT (1<<2) +#define LP_I2S_LL_EVENT_VAD_DONE_INT (1<<3) +#define LP_I2S_LL_EVENT_VAD_RESET_DONE_INT (1<<4) #define LP_I2S_LL_EVENT_RX_MEM_THRESHOLD_INT (1<<5) #define LP_I2S_LL_TDM_CH_MASK (0x03UL) @@ -709,9 +711,9 @@ static inline uint32_t lp_i2s_ll_get_intr_status_reg_addr(lp_i2s_dev_t *hw) /** * @brief Enable LP I2S RX channel interrupt * - * @param hw LP I2S hardware instance - * @param mask mask - * @param enable enable or disable + * @param[in] hw LP I2S hardware instance + * @param[in] mask mask + * @param[in] enable enable or disable */ static inline void lp_i2s_ll_rx_enable_interrupt(lp_i2s_dev_t *hw, uint32_t mask, bool enable) { @@ -727,8 +729,8 @@ static inline void lp_i2s_ll_rx_enable_interrupt(lp_i2s_dev_t *hw, uint32_t mask /** * @brief Clear LP I2S RX channel interrupt * - * @param hw LP I2S hardware instance - * @param mask mask + * @param[in] hw LP I2S hardware instance + * @param[in] mask mask */ __attribute__((always_inline)) static inline void lp_i2s_ll_rx_clear_interrupt_status(lp_i2s_dev_t *hw, uint32_t mask) @@ -736,6 +738,100 @@ static inline void lp_i2s_ll_rx_clear_interrupt_status(lp_i2s_dev_t *hw, uint32_ hw->int_clr.val = mask; } +/*--------------------------------------------------------------- + VAD +---------------------------------------------------------------*/ +#define LP_VAD_LL_INIT_FRAME_MIN 100 +#define LP_VAD_LL_INIT_FRAME_MAX 200 + +/** + * @brief Set VAD init frame number + * + * @param[in] hw LP I2S hardware instance + * @param[in] frame_num Frame number + */ +static inline void lp_vad_ll_set_init_frame_num(lp_i2s_dev_t *hw, int frame_num) +{ + hw->vad_param0.param_init_frame_num = frame_num; +} + +/** + * @brief Set VAD min energy + * + * @param[in] hw LP I2S hardware instance + * @param[in] min_energy Min energy + */ +static inline void lp_vad_ll_set_init_min_energy(lp_i2s_dev_t *hw, int min_energy) +{ + HAL_FORCE_MODIFY_U32_REG_FIELD(hw->vad_param0, param_min_energy, min_energy); +} + +/** + * @brief Set VAD speak activity thresh + * + * @param[in] hw LP I2S hardware instance + * @param[in] thresh Threshold + */ +static inline void lp_vad_ll_set_speak_activity_thresh(lp_i2s_dev_t *hw, int thresh) +{ + hw->vad_param1.param_hangover_speech = thresh; +} + +/** + * @brief Set VAD non speak activity thresh + * + * @param[in] hw LP I2S hardware instance + * @param[in] thresh Threshold + */ +static inline void lp_vad_ll_set_non_speak_activity_thresh(lp_i2s_dev_t *hw, int thresh) +{ + hw->vad_param1.param_hangover_silent = thresh; +} + +/** + * @brief Set VAD min speak activity thresh + * + * @param[in] hw LP I2S hardware instance + * @param[in] thresh Threshold + */ +static inline void lp_vad_ll_set_min_speak_activity_thresh(lp_i2s_dev_t *hw, int thresh) +{ + hw->vad_param1.param_min_speech_count = thresh; +} + +/** + * @brief Set VAD max speak activity thresh + * + * @param[in] hw LP I2S hardware instance + * @param[in] thresh Threshold + */ +static inline void lp_vad_ll_set_max_speak_activity_thresh(lp_i2s_dev_t *hw, int thresh) +{ + hw->vad_param1.param_max_speech_count = thresh; +} + +/** + * @brief Skip band energy check + * + * @param[in] hw LP I2S hardware instance + * @param[in] skip 1: skip; 0: not skip + */ +static inline void lp_vad_ll_skip_band_energy(lp_i2s_dev_t *hw, bool skip) +{ + hw->vad_param1.param_skip_band_energy = skip; +} + +/** + * @brief Enable LP I2S 24 fill + * + * @param[in] hw LP I2S hardware instance + * @param[in] en enable or disable + */ +static inline void lp_vad_ll_enable(lp_i2s_dev_t *hw, bool en) +{ + hw->vad_conf.vad_en = en; +} + #ifdef __cplusplus } #endif diff --git a/components/soc/esp32p4/include/soc/Kconfig.soc_caps.in b/components/soc/esp32p4/include/soc/Kconfig.soc_caps.in index f8319e722e..eac76a74a3 100644 --- a/components/soc/esp32p4/include/soc/Kconfig.soc_caps.in +++ b/components/soc/esp32p4/include/soc/Kconfig.soc_caps.in @@ -263,6 +263,10 @@ config SOC_LP_ADC_SUPPORTED bool default y +config SOC_LP_VAD_SUPPORTED + bool + default y + config SOC_SPIRAM_SUPPORTED bool default y @@ -1763,6 +1767,10 @@ config SOC_UART_SUPPORT_FSM_TX_WAIT_SEND bool default y +config SOC_LP_I2S_SUPPORT_VAD + bool + default y + config SOC_COEX_HW_PTI bool default y @@ -1962,3 +1970,7 @@ config SOC_LP_CORE_SUPPORT_ETM config SOC_LP_CORE_SUPPORT_LP_ADC bool default y + +config SOC_LP_CORE_SUPPORT_LP_VAD + bool + default y diff --git a/components/soc/esp32p4/include/soc/soc_caps.h b/components/soc/esp32p4/include/soc/soc_caps.h index a8a90bda86..16d2aa7643 100644 --- a/components/soc/esp32p4/include/soc/soc_caps.h +++ b/components/soc/esp32p4/include/soc/soc_caps.h @@ -83,6 +83,7 @@ #define SOC_LP_I2S_SUPPORTED 1 #define SOC_LP_SPI_SUPPORTED 1 #define SOC_LP_ADC_SUPPORTED 1 +#define SOC_LP_VAD_SUPPORTED 1 #define SOC_SPIRAM_SUPPORTED 1 #define SOC_PSRAM_DMA_CAPABLE 1 #define SOC_SDMMC_HOST_SUPPORTED 1 @@ -662,6 +663,9 @@ // UART has an extra TX_WAIT_SEND state when the FIFO is not empty and XOFF is enabled #define SOC_UART_SUPPORT_FSM_TX_WAIT_SEND (1) +/*-------------------------- LP_VAD CAPS -------------------------------------*/ +#define SOC_LP_I2S_SUPPORT_VAD (1) + // TODO: IDF-5679 (Copy from esp32c3, need check) /*-------------------------- COEXISTENCE HARDWARE PTI CAPS -------------------------------*/ #define SOC_COEX_HW_PTI (1) @@ -745,3 +749,4 @@ /*------------------------------------- ULP CAPS -------------------------------------*/ #define SOC_LP_CORE_SUPPORT_ETM (1) /*!< LP Core supports ETM */ #define SOC_LP_CORE_SUPPORT_LP_ADC (1) /*!< LP ADC can be accessed from the LP-Core */ +#define SOC_LP_CORE_SUPPORT_LP_VAD (1) /*!< LP VAD can be accessed from the LP-Core */ diff --git a/components/ulp/CMakeLists.txt b/components/ulp/CMakeLists.txt index 058d05922b..dbe990e652 100644 --- a/components/ulp/CMakeLists.txt +++ b/components/ulp/CMakeLists.txt @@ -78,6 +78,10 @@ if(CONFIG_ULP_COPROC_TYPE_LP_CORE) if(CONFIG_SOC_LP_ADC_SUPPORTED) list(APPEND srcs "lp_core/shared/ulp_lp_core_lp_adc_shared.c") endif() + + if(CONFIG_SOC_LP_VAD_SUPPORTED) + list(APPEND srcs "lp_core/shared/ulp_lp_core_lp_vad_shared.c") + endif() endif() idf_component_register(SRCS ${srcs} diff --git a/components/ulp/cmake/IDFULPProject.cmake b/components/ulp/cmake/IDFULPProject.cmake index 04da71b00c..fd90cfd87f 100644 --- a/components/ulp/cmake/IDFULPProject.cmake +++ b/components/ulp/cmake/IDFULPProject.cmake @@ -126,6 +126,7 @@ function(ulp_apply_default_sources ulp_app_name) "${IDF_PATH}/components/ulp/lp_core/lp_core/lp_core_spi.c" "${IDF_PATH}/components/ulp/lp_core/lp_core/lp_core_ubsan.c" "${IDF_PATH}/components/ulp/lp_core/shared/ulp_lp_core_lp_adc_shared.c" + "${IDF_PATH}/components/ulp/lp_core/shared/ulp_lp_core_lp_vad_shared.c" "${IDF_PATH}/components/ulp/lp_core/shared/ulp_lp_core_critical_section_shared.c") set(target_folder ${IDF_TARGET}) diff --git a/components/ulp/lp_core/include/ulp_lp_core.h b/components/ulp/lp_core/include/ulp_lp_core.h index d737d638ac..6a0373aefa 100644 --- a/components/ulp/lp_core/include/ulp_lp_core.h +++ b/components/ulp/lp_core/include/ulp_lp_core.h @@ -22,6 +22,7 @@ extern "C" { #define ULP_LP_CORE_WAKEUP_SOURCE_LP_IO BIT(2) // Enable wake-up by LP IO interrupt #define ULP_LP_CORE_WAKEUP_SOURCE_ETM BIT(3) // Enable wake-up by ETM event #define ULP_LP_CORE_WAKEUP_SOURCE_LP_TIMER BIT(4) // Enable wake-up by LP timer +#define ULP_LP_CORE_WAKEUP_SOURCE_LP_VAD BIT(5) // Enable wake-up by LP VAD /** * @brief ULP LP core init parameters diff --git a/components/ulp/lp_core/lp_core.c b/components/ulp/lp_core/lp_core.c index aedcf0e78e..cfc8fc81cc 100644 --- a/components/ulp/lp_core/lp_core.c +++ b/components/ulp/lp_core/lp_core.c @@ -35,7 +35,7 @@ extern uint32_t _rtc_ulp_memory_start; const static char* TAG = "ulp-lp-core"; -#define WAKEUP_SOURCE_MAX_NUMBER 5 +#define WAKEUP_SOURCE_MAX_NUMBER 6 #define RESET_HANDLER_ADDR (intptr_t)(&_rtc_ulp_memory_start + 0x80 / 4) // Placed after the 0x80 byte long vector table @@ -46,6 +46,9 @@ static uint32_t wakeup_src_sw_to_hw_flag_lookup[WAKEUP_SOURCE_MAX_NUMBER] = { LP_CORE_LL_WAKEUP_SOURCE_LP_IO, LP_CORE_LL_WAKEUP_SOURCE_ETM, LP_CORE_LL_WAKEUP_SOURCE_LP_TIMER, +#if SOC_LP_VAD_SUPPORTED + LP_CORE_LL_WAKEUP_SOURCE_LP_VAD, +#endif }; /* Convert the wake-up sources defined in ulp_lp_core.h to the actual HW wake-up source values */ 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 8b358741fb..d3e8664907 100644 --- a/components/ulp/lp_core/lp_core/lp_core_utils.c +++ b/components/ulp/lp_core/lp_core/lp_core_utils.c @@ -14,6 +14,10 @@ #include "hal/pmu_ll.h" #include "hal/uart_ll.h" #include "hal/rtc_io_ll.h" +#if SOC_LP_I2S_SUPPORT_VAD +//For VAD +#include "hal/lp_i2s_ll.h" +#endif #if SOC_LP_TIMER_SUPPORTED #include "hal/lp_timer_ll.h" @@ -56,6 +60,13 @@ void ulp_lp_core_update_wakeup_cause(void) rtcio_ll_clear_interrupt_status(); } +#if SOC_LP_VAD_SUPPORTED + if ((lp_core_ll_get_wakeup_source() & LP_CORE_LL_WAKEUP_SOURCE_LP_VAD)) { + lp_wakeup_cause |= LP_CORE_LL_WAKEUP_SOURCE_LP_VAD; + lp_i2s_ll_rx_clear_interrupt_status(&LP_I2S, LP_I2S_LL_EVENT_VAD_DONE_INT); + } +#endif + #if SOC_ETM_SUPPORTED if ((lp_core_ll_get_wakeup_source() & LP_CORE_LL_WAKEUP_SOURCE_ETM) \ && lp_core_ll_get_etm_wakeup_flag()) { diff --git a/components/ulp/lp_core/shared/include/ulp_lp_core_lp_vad_shared.h b/components/ulp/lp_core/shared/include/ulp_lp_core_lp_vad_shared.h new file mode 100644 index 0000000000..8a37dee54d --- /dev/null +++ b/components/ulp/lp_core/shared/include/ulp_lp_core_lp_vad_shared.h @@ -0,0 +1,121 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include +#include "esp_err.h" +#include "driver/lp_i2s_vad.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief LP VAD configurations + */ +typedef lp_vad_init_config_t lp_core_lp_vad_cfg_t; + +/** + * @brief State Machine + ┌──────────────────────────────────┐ + │ │ + ┌─────────────┤ speak-activity-listening-state │ ◄───────────────┐ + │ │ │ │ + │ └──────────────────────────────────┘ │ + │ ▲ │ + │ │ │ + │ │ │ + │ │ │ + │ │ │ +detected speak activity │ │ detected speak activity │ detected speak activity + >= │ │ >= │ >= +'speak_activity_thresh' │ │ 'min_speak_activity_thresh' │ 'max_speak_activity_thresh' + │ │ │ + │ │ && │ + │ │ │ + │ │ detected non-speak activity │ + │ │ < │ + │ │ 'non_speak_activity_thresh' │ + │ │ │ + │ │ │ + │ │ │ + │ │ │ + │ │ │ + │ ┌───────────┴─────────────────────┐ │ + │ │ │ │ + └───────────► │ speak-activity-detected-state ├─────────────────┘ + │ │ + └─┬───────────────────────────────┘ + │ + │ ▲ + │ │ + │ │ + │ │ detected speak activity + │ │ >= + │ │ 'min_speak_activity_thresh' + │ │ + │ │ && + │ │ + │ │ detected non-speak activity + │ │ < + └─────────────────────┘ 'non_speak_activity_thresh' +*/ + +/** + * @brief LP VAD init + * + * @param[in] vad_id VAD ID + * @param[in] init_config Initial configurations + * + * @return + * - ESP_OK: On success + * - ESP_ERR_INVALID_ARG: Invalid argument + * - ESP_ERR_INVALID_STATE: Driver state is invalid, you shouldn't call this API at this moment + */ +esp_err_t lp_core_lp_vad_init(lp_vad_t vad_id, const lp_core_lp_vad_cfg_t *init_config); + +/** + * @brief Enable LP VAD + * + * @param[in] vad_id VAD ID + * @param[in] init_config Initial configurations + * + * @return + * - ESP_OK: On success + * - ESP_ERR_INVALID_ARG: Invalid argument + * - ESP_ERR_INVALID_STATE: Driver state is invalid, you shouldn't call this API at this moment + */ +esp_err_t lp_core_lp_vad_enable(lp_vad_t vad_id); + +/** + * @brief Disable LP VAD + * + * @param[in] vad_id VAD ID + * @param[in] init_config Initial configurations + * + * @return + * - ESP_OK: On success + * - ESP_ERR_INVALID_ARG: Invalid argument + * - ESP_ERR_INVALID_STATE: Driver state is invalid, you shouldn't call this API at this moment + */ +esp_err_t lp_core_lp_vad_disable(lp_vad_t vad_id); + +/** + * @brief Deinit LP VAD + * + * @param[in] vad_id VAD ID + * + * @return + * - ESP_OK: On success + * - ESP_ERR_INVALID_ARG: Invalid argument + * - ESP_ERR_INVALID_STATE: Driver state is invalid, you shouldn't call this API at this moment + */ +esp_err_t lp_core_lp_vad_deinit(lp_vad_t vad_id); + +#ifdef __cplusplus +} +#endif diff --git a/components/ulp/lp_core/shared/ulp_lp_core_lp_vad_shared.c b/components/ulp/lp_core/shared/ulp_lp_core_lp_vad_shared.c new file mode 100644 index 0000000000..db78e74633 --- /dev/null +++ b/components/ulp/lp_core/shared/ulp_lp_core_lp_vad_shared.c @@ -0,0 +1,65 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "soc/soc_caps.h" +#if SOC_LP_VAD_SUPPORTED +#include "esp_check.h" +#include "esp_err.h" +#include "ulp_lp_core_lp_vad_shared.h" +#if SOC_LP_I2S_SUPPORT_VAD +//For VAD +#include "hal/lp_i2s_ll.h" +#include "hal/lp_i2s_hal.h" +#include "esp_private/lp_i2s_private.h" +#endif //SOC_LP_I2S_SUPPORT_VAD + +//make this available for multi vad id in future +vad_unit_handle_t s_vad_handle; + +esp_err_t lp_core_lp_vad_init(lp_vad_t vad_id, const lp_core_lp_vad_cfg_t *init_config) +{ +#if IS_ULP_COCPU + // Not supported + return ESP_ERR_NOT_SUPPORTED; +#else + esp_err_t ret = lp_i2s_vad_new_unit(vad_id, init_config, &s_vad_handle); + return ret; +#endif +} + +esp_err_t lp_core_lp_vad_enable(lp_vad_t vad_id) +{ +#if IS_ULP_COCPU + // Not supported + return ESP_ERR_NOT_SUPPORTED; +#else + esp_err_t ret = lp_i2s_vad_enable(s_vad_handle); + return ret; +#endif +} + +esp_err_t lp_core_lp_vad_disable(lp_vad_t vad_id) +{ +#if IS_ULP_COCPU + // Not supported + return ESP_ERR_NOT_SUPPORTED; +#else + esp_err_t ret = lp_i2s_vad_disable(s_vad_handle); + return ret; +#endif +} + +esp_err_t lp_core_lp_vad_deinit(lp_vad_t vad_id) +{ +#if IS_ULP_COCPU + // Not supported + return ESP_ERR_NOT_SUPPORTED; +#else + esp_err_t ret = lp_i2s_vad_del_unit(s_vad_handle); + return ret; +#endif +} +#endif /* SOC_LP_VAD_SUPPORTED */