mirror of
https://github.com/espressif/esp-idf.git
synced 2024-10-05 20:47:46 -04:00
Merge branch 'feature/c6_ulp_timer' into 'master'
ulp: lp timer support for lp core Closes IDF-6956, IDF-6830, and IDF-6835 See merge request espressif/esp-idf!23453
This commit is contained in:
commit
099ffe6243
@ -227,7 +227,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
|
||||
|
@ -859,7 +859,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
|
||||
@ -1004,7 +1004,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
|
||||
@ -1589,6 +1589,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
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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}
|
||||
|
@ -64,5 +64,12 @@ menu "Ultra Low Power (ULP) Co-processor"
|
||||
is invoked on.
|
||||
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
|
||||
|
@ -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})
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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
|
@ -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();
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -0,0 +1,36 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#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
|
@ -0,0 +1,28 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#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
|
52
components/ulp/lp_core/shared/ulp_lp_core_lp_timer_shared.c
Normal file
52
components/ulp/lp_core/shared/ulp_lp_core_lp_timer_shared.c
Normal file
@ -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);
|
||||
}
|
28
components/ulp/lp_core/shared/ulp_lp_core_memory_shared.c
Normal file
28
components/ulp/lp_core/shared/ulp_lp_core_memory_shared.c
Normal file
@ -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;
|
||||
}
|
@ -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}")
|
||||
|
@ -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;
|
||||
|
||||
|
@ -0,0 +1,24 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#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;
|
||||
}
|
@ -0,0 +1,23 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#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;
|
||||
}
|
@ -6,40 +6,47 @@
|
||||
|
||||
#include <stdio.h>
|
||||
#include <inttypes.h>
|
||||
#include <sys/time.h>
|
||||
#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");
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user