diff --git a/components/esp_hw_support/port/esp32c6/rtc_time.c b/components/esp_hw_support/port/esp32c6/rtc_time.c index 0b894ee479..a17b4f1c79 100644 --- a/components/esp_hw_support/port/esp32c6/rtc_time.c +++ b/components/esp_hw_support/port/esp32c6/rtc_time.c @@ -218,7 +218,7 @@ uint64_t rtc_time_slowclk_to_us(uint64_t rtc_cycles, uint32_t period) uint64_t rtc_time_get(void) { - return lp_timer_hal_get_cycle_count(0); + return lp_timer_hal_get_cycle_count(); } void rtc_clk_wait_for_slow_cycle(void) //This function may not by useful any more diff --git a/components/esp_hw_support/sleep_modes.c b/components/esp_hw_support/sleep_modes.c index ca088feb69..c3b2bce7ea 100644 --- a/components/esp_hw_support/sleep_modes.c +++ b/components/esp_hw_support/sleep_modes.c @@ -871,7 +871,7 @@ esp_err_t esp_light_sleep_start(void) esp_clk_private_lock(); #if SOC_LP_TIMER_SUPPORTED - s_config.rtc_ticks_at_sleep_start = lp_timer_hal_get_cycle_count(0); + s_config.rtc_ticks_at_sleep_start = lp_timer_hal_get_cycle_count(); #else s_config.rtc_ticks_at_sleep_start = rtc_time_get(); #endif @@ -1006,7 +1006,7 @@ esp_err_t esp_light_sleep_start(void) // System timer has been stopped for the duration of the sleep, correct for that. #if SOC_LP_TIMER_SUPPORTED - uint64_t rtc_ticks_at_end = lp_timer_hal_get_cycle_count(0); + uint64_t rtc_ticks_at_end = lp_timer_hal_get_cycle_count(); #else uint64_t rtc_ticks_at_end = rtc_time_get(); #endif @@ -1591,6 +1591,12 @@ static uint32_t get_power_down_flags(void) // prevents ULP timer and touch FSMs from working correctly. s_config.domain[ESP_PD_DOMAIN_RTC_PERIPH].pd_option = ESP_PD_OPTION_OFF; } +#endif //CONFIG_IDF_TARGET_ESP32 +#if SOC_LP_CORE_SUPPORTED + else if (s_config.wakeup_triggers & RTC_LP_CORE_TRIG_EN) { + // Need to keep RTC_PERIPH on to allow lp core to wakeup during sleep (e.g. from lp timer) + s_config.domain[ESP_PD_DOMAIN_RTC_PERIPH].pd_option = ESP_PD_OPTION_ON; + } #endif //CONFIG_IDF_TARGET_ESP32 } #endif // SOC_PM_SUPPORT_RTC_PERIPH_PD diff --git a/components/hal/esp32c6/include/hal/lp_timer_hal.h b/components/hal/esp32c6/include/hal/lp_timer_hal.h index 813b865fef..6acc565f51 100644 --- a/components/hal/esp32c6/include/hal/lp_timer_hal.h +++ b/components/hal/esp32c6/include/hal/lp_timer_hal.h @@ -28,9 +28,8 @@ void lp_timer_hal_set_alarm_target(uint8_t timer_id, uint64_t value); /** * @brief get current counter value * - * @param timer_id timer num of lp_timer, 0 or 1 for esp32c6 */ -uint64_t lp_timer_hal_get_cycle_count(uint8_t timer_id); +uint64_t lp_timer_hal_get_cycle_count(void); /** * @brief clear alarm interrupt status diff --git a/components/hal/esp32c6/include/hal/lp_timer_ll.h b/components/hal/esp32c6/include/hal/lp_timer_ll.h index 7f2aa34460..a99349240b 100644 --- a/components/hal/esp32c6/include/hal/lp_timer_ll.h +++ b/components/hal/esp32c6/include/hal/lp_timer_ll.h @@ -14,6 +14,7 @@ #include "soc/lp_timer_struct.h" #include "soc/lp_aon_reg.h" #include "hal/lp_timer_types.h" +#include "esp_attr.h" #ifdef __cplusplus extern "C" { @@ -30,14 +31,14 @@ FORCE_INLINE_ATTR void lp_timer_ll_set_target_enable(lp_timer_dev_t *dev, uint8_ dev->target[timer_id].hi.enable = en; } -FORCE_INLINE_ATTR uint32_t lp_timer_ll_get_counter_value_low(lp_timer_dev_t *dev, uint8_t timer_id) +FORCE_INLINE_ATTR uint32_t lp_timer_ll_get_counter_value_low(lp_timer_dev_t *dev, uint8_t buffer_id) { - return dev->counter[timer_id].lo.counter_lo; + return dev->counter[buffer_id].lo.counter_lo; } -FORCE_INLINE_ATTR uint32_t lp_timer_ll_get_counter_value_high(lp_timer_dev_t *dev, uint8_t timer_id) +FORCE_INLINE_ATTR uint32_t lp_timer_ll_get_counter_value_high(lp_timer_dev_t *dev, uint8_t buffer_id) { - return dev->counter[timer_id].hi.counter_hi; + return dev->counter[buffer_id].hi.counter_hi; } FORCE_INLINE_ATTR void lp_timer_ll_counter_snapshot(lp_timer_dev_t *dev) @@ -55,6 +56,11 @@ FORCE_INLINE_ATTR void lp_timer_ll_clear_overflow_intr_status(lp_timer_dev_t *de dev->int_clr.overflow = 1; } +FORCE_INLINE_ATTR void lp_timer_ll_clear_lp_alarm_intr_status(lp_timer_dev_t *dev) +{ + dev->lp_int_clr.alarm = 1; +} + FORCE_INLINE_ATTR uint64_t lp_timer_ll_time_to_count(uint64_t time_in_us) { uint32_t slow_clk_value = REG_READ(LP_AON_STORE1_REG); diff --git a/components/hal/esp32c6/lp_timer_hal.c b/components/hal/esp32c6/lp_timer_hal.c index caba3725f8..7fcf95452e 100644 --- a/components/hal/esp32c6/lp_timer_hal.c +++ b/components/hal/esp32c6/lp_timer_hal.c @@ -23,11 +23,14 @@ void IRAM_ATTR lp_timer_hal_set_alarm_target(uint8_t timer_id, uint64_t value) lp_timer_ll_set_target_enable(lp_timer_context.dev, timer_id, true); } -uint64_t IRAM_ATTR lp_timer_hal_get_cycle_count(uint8_t timer_id) +uint64_t IRAM_ATTR lp_timer_hal_get_cycle_count(void) { + /* Shifts current count to buffer 0, and the value in buffer 0 to buffer 1 */ lp_timer_ll_counter_snapshot(lp_timer_context.dev); - uint32_t lo = lp_timer_ll_get_counter_value_low(lp_timer_context.dev, timer_id); - uint32_t hi = lp_timer_ll_get_counter_value_high(lp_timer_context.dev, timer_id); + + uint32_t lo = lp_timer_ll_get_counter_value_low(lp_timer_context.dev, 0); + uint32_t hi = lp_timer_ll_get_counter_value_high(lp_timer_context.dev, 0); + lp_timer_counter_value_t result = { .lo = lo, .hi = hi diff --git a/components/ulp/CMakeLists.txt b/components/ulp/CMakeLists.txt index 6b3f590bed..bf695247d4 100644 --- a/components/ulp/CMakeLists.txt +++ b/components/ulp/CMakeLists.txt @@ -40,10 +40,13 @@ if(CONFIG_ULP_COPROC_TYPE_LP_CORE) ulp_common/include/${target}) list(APPEND srcs - "lp_core/lp_core.c") + "lp_core/lp_core.c" + "lp_core/shared/ulp_lp_core_memory_shared.c" + "lp_core/shared/ulp_lp_core_lp_timer_shared.c") list(APPEND includes - "lp_core/include") + "lp_core/include" + "lp_core/shared/include") endif() idf_component_register(SRCS ${srcs} diff --git a/components/ulp/Kconfig b/components/ulp/Kconfig index 5997ea5ae6..2bd1e83be0 100644 --- a/components/ulp/Kconfig +++ b/components/ulp/Kconfig @@ -51,5 +51,12 @@ menu "Ultra Low Power (ULP) Co-processor" recommend to increase the value above 19200. endmenu + config ULP_SHARED_MEM + depends on ULP_COPROC_TYPE_LP_CORE + hex + default 0x8 + help + Size of the shared memory defined in ulp_lp_core_memory_shared.c. + Size should be kept in-sync with the size of the struct defined there. endmenu # Ultra Low Power (ULP) Co-processor diff --git a/components/ulp/cmake/CMakeLists.txt b/components/ulp/cmake/CMakeLists.txt index 74fe03c6d0..0e59300887 100644 --- a/components/ulp/cmake/CMakeLists.txt +++ b/components/ulp/cmake/CMakeLists.txt @@ -80,6 +80,8 @@ 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/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" "${IDF_PATH}/components/ulp/lp_core/lp_core/lp_core_utils.c") @@ -87,8 +89,11 @@ elseif(ULP_COCPU_IS_LP_CORE) target_link_options(${ULP_APP_NAME} PRIVATE "-Wl,--no-warn-rwx-segments") target_link_options(${ULP_APP_NAME} PRIVATE -Wl,--gc-sections) target_link_options(${ULP_APP_NAME} PRIVATE -Wl,-Map=${CMAKE_CURRENT_BINARY_DIR}/${ULP_APP_NAME}.map) + target_link_options(${ULP_APP_NAME} + PRIVATE SHELL:-T ${IDF_PATH}/components/soc/${IDF_TARGET}/ld/${IDF_TARGET}.peripherals.ld) target_sources(${ULP_APP_NAME} PRIVATE ${ULP_S_SOURCES}) - target_include_directories(${ULP_APP_NAME} PRIVATE "${IDF_PATH}/components/ulp/lp_core/lp_core/include") + target_include_directories(${ULP_APP_NAME} PRIVATE "${IDF_PATH}/components/ulp/lp_core/lp_core/include" + "${IDF_PATH}/components/ulp/lp_core/shared/include") else() foreach(ulp_s_source ${ULP_S_SOURCES}) diff --git a/components/ulp/ld/lp_core_riscv.ld b/components/ulp/ld/lp_core_riscv.ld index ab288c633e..ee4371fe33 100644 --- a/components/ulp/ld/lp_core_riscv.ld +++ b/components/ulp/ld/lp_core_riscv.ld @@ -11,7 +11,7 @@ MEMORY { /*first 128byte for exception/interrupt vectors*/ vector_table(RX) : ORIGIN = 0x50000000, LENGTH = 0x80 - ram(RWX) : ORIGIN = 0x50000080, LENGTH = CONFIG_ULP_COPROC_RESERVE_MEM - 0x80 + ram(RWX) : ORIGIN = 0x50000080, LENGTH = CONFIG_ULP_COPROC_RESERVE_MEM - 0x80 - CONFIG_ULP_SHARED_MEM } SECTIONS diff --git a/components/ulp/lp_core/include/ulp_lp_core.h b/components/ulp/lp_core/include/ulp_lp_core.h index 07e7efa198..9204d8ba99 100644 --- a/components/ulp/lp_core/include/ulp_lp_core.h +++ b/components/ulp/lp_core/include/ulp_lp_core.h @@ -16,16 +16,19 @@ extern "C" { #endif -typedef enum { - ULP_LP_CORE_WAKEUP_SOURCE_HP_CPU, // LP core is started by HP core (1 single wakeup) -} ulp_lp_core_wakeup_source_t; +#define ULP_LP_CORE_WAKEUP_SOURCE_HP_CPU BIT(0) // Started by HP core (1 single wakeup) +#define ULP_LP_CORE_WAKEUP_SOURCE_LP_UART BIT(1) // Enable wake-up by a certain number of LP UART RX pulses +#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 /** * @brief ULP LP core init parameters * */ typedef struct { - ulp_lp_core_wakeup_source_t wakeup_source; + uint32_t wakeup_source; // Wakeup source flags + uint32_t lp_timer_sleep_duration_us; // Sleep duration when ULP_LP_CORE_WAKEUP_SOURCE_LP_TIMER is specified. } ulp_lp_core_cfg_t; /** @@ -47,6 +50,12 @@ esp_err_t ulp_lp_core_run(ulp_lp_core_cfg_t* cfg); */ esp_err_t ulp_lp_core_load_binary(const uint8_t* program_binary, size_t program_size_bytes); +/** + * @brief Puts the ulp to sleep and disables all wakeup sources. + * To restart the ULP call ulp_lp_core_run() with the desired + * wake up trigger. + */ +void ulp_lp_core_stop(void); #ifdef __cplusplus } diff --git a/components/ulp/lp_core/lp_core.c b/components/ulp/lp_core/lp_core.c index 38f7a1339a..8513880bb9 100644 --- a/components/ulp/lp_core/lp_core.c +++ b/components/ulp/lp_core/lp_core.c @@ -12,11 +12,20 @@ #include "hal/misc.h" #include "ulp_common.h" #include "ulp_lp_core.h" +#include "ulp_lp_core_memory_shared.h" +#include "ulp_lp_core_lp_timer_shared.h" const static char* TAG = "ulp-lp-core"; esp_err_t ulp_lp_core_run(ulp_lp_core_cfg_t* cfg) { + if (!cfg->wakeup_source) { + ESP_LOGE(TAG, "No valid wakeup source specified"); + return ESP_ERR_INVALID_ARG; + } + + ulp_lp_core_memory_shared_cfg_t* shared_mem = ulp_lp_core_memory_shared_cfg_get(); + /* Enable LP-Core */ REG_CLR_BIT(LP_AON_LPCORE_REG, LP_AON_LPCORE_DISABLE); @@ -24,19 +33,38 @@ esp_err_t ulp_lp_core_run(ulp_lp_core_cfg_t* cfg) REG_CLR_BIT(LP_AON_LPBUS_REG, LP_AON_FAST_MEM_MUX_SEL); REG_SET_BIT(LP_AON_LPBUS_REG, LP_AON_FAST_MEM_MUX_SEL_UPDATE); - REG_SET_FIELD(PMU_LP_CPU_PWR1_REG, PMU_LP_CPU_WAKEUP_EN, 1); + /* Enable stall at sleep request*/ + REG_SET_FIELD(PMU_LP_CPU_PWR0_REG, PMU_LP_CPU_SLP_STALL_EN, 1); + + /* Enable reset after wake-up */ + REG_SET_BIT(PMU_LP_CPU_PWR0_REG, PMU_LP_CPU_SLP_RESET_EN); + + /* Set wake-up sources */ + REG_SET_FIELD(PMU_LP_CPU_PWR1_REG, PMU_LP_CPU_WAKEUP_EN, cfg->wakeup_source); /* Enable JTAG debugging */ REG_CLR_BIT(LPPERI_CPU_REG, LPPERI_LPCORE_DBGM_UNAVALIABLE); - switch(cfg->wakeup_source) { - case ULP_LP_CORE_WAKEUP_SOURCE_HP_CPU: - REG_SET_FIELD(PMU_HP_LP_CPU_COMM_REG, PMU_HP_TRIGGER_LP, 1); - break; - default: - ESP_LOGE(TAG, "No valid wakeup source specified"); - break; + if (cfg->wakeup_source & ULP_LP_CORE_WAKEUP_SOURCE_HP_CPU) { + REG_SET_FIELD(PMU_HP_LP_CPU_COMM_REG, PMU_HP_TRIGGER_LP, 1); + } + + if (cfg->wakeup_source & ULP_LP_CORE_WAKEUP_SOURCE_LP_TIMER) { + if (!cfg->lp_timer_sleep_duration_us) { + ESP_LOGI(TAG, "LP timer specified as wakeup source, but no sleep duration set. ULP will only wake-up once unless it calls ulp_lp_core_lp_timer_set_wakeup_time()"); } + shared_mem->sleep_duration_us = cfg->lp_timer_sleep_duration_us; + + /* Set first wakeup alarm */ + ulp_lp_core_lp_timer_set_wakeup_time(cfg->lp_timer_sleep_duration_us); + } + + if (cfg->wakeup_source & (ULP_LP_CORE_WAKEUP_SOURCE_LP_UART | ULP_LP_CORE_WAKEUP_SOURCE_LP_IO | ULP_LP_CORE_WAKEUP_SOURCE_ETM )) { + ESP_LOGE(TAG, "Wake-up source not yet supported"); + return ESP_ERR_INVALID_ARG; + } + + return ESP_OK; } @@ -50,8 +78,7 @@ esp_err_t ulp_lp_core_load_binary(const uint8_t* program_binary, size_t program_ } /* Turn off LP CPU before loading binary */ - REG_SET_FIELD(PMU_LP_CPU_PWR1_REG, PMU_LP_CPU_WAKEUP_EN, 0); - REG_SET_FIELD(PMU_LP_CPU_PWR1_REG, PMU_LP_CPU_SLEEP_REQ, 1); + ulp_lp_core_stop(); uint8_t* base = (uint8_t*) RTC_SLOW_MEM; @@ -61,3 +88,11 @@ esp_err_t ulp_lp_core_load_binary(const uint8_t* program_binary, size_t program_ return ESP_OK; } + + +void ulp_lp_core_stop(void) +{ + /* Disable wake-up source and put lp core to sleep */ + REG_SET_FIELD(PMU_LP_CPU_PWR1_REG, PMU_LP_CPU_WAKEUP_EN, 0); + REG_SET_FIELD(PMU_LP_CPU_PWR1_REG, PMU_LP_CPU_SLEEP_REQ, 1); +} diff --git a/components/ulp/lp_core/include/ulp_lp_core_utils.h b/components/ulp/lp_core/lp_core/include/ulp_lp_core_utils.h similarity index 60% rename from components/ulp/lp_core/include/ulp_lp_core_utils.h rename to components/ulp/lp_core/lp_core/include/ulp_lp_core_utils.h index e1ba5c6f2a..00e93b40e5 100644 --- a/components/ulp/lp_core/include/ulp_lp_core_utils.h +++ b/components/ulp/lp_core/lp_core/include/ulp_lp_core_utils.h @@ -31,6 +31,23 @@ void ulp_lp_core_wakeup_main_processor(void); */ void ulp_lp_core_delay_us(uint32_t us); + +/** + * @brief Finishes the ULP program and powers down the ULP + * until next wakeup. + * + * @note This function does not return. After called it will + * fully reset the ULP. + * + * @note The program will automatically call this function when + * returning from main(). + * + * @note To stop the ULP from waking up, call ulp_lp_core_lp_timer_disable() + * before halting. + * + */ +__attribute__((noreturn)) void ulp_lp_core_halt(void); + #ifdef __cplusplus } #endif diff --git a/components/ulp/lp_core/lp_core/lp_core_startup.c b/components/ulp/lp_core/lp_core/lp_core_startup.c index fc8d3d6d7c..e094848e5a 100644 --- a/components/ulp/lp_core/lp_core/lp_core_startup.c +++ b/components/ulp/lp_core/lp_core/lp_core_startup.c @@ -3,10 +3,25 @@ * * SPDX-License-Identifier: Apache-2.0 */ +#include "ulp_lp_core_utils.h" +#include "ulp_lp_core_lp_timer_shared.h" +#include "ulp_lp_core_memory_shared.h" + + extern void main(); + /* Initialize lp core related system functions before calling user's main*/ void lp_core_startup() { main(); + + ulp_lp_core_memory_shared_cfg_t* shared_mem = ulp_lp_core_memory_shared_cfg_get(); + uint64_t sleep_duration = shared_mem->sleep_duration_us; + + if (sleep_duration) { + ulp_lp_core_lp_timer_set_wakeup_time(sleep_duration); + } + + ulp_lp_core_halt(); } 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 c5d753bdc6..47a206a246 100644 --- a/components/ulp/lp_core/lp_core/lp_core_utils.c +++ b/components/ulp/lp_core/lp_core/lp_core_utils.c @@ -41,3 +41,10 @@ void ulp_lp_core_delay_us(uint32_t us) /* nothing to do */ } } + +void ulp_lp_core_halt(void) +{ + REG_SET_FIELD(PMU_LP_CPU_PWR1_REG, PMU_LP_CPU_SLEEP_REQ, 1); + + while(1); +} diff --git a/components/ulp/lp_core/shared/include/ulp_lp_core_lp_timer_shared.h b/components/ulp/lp_core/shared/include/ulp_lp_core_lp_timer_shared.h new file mode 100644 index 0000000000..522965ff83 --- /dev/null +++ b/components/ulp/lp_core/shared/include/ulp_lp_core_lp_timer_shared.h @@ -0,0 +1,36 @@ +/* + * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Sets the next wakeup alarm + * + * @note This only sets the alarm for a single wakeup. For periodic wakeups you will + * have to call this function again after each wakeup to configure the next time. + * + * @note If ulp_lp_core_cfg_t.lp_timer_sleep_duration_us is set the ulp will automatically set + * the next wakeup time after returning from main and override this value. + * + * @param sleep_duration_us Time to next wakeup in microseconds + */ +void ulp_lp_core_lp_timer_set_wakeup_time(uint64_t sleep_duration_us); + + +/** + * @brief Disables the lp timer alarm and clears any pending alarm interrupts + * + */ +void ulp_lp_core_lp_timer_disable(void); + +#ifdef __cplusplus +} +#endif diff --git a/components/ulp/lp_core/shared/include/ulp_lp_core_memory_shared.h b/components/ulp/lp_core/shared/include/ulp_lp_core_memory_shared.h new file mode 100644 index 0000000000..c76f4d8e2b --- /dev/null +++ b/components/ulp/lp_core/shared/include/ulp_lp_core_memory_shared.h @@ -0,0 +1,28 @@ +/* + * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + uint64_t sleep_duration_us; /* Configured sleep duration for periodic wakeup, if set the ulp will automatically schedule the next wakeup */ +} ulp_lp_core_memory_shared_cfg_t; + +/** + * @brief Returns a pointer to a struct shared between the main cpu and lp core, + * intended for sharing variables between the ulp component and ulp binary + * + * @return ulp_lp_core_memory_shared_cfg_t* Pointer to the shared config struct + */ +ulp_lp_core_memory_shared_cfg_t* ulp_lp_core_memory_shared_cfg_get(void); + +#ifdef __cplusplus +} +#endif diff --git a/components/ulp/lp_core/shared/ulp_lp_core_lp_timer_shared.c b/components/ulp/lp_core/shared/ulp_lp_core_lp_timer_shared.c new file mode 100644 index 0000000000..a555aa96da --- /dev/null +++ b/components/ulp/lp_core/shared/ulp_lp_core_lp_timer_shared.c @@ -0,0 +1,52 @@ +/* + * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include "ulp_lp_core_lp_timer_shared.h" +#include "hal/lp_timer_ll.h" + +#define TIMER_ID 1 + +static struct { + lp_timer_dev_t *dev; +} lp_timer_context = { .dev = &LP_TIMER }; + +static void lp_timer_hal_set_alarm_target(uint64_t value) +{ + lp_timer_ll_clear_lp_alarm_intr_status(lp_timer_context.dev); + lp_timer_ll_set_alarm_target(lp_timer_context.dev, TIMER_ID, value); + lp_timer_ll_set_target_enable(lp_timer_context.dev, TIMER_ID, true); +} + +static uint64_t lp_timer_hal_get_cycle_count(void) +{ + lp_timer_ll_counter_snapshot(lp_timer_context.dev); + + uint32_t lo = lp_timer_ll_get_counter_value_low(lp_timer_context.dev, 0); + uint32_t hi = lp_timer_ll_get_counter_value_high(lp_timer_context.dev, 0); + + + lp_timer_counter_value_t result = { + .lo = lo, + .hi = hi + }; + + return result.val; +} + + +void ulp_lp_core_lp_timer_set_wakeup_time(uint64_t sleep_duration_us) +{ + uint64_t cycle_cnt = lp_timer_hal_get_cycle_count(); + uint64_t alarm_target = cycle_cnt + lp_timer_ll_time_to_count(sleep_duration_us); + + lp_timer_hal_set_alarm_target(alarm_target); +} + + +void ulp_lp_core_lp_timer_disable(void) +{ + lp_timer_ll_set_target_enable(lp_timer_context.dev, TIMER_ID, false); + lp_timer_ll_clear_lp_alarm_intr_status(lp_timer_context.dev); +} diff --git a/components/ulp/lp_core/shared/ulp_lp_core_memory_shared.c b/components/ulp/lp_core/shared/ulp_lp_core_memory_shared.c new file mode 100644 index 0000000000..d58fe62508 --- /dev/null +++ b/components/ulp/lp_core/shared/ulp_lp_core_memory_shared.c @@ -0,0 +1,28 @@ +/* + * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include "ulp_lp_core_memory_shared.h" + +#include "sdkconfig.h" +#include "soc/soc.h" +#include "esp_assert.h" + + +/* The last CONFIG_ULP_SHARED_MEM bytes of the reserved memory are reserved for a shared cfg struct + The main cpu app and the ulp binary can share variables automatically through the linkerscript generated from + esp32ulp_mapgen.py, but this is not available when compiling the ULP library. + + For those special cases, e.g. config settings. We can use this shared area. + */ +#define LP_CORE_SHARED_MEM_ADDR (SOC_RTC_DRAM_LOW + CONFIG_ULP_COPROC_RESERVE_MEM - CONFIG_ULP_SHARED_MEM) + +static ulp_lp_core_memory_shared_cfg_t *const s_shared_mem = (ulp_lp_core_memory_shared_cfg_t *)LP_CORE_SHARED_MEM_ADDR; + +ESP_STATIC_ASSERT(CONFIG_ULP_SHARED_MEM == sizeof(ulp_lp_core_memory_shared_cfg_t)); + +ulp_lp_core_memory_shared_cfg_t* ulp_lp_core_memory_shared_cfg_get(void) +{ + return s_shared_mem; +} diff --git a/components/ulp/test_apps/lp_core/main/CMakeLists.txt b/components/ulp/test_apps/lp_core/main/CMakeLists.txt index 177c57e546..35cac1c9ec 100644 --- a/components/ulp/test_apps/lp_core/main/CMakeLists.txt +++ b/components/ulp/test_apps/lp_core/main/CMakeLists.txt @@ -1,11 +1,14 @@ set(app_sources "test_app_main.c" "test_lp_core.c") -set(lp_core_sources "lp_core/test_main.c") +set(lp_core_sources "lp_core/test_main.c") +set(lp_core_sources_counter "lp_core/test_main_counter.c") +set(lp_core_sources_set_timer_wakeup "lp_core/test_main_set_timer_wakeup.c") idf_component_register(SRCS ${app_sources} INCLUDE_DIRS "lp_core" REQUIRES ulp unity esp_timer WHOLE_ARCHIVE) -set(lp_core_app_name lp_core_test_app) set(lp_core_exp_dep_srcs ${app_sources}) -ulp_embed_binary(${lp_core_app_name} "${lp_core_sources}" "${lp_core_exp_dep_srcs}") +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_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.c b/components/ulp/test_apps/lp_core/main/lp_core/test_main.c index 16f869e56e..2a21cad981 100644 --- a/components/ulp/test_apps/lp_core/main/lp_core/test_main.c +++ b/components/ulp/test_apps/lp_core/main/lp_core/test_main.c @@ -17,13 +17,11 @@ volatile lp_core_test_command_reply_t main_cpu_reply = LP_CORE_COMMAND_INVALID; volatile lp_core_test_commands_t command_resp = LP_CORE_NO_COMMAND; volatile uint32_t test_data_in = 0; volatile uint32_t test_data_out = 0; -volatile uint32_t counter = 0; volatile uint32_t incrementer = 0; void handle_commands(lp_core_test_commands_t cmd) { - counter++; switch (cmd) { case LP_CORE_READ_WRITE_TEST: @@ -40,7 +38,6 @@ void handle_commands(lp_core_test_commands_t cmd) break; case LP_CORE_DELAY_TEST: - counter++; /* Echo the command ID back to the main CPU */ command_resp = LP_CORE_DELAY_TEST; diff --git a/components/ulp/test_apps/lp_core/main/lp_core/test_main_counter.c b/components/ulp/test_apps/lp_core/main/lp_core/test_main_counter.c new file mode 100644 index 0000000000..405d9730e6 --- /dev/null +++ b/components/ulp/test_apps/lp_core/main/lp_core/test_main_counter.c @@ -0,0 +1,24 @@ +/* + * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include "ulp_lp_core_utils.h" + +volatile uint32_t counter; +volatile uint32_t counter_wakeup_limit; + +int main (void) +{ + counter++; + + if (counter_wakeup_limit && (counter > counter_wakeup_limit)) { + counter = 0; + + ulp_lp_core_wakeup_main_processor(); + } + + return 0; +} diff --git a/components/ulp/test_apps/lp_core/main/lp_core/test_main_set_timer_wakeup.c b/components/ulp/test_apps/lp_core/main/lp_core/test_main_set_timer_wakeup.c new file mode 100644 index 0000000000..e46124d136 --- /dev/null +++ b/components/ulp/test_apps/lp_core/main/lp_core/test_main_set_timer_wakeup.c @@ -0,0 +1,23 @@ +/* + * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include "ulp_lp_core_utils.h" +#include "ulp_lp_core_lp_timer_shared.h" + +volatile uint32_t set_timer_wakeup_counter; +volatile uint32_t WAKEUP_PERIOD_BASE_US = 100000; + +int main (void) +{ + set_timer_wakeup_counter++; + + /* Alternate between WAKEUP_PERIOD_BASE_US and 2*WAKEUP_PERIOD_BASE_US to let the main CPU see that + the wake-up time can be reconfigured */ + ulp_lp_core_lp_timer_set_wakeup_time( ((set_timer_wakeup_counter % 2) + 1)*WAKEUP_PERIOD_BASE_US); + + 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 88b6b004b2..d6412cbd28 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 @@ -6,40 +6,47 @@ #include #include +#include #include "lp_core_test_app.h" +#include "lp_core_test_app_counter.h" +#include "lp_core_test_app_set_timer_wakeup.h" #include "ulp_lp_core.h" +#include "ulp_lp_core_lp_timer_shared.h" #include "test_shared.h" #include "unity.h" #include "esp_sleep.h" #include "esp_timer.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.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"); -static bool firmware_loaded = false; -static void load_and_start_lp_core_firmware(void) +extern const uint8_t lp_core_main_counter_bin_start[] asm("_binary_lp_core_test_app_counter_bin_start"); +extern const uint8_t lp_core_main_counter_bin_end[] asm("_binary_lp_core_test_app_counter_bin_end"); + +extern const uint8_t lp_core_main_set_timer_wakeup_bin_start[] asm("_binary_lp_core_test_app_set_timer_wakeup_bin_start"); +extern const uint8_t lp_core_main_set_timer_wakeup_bin_end[] asm("_binary_lp_core_test_app_set_timer_wakeup_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) { - if (!firmware_loaded) { + TEST_ASSERT(ulp_lp_core_load_binary(firmware_start, + (firmware_end - firmware_start)) == ESP_OK); - ulp_lp_core_cfg_t cfg = { - .wakeup_source = ULP_LP_CORE_WAKEUP_SOURCE_HP_CPU, - }; + TEST_ASSERT(ulp_lp_core_run(cfg) == ESP_OK); - TEST_ASSERT(ulp_lp_core_load_binary(lp_core_main_bin_start, - (lp_core_main_bin_end - lp_core_main_bin_start)) == ESP_OK); - - TEST_ASSERT(ulp_lp_core_run(&cfg) == ESP_OK); - - firmware_loaded = true; - } } TEST_CASE("LP core and main CPU are able to exchange data", "[lp_core]") { const uint32_t test_data = 0x12345678; - /* Load ULP RISC-V firmware and start the coprocessor */ - load_and_start_lp_core_firmware(); + /* 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_bin_start, lp_core_main_bin_end); /* Setup test data */ ulp_test_data_in = test_data ^ XOR_MASK; @@ -70,8 +77,12 @@ TEST_CASE("Test LP core delay", "[lp_core]") const uint32_t delay_period_us = 5000000; const uint32_t delta_us = 500000; // RTC FAST is not very accurate - /* Load ULP RISC-V firmware and start the coprocessor */ - load_and_start_lp_core_firmware(); + /* 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_bin_start, lp_core_main_bin_end); /* Setup test data */ ulp_test_data_in = delay_period_us; @@ -100,8 +111,12 @@ TEST_CASE("Test LP core delay", "[lp_core]") static void do_ulp_wakeup_deepsleep(lp_core_test_commands_t ulp_cmd) { - /* Load ULP RISC-V firmware and start the ULP RISC-V Coprocessor */ - load_and_start_lp_core_firmware(); + /* Load ULP firmware and start the ULP RISC-V 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_bin_start, lp_core_main_bin_end); /* Setup wakeup triggers */ TEST_ASSERT(esp_sleep_enable_ulp_wakeup() == ESP_OK); @@ -138,3 +153,134 @@ TEST_CASE_MULTIPLE_STAGES("LP-core is able to wakeup main CPU from deep sleep af TEST_CASE_MULTIPLE_STAGES("LP-core is able to wakeup main CPU from deep sleep after a long delay", "[ulp]", do_ulp_wakeup_after_long_delay_deepsleep, check_reset_reason_ulp_wakeup); + + +#define LP_TIMER_TEST_DURATION_S (5) +#define LP_TIMER_TEST_SLEEP_DURATION_US (20000) + +TEST_CASE("LP Timer can wakeup lp core periodically", "[lp_core]") +{ + int64_t start, test_duration; + /* Load ULP firmware and start the coprocessor */ + ulp_lp_core_cfg_t cfg = { + .wakeup_source = ULP_LP_CORE_WAKEUP_SOURCE_LP_TIMER, + .lp_timer_sleep_duration_us = LP_TIMER_TEST_SLEEP_DURATION_US, + }; + + load_and_start_lp_core_firmware(&cfg, lp_core_main_counter_bin_start, lp_core_main_counter_bin_end); + + start = esp_timer_get_time(); + vTaskDelay(pdMS_TO_TICKS(LP_TIMER_TEST_DURATION_S*1000)); + + test_duration = esp_timer_get_time() - start; + uint32_t expected_run_count = test_duration / LP_TIMER_TEST_SLEEP_DURATION_US; + printf("LP core ran %"PRIu32" times in %"PRIi64" ms, expected it to run approx %"PRIu32" times\n", ulp_counter, test_duration/1000, expected_run_count); + + TEST_ASSERT_INT_WITHIN_MESSAGE(5, expected_run_count, ulp_counter, "LP Core did not wake up the expected number of times"); +} + +RTC_FAST_ATTR static struct timeval tv_start; + +#define ULP_COUNTER_WAKEUP_LIMIT_CNT 50 + +static void do_ulp_wakeup_with_lp_timer_deepsleep(void) +{ + /* Load ULP firmware and start the coprocessor */ + ulp_lp_core_cfg_t cfg = { + .wakeup_source = ULP_LP_CORE_WAKEUP_SOURCE_LP_TIMER, + .lp_timer_sleep_duration_us = LP_TIMER_TEST_SLEEP_DURATION_US, + }; + + load_and_start_lp_core_firmware(&cfg, lp_core_main_counter_bin_start, lp_core_main_counter_bin_end); + ulp_counter_wakeup_limit = ULP_COUNTER_WAKEUP_LIMIT_CNT; + + gettimeofday(&tv_start, NULL); + + /* Setup wakeup triggers */ + TEST_ASSERT(esp_sleep_enable_ulp_wakeup() == ESP_OK); + + /* Enter Deep Sleep */ + esp_deep_sleep_start(); + + UNITY_TEST_FAIL(__LINE__, "Should not get here!"); +} + +static void check_reset_reason_and_sleep_duration(void) +{ + struct timeval tv_stop = {}; + gettimeofday(&tv_stop, NULL); + + TEST_ASSERT_EQUAL(ESP_SLEEP_WAKEUP_ULP, esp_sleep_get_wakeup_cause()); + + + int64_t sleep_duration = (tv_stop.tv_sec - tv_start.tv_sec)*1000 + (tv_stop.tv_usec - tv_start.tv_usec)/1000; + int64_t expected_sleep_duration_ms = ulp_counter_wakeup_limit * LP_TIMER_TEST_SLEEP_DURATION_US/1000; + + printf("CPU slept for %"PRIi64" ms, expected it to sleep approx %"PRIi64" ms\n", sleep_duration, expected_sleep_duration_ms); + /* Rough estimate, as CPU spends quite some time waking up, but will test if lp core is waking up way too often etc */ + TEST_ASSERT_INT_WITHIN_MESSAGE(1000, expected_sleep_duration_ms, sleep_duration, "LP Core did not wake up the expected number of times"); +} + +TEST_CASE_MULTIPLE_STAGES("LP Timer can wakeup lp core periodically during deep sleep", "[ulp]", + do_ulp_wakeup_with_lp_timer_deepsleep, + check_reset_reason_and_sleep_duration); + +static bool ulp_is_running(uint32_t *counter_variable) +{ + uint32_t start_cnt = *counter_variable; + + /* Wait a few ULP wakeup cycles to ensure ULP has run */ + vTaskDelay((5 * LP_TIMER_TEST_SLEEP_DURATION_US / 1000) / portTICK_PERIOD_MS); + + uint32_t end_cnt = *counter_variable; + printf("start run count: %" PRIu32 ", end run count %" PRIu32 "\n", start_cnt, end_cnt); + + /* If the ulp is running the counter should have been incremented */ + return (start_cnt != end_cnt); +} + +#define STOP_TEST_ITERATIONS 10 + +TEST_CASE("LP core can be stopped and and started again from main CPU", "[ulp]") +{ + /* Load ULP firmware and start the coprocessor */ + ulp_lp_core_cfg_t cfg = { + .wakeup_source = ULP_LP_CORE_WAKEUP_SOURCE_LP_TIMER, + .lp_timer_sleep_duration_us = LP_TIMER_TEST_SLEEP_DURATION_US, + }; + + load_and_start_lp_core_firmware(&cfg, lp_core_main_counter_bin_start, lp_core_main_counter_bin_end); + + TEST_ASSERT(ulp_is_running(&ulp_counter)); + + for (int i = 0; i < STOP_TEST_ITERATIONS; i++) { + ulp_lp_core_stop(); + TEST_ASSERT(!ulp_is_running(&ulp_counter)); + + ulp_lp_core_run(&cfg); + TEST_ASSERT(ulp_is_running(&ulp_counter)); + } +} + +TEST_CASE("LP core can schedule next wake-up time by itself", "[ulp]") +{ + int64_t start, test_duration; + + /* Load ULP firmware and start the coprocessor */ + ulp_lp_core_cfg_t cfg = { + .wakeup_source = ULP_LP_CORE_WAKEUP_SOURCE_LP_TIMER, + }; + + load_and_start_lp_core_firmware(&cfg, lp_core_main_set_timer_wakeup_bin_start, lp_core_main_set_timer_wakeup_bin_end); + + start = esp_timer_get_time(); + vTaskDelay(pdMS_TO_TICKS(LP_TIMER_TEST_DURATION_S*1000)); + + test_duration = esp_timer_get_time() - start; + /* ULP will alternative between setting WAKEUP_PERIOD_BASE_US and 2*WAKEUP_PERIOD_BASE_US + as a wakeup period which should give an average wakeup time of 1.5*WAKEUP_PERIOD_BASE_US */ + uint32_t expected_run_count = test_duration / (1.5*ulp_WAKEUP_PERIOD_BASE_US); + printf("LP core ran %"PRIu32" times in %"PRIi64" ms, expected it to run approx %"PRIu32" times\n", ulp_set_timer_wakeup_counter, test_duration/1000, expected_run_count); + + TEST_ASSERT_INT_WITHIN_MESSAGE(5, expected_run_count, ulp_set_timer_wakeup_counter, "LP Core did not wake up the expected number of times"); +}