Skip to content

Commit

Permalink
Merge branch 'feature/c6_ulp_timer' into 'master'
Browse files Browse the repository at this point in the history
ulp: lp timer support for lp core

Closes IDF-6956, IDF-6830, and IDF-6835

See merge request espressif/esp-idf!23453
  • Loading branch information
Zim Kalinowski committed May 9, 2023
2 parents 54576b7 + 3ced7b1 commit 099ffe6
Show file tree
Hide file tree
Showing 23 changed files with 503 additions and 54 deletions.
2 changes: 1 addition & 1 deletion components/esp_hw_support/port/esp32c6/rtc_time.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
10 changes: 8 additions & 2 deletions components/esp_hw_support/sleep_modes.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
3 changes: 1 addition & 2 deletions components/hal/esp32c6/include/hal/lp_timer_hal.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
14 changes: 10 additions & 4 deletions components/hal/esp32c6/include/hal/lp_timer_ll.h
Original file line number Diff line number Diff line change
Expand Up @@ -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" {
Expand All @@ -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)
Expand All @@ -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);
Expand Down
9 changes: 6 additions & 3 deletions components/hal/esp32c6/lp_timer_hal.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
7 changes: 5 additions & 2 deletions components/ulp/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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}
Expand Down
7 changes: 7 additions & 0 deletions components/ulp/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -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
7 changes: 6 additions & 1 deletion components/ulp/cmake/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -80,15 +80,20 @@ 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")

target_link_options(${ULP_APP_NAME} PRIVATE "-nostartfiles")
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})
Expand Down
2 changes: 1 addition & 1 deletion components/ulp/ld/lp_core_riscv.ld
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
17 changes: 13 additions & 4 deletions components/ulp/lp_core/include/ulp_lp_core.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;

/**
Expand All @@ -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
}
Expand Down
55 changes: 45 additions & 10 deletions components/ulp/lp_core/lp_core.c
Original file line number Diff line number Diff line change
Expand Up @@ -12,31 +12,59 @@
#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);

/* Allow LP core to access LP memory during sleep */
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;
}

Expand All @@ -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;

Expand All @@ -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);
}
Original file line number Diff line number Diff line change
Expand Up @@ -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
15 changes: 15 additions & 0 deletions components/ulp/lp_core/lp_core/lp_core_startup.c
Original file line number Diff line number Diff line change
Expand Up @@ -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();
}
7 changes: 7 additions & 0 deletions components/ulp/lp_core/lp_core/lp_core_utils.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Original file line number Diff line number Diff line change
@@ -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
Loading

0 comments on commit 099ffe6

Please sign in to comment.