mirror of
https://github.com/espressif/esp-idf.git
synced 2025-07-31 19:24:33 +02:00
light sleep: dfs support for esp32c3
This commit is contained in:
@@ -1,11 +1,3 @@
|
||||
idf_build_get_property(target IDF_TARGET)
|
||||
if(${target} STREQUAL "esp32c3")
|
||||
# TODO ESP32-C3 IDF-2107 - include the headers to avoid compile errors, no functions available to link...
|
||||
idf_component_register(SRCS "pm_impl_riscv_temp.c" INCLUDE_DIRS include)
|
||||
return()
|
||||
endif()
|
||||
|
||||
|
||||
idf_component_register(SRCS "pm_locks.c" "pm_trace.c" "pm_impl.c"
|
||||
INCLUDE_DIRS include
|
||||
LDFRAGMENTS linker.lf)
|
||||
|
@@ -12,8 +12,31 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
/* TODO ESP32-C3 Placeholder until IDF-2107 when this file can be dropped */
|
||||
void esp_pm_impl_waiti(void)
|
||||
{
|
||||
|
||||
#pragma once
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include "esp_err.h"
|
||||
|
||||
#include "soc/rtc.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
|
||||
/**
|
||||
* @brief Power management config for ESP32C3
|
||||
*
|
||||
* Pass a pointer to this structure as an argument to esp_pm_configure function.
|
||||
*/
|
||||
typedef struct {
|
||||
int max_freq_mhz; /*!< Maximum CPU frequency, in MHz */
|
||||
int min_freq_mhz; /*!< Minimum CPU frequency to use when no locks are taken, in MHz */
|
||||
bool light_sleep_enable; /*!< Enter light sleep when no locks are taken */
|
||||
} esp_pm_config_esp32c3_t;
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
@@ -23,6 +23,8 @@
|
||||
#include "esp32s2/pm.h"
|
||||
#elif CONFIG_IDF_TARGET_ESP32S3
|
||||
#include "esp32s3/pm.h"
|
||||
#elif CONFIG_IDF_TARGET_ESP32C3
|
||||
#include "esp32c3/pm.h"
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
@@ -31,8 +31,10 @@
|
||||
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#if __XTENSA__
|
||||
#include "freertos/xtensa_timer.h"
|
||||
#include "xtensa/core-macros.h"
|
||||
#endif
|
||||
|
||||
#include "esp_private/pm_impl.h"
|
||||
#include "esp_private/pm_trace.h"
|
||||
@@ -54,10 +56,15 @@
|
||||
#elif CONFIG_IDF_TARGET_ESP32S3
|
||||
#include "esp32s3/clk.h"
|
||||
#include "esp32s3/pm.h"
|
||||
#elif CONFIG_IDF_TARGET_ESP32C3
|
||||
#include "esp32c3/clk.h"
|
||||
#include "esp32c3/pm.h"
|
||||
#include "driver/gpio.h"
|
||||
#endif
|
||||
|
||||
#define MHZ (1000000)
|
||||
|
||||
#if __XTENSA__
|
||||
/* CCOMPARE update timeout, in CPU cycles. Any value above ~600 cycles will work
|
||||
* for the purpose of detecting a deadlock.
|
||||
*/
|
||||
@@ -67,6 +74,7 @@
|
||||
* than this. This is to prevent setting CCOMPARE below CCOUNT.
|
||||
*/
|
||||
#define CCOMPARE_MIN_CYCLES_IN_FUTURE 1000
|
||||
#endif
|
||||
|
||||
/* When light sleep is used, wake this number of microseconds earlier than
|
||||
* the next tick.
|
||||
@@ -85,6 +93,9 @@
|
||||
/* Minimal divider at which REF_CLK_FREQ can be obtained */
|
||||
#define REF_CLK_DIV_MIN 2
|
||||
#define DEFAULT_CPU_FREQ CONFIG_ESP32S3_DEFAULT_CPU_FREQ_MHZ
|
||||
#elif CONFIG_IDF_TARGET_ESP32C3
|
||||
#define REF_CLK_DIV_MIN 2
|
||||
#define DEFAULT_CPU_FREQ CONFIG_ESP32C3_DEFAULT_CPU_FREQ_MHZ
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_PM_PROFILING
|
||||
@@ -104,12 +115,6 @@ static size_t s_mode_lock_counts[PM_MODE_COUNT];
|
||||
/* Bit mask of locked modes. BIT(i) is set iff s_mode_lock_counts[i] > 0. */
|
||||
static uint32_t s_mode_mask;
|
||||
|
||||
/* Divider and multiplier used to adjust (ccompare - ccount) duration.
|
||||
* Only set to non-zero values when switch is in progress.
|
||||
*/
|
||||
static uint32_t s_ccount_div;
|
||||
static uint32_t s_ccount_mul;
|
||||
|
||||
#if CONFIG_FREERTOS_USE_TICKLESS_IDLE
|
||||
|
||||
#define PERIPH_SKIP_LIGHT_SLEEP_NO 1
|
||||
@@ -133,11 +138,6 @@ static bool s_skip_light_sleep[portNUM_PROCESSORS];
|
||||
#endif // portNUM_PROCESSORS == 2
|
||||
#endif // CONFIG_FREERTOS_USE_TICKLESS_IDLE
|
||||
|
||||
/* Indicates to the ISR hook that CCOMPARE needs to be updated on the given CPU.
|
||||
* Used in conjunction with cross-core interrupt to update CCOMPARE on the other CPU.
|
||||
*/
|
||||
static volatile bool s_need_update_ccompare[portNUM_PROCESSORS];
|
||||
|
||||
/* A flag indicating that Idle hook has run on a given CPU;
|
||||
* Next interrupt on the same CPU will take s_rtos_lock_handle.
|
||||
*/
|
||||
@@ -177,9 +177,23 @@ static const char* s_mode_names[] = {
|
||||
};
|
||||
#endif // WITH_PROFILING
|
||||
|
||||
static const char* TAG = "pm_" CONFIG_IDF_TARGET;
|
||||
#if __XTENSA__
|
||||
/* Indicates to the ISR hook that CCOMPARE needs to be updated on the given CPU.
|
||||
* Used in conjunction with cross-core interrupt to update CCOMPARE on the other CPU.
|
||||
*/
|
||||
static volatile bool s_need_update_ccompare[portNUM_PROCESSORS];
|
||||
|
||||
/* Divider and multiplier used to adjust (ccompare - ccount) duration.
|
||||
* Only set to non-zero values when switch is in progress.
|
||||
*/
|
||||
static uint32_t s_ccount_div;
|
||||
static uint32_t s_ccount_mul;
|
||||
|
||||
static void update_ccompare(void);
|
||||
#endif // __XTENSA__
|
||||
|
||||
static const char* TAG = "pm";
|
||||
|
||||
static void do_switch(pm_mode_t new_mode);
|
||||
static void leave_idle(void);
|
||||
static void on_freq_update(uint32_t old_ticks_per_us, uint32_t ticks_per_us);
|
||||
@@ -211,6 +225,8 @@ esp_err_t esp_pm_configure(const void* vconfig)
|
||||
const esp_pm_config_esp32s2_t* config = (const esp_pm_config_esp32s2_t*) vconfig;
|
||||
#elif CONFIG_IDF_TARGET_ESP32S3
|
||||
const esp_pm_config_esp32s3_t* config = (const esp_pm_config_esp32s3_t*) vconfig;
|
||||
#elif CONFIG_IDF_TARGET_ESP32C3
|
||||
const esp_pm_config_esp32c3_t* config = (const esp_pm_config_esp32c3_t*) vconfig;
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_FREERTOS_USE_TICKLESS_IDLE
|
||||
@@ -256,7 +272,7 @@ esp_err_t esp_pm_configure(const void* vconfig)
|
||||
*/
|
||||
apb_max_freq = 80;
|
||||
}
|
||||
#elif CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3
|
||||
#else
|
||||
int apb_max_freq = MIN(max_freq_mhz, 80); /* CPU frequency in APB_MAX mode */
|
||||
#endif
|
||||
|
||||
@@ -357,8 +373,11 @@ static void IRAM_ATTR on_freq_update(uint32_t old_ticks_per_us, uint32_t ticks_p
|
||||
esp_timer_private_update_apb_freq(apb_ticks_per_us);
|
||||
}
|
||||
|
||||
#if __XTENSA__
|
||||
#if XT_RTOS_TIMER_INT
|
||||
/* Calculate new tick divisor */
|
||||
_xt_tick_divisor = ticks_per_us * MHZ / XT_TICK_PER_SEC;
|
||||
#endif
|
||||
|
||||
int core_id = xPortGetCoreID();
|
||||
if (s_rtos_lock_handle[core_id] != NULL) {
|
||||
@@ -391,6 +410,7 @@ static void IRAM_ATTR on_freq_update(uint32_t old_ticks_per_us, uint32_t ticks_p
|
||||
s_ccount_div = 0;
|
||||
ESP_PM_TRACE_EXIT(CCOMPARE_UPDATE, core_id);
|
||||
}
|
||||
#endif // __XTENSA__
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -412,9 +432,11 @@ static void IRAM_ATTR do_switch(pm_mode_t new_mode)
|
||||
portEXIT_CRITICAL_ISR(&s_switch_lock);
|
||||
return;
|
||||
}
|
||||
#if __XTENSA__
|
||||
if (s_need_update_ccompare[core_id]) {
|
||||
s_need_update_ccompare[core_id] = false;
|
||||
}
|
||||
#endif
|
||||
portEXIT_CRITICAL_ISR(&s_switch_lock);
|
||||
} while (true);
|
||||
s_new_mode = new_mode;
|
||||
@@ -455,6 +477,7 @@ static void IRAM_ATTR do_switch(pm_mode_t new_mode)
|
||||
portEXIT_CRITICAL_ISR(&s_switch_lock);
|
||||
}
|
||||
|
||||
#if __XTENSA__
|
||||
/**
|
||||
* @brief Calculate new CCOMPARE value based on s_ccount_{mul,div}
|
||||
*
|
||||
@@ -475,6 +498,7 @@ static void IRAM_ATTR update_ccompare(void)
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif // __XTENSA__
|
||||
|
||||
static void IRAM_ATTR leave_idle(void)
|
||||
{
|
||||
@@ -578,6 +602,7 @@ void IRAM_ATTR vApplicationSleep( TickType_t xExpectedIdleTime )
|
||||
/* Adjust RTOS tick count based on the amount of time spent in sleep */
|
||||
vTaskStepTick(slept_ticks);
|
||||
|
||||
#if __XTENSA__
|
||||
/* Trigger tick interrupt, since sleep time was longer
|
||||
* than portTICK_PERIOD_MS. Note that setting INTSET does not
|
||||
* work for timer interrupt, and changing CCOMPARE would clear
|
||||
@@ -587,6 +612,7 @@ void IRAM_ATTR vApplicationSleep( TickType_t xExpectedIdleTime )
|
||||
while (!(XTHAL_GET_INTERRUPT() & BIT(XT_TIMER_INTNUM))) {
|
||||
;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
other_core_should_skip_light_sleep(core_id);
|
||||
}
|
||||
@@ -676,6 +702,8 @@ void esp_pm_impl_init(void)
|
||||
esp_pm_config_esp32s2_t cfg = {
|
||||
#elif CONFIG_IDF_TARGET_ESP32S3
|
||||
esp_pm_config_esp32s3_t cfg = {
|
||||
#elif CONFIG_IDF_TARGET_ESP32C3
|
||||
esp_pm_config_esp32c3_t cfg = {
|
||||
#endif
|
||||
.max_freq_mhz = DEFAULT_CPU_FREQ,
|
||||
.min_freq_mhz = xtal_freq,
|
||||
@@ -709,7 +737,7 @@ void IRAM_ATTR esp_pm_impl_isr_hook(void)
|
||||
* from happening in this section, since they will also call into esp_pm_impl_isr_hook.
|
||||
*/
|
||||
uint32_t state = portENTER_CRITICAL_NESTED();
|
||||
#if portNUM_PROCESSORS == 2
|
||||
#if __XTENSA__ && (portNUM_PROCESSORS == 2)
|
||||
if (s_need_update_ccompare[core_id]) {
|
||||
update_ccompare();
|
||||
s_need_update_ccompare[core_id] = false;
|
||||
@@ -728,7 +756,7 @@ void esp_pm_impl_waiti(void)
|
||||
#if CONFIG_FREERTOS_USE_TICKLESS_IDLE
|
||||
int core_id = xPortGetCoreID();
|
||||
if (s_skipped_light_sleep[core_id]) {
|
||||
asm("waiti 0");
|
||||
cpu_hal_waiti();
|
||||
/* Interrupt took the CPU out of waiti and s_rtos_lock_handle[core_id]
|
||||
* is now taken. However since we are back to idle task, we can release
|
||||
* the lock so that vApplicationSleep can attempt to enter light sleep.
|
||||
@@ -737,7 +765,7 @@ void esp_pm_impl_waiti(void)
|
||||
s_skipped_light_sleep[core_id] = false;
|
||||
}
|
||||
#else
|
||||
asm("waiti 0");
|
||||
cpu_hal_waiti();
|
||||
#endif // CONFIG_FREERTOS_USE_TICKLESS_IDLE
|
||||
}
|
||||
|
||||
|
@@ -21,12 +21,21 @@
|
||||
* Feel free to change when debugging.
|
||||
*/
|
||||
static const int DRAM_ATTR s_trace_io[] = {
|
||||
#ifndef CONFIG_IDF_TARGET_ESP32C3
|
||||
BIT(4), BIT(5), // ESP_PM_TRACE_IDLE
|
||||
BIT(16), BIT(17), // ESP_PM_TRACE_TICK
|
||||
BIT(18), BIT(18), // ESP_PM_TRACE_FREQ_SWITCH
|
||||
BIT(19), BIT(19), // ESP_PM_TRACE_CCOMPARE_UPDATE
|
||||
BIT(25), BIT(26), // ESP_PM_TRACE_ISR_HOOK
|
||||
BIT(27), BIT(27), // ESP_PM_TRACE_SLEEP
|
||||
#else
|
||||
BIT(2), BIT(3), // ESP_PM_TRACE_IDLE
|
||||
BIT(4), BIT(5), // ESP_PM_TRACE_TICK
|
||||
BIT(6), BIT(6), // ESP_PM_TRACE_FREQ_SWITCH
|
||||
BIT(7), BIT(7), // ESP_PM_TRACE_CCOMPARE_UPDATE
|
||||
BIT(8), BIT(9), // ESP_PM_TRACE_ISR_HOOK
|
||||
BIT(18), BIT(18), // ESP_PM_TRACE_SLEEP
|
||||
#endif
|
||||
};
|
||||
|
||||
void esp_pm_trace_init(void)
|
||||
|
@@ -96,6 +96,7 @@
|
||||
#include "esp_attr.h"
|
||||
#include "esp_debug_helpers.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_private/pm_trace.h"
|
||||
|
||||
/**
|
||||
* @brief A variable is used to keep track of the critical section nesting.
|
||||
@@ -215,6 +216,10 @@ IRAM_ATTR void vPortSysTickHandler(void *arg)
|
||||
|
||||
systimer_ll_clear_alarm_int(SYSTIMER_ALARM_0);
|
||||
|
||||
#ifdef CONFIG_PM_TRACE
|
||||
ESP_PM_TRACE_ENTER(TICK, xPortGetCoreID());
|
||||
#endif
|
||||
|
||||
if (!uxSchedulerRunning) {
|
||||
return;
|
||||
}
|
||||
@@ -222,6 +227,10 @@ IRAM_ATTR void vPortSysTickHandler(void *arg)
|
||||
if (xTaskIncrementTick() != pdFALSE) {
|
||||
vPortYieldFromISR();
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_TRACE
|
||||
ESP_PM_TRACE_EXIT(TICK, xPortGetCoreID());
|
||||
#endif
|
||||
}
|
||||
|
||||
BaseType_t xPortStartScheduler(void)
|
||||
|
@@ -178,6 +178,11 @@ static inline void cpu_ll_set_vecbase(const void* vecbase)
|
||||
asm volatile ("wsr %0, vecbase" :: "r" (vecbase));
|
||||
}
|
||||
|
||||
static inline void cpu_ll_waiti(void)
|
||||
{
|
||||
asm volatile ("waiti 0\n");
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
@@ -151,6 +151,11 @@ static inline void cpu_ll_set_vecbase(const void* vecbase)
|
||||
RV_WRITE_CSR(mtvec, vecbase_int);
|
||||
}
|
||||
|
||||
static inline void cpu_ll_waiti(void)
|
||||
{
|
||||
asm volatile ("wfi\n");
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
@@ -197,6 +197,11 @@ static inline void cpu_ll_write_dedic_gpio_mask(uint32_t mask, uint32_t value)
|
||||
asm volatile("wr_mask_gpio_out %0, %1" : : "r"(value), "r"(mask):);
|
||||
}
|
||||
|
||||
static inline void cpu_ll_waiti(void)
|
||||
{
|
||||
asm volatile ("waiti 0\n");
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
@@ -177,6 +177,11 @@ static inline void cpu_ll_set_vecbase(const void *vecbase)
|
||||
asm volatile ("wsr %0, vecbase" :: "r" (vecbase));
|
||||
}
|
||||
|
||||
static inline void cpu_ll_waiti(void)
|
||||
{
|
||||
asm volatile ("waiti 0\n");
|
||||
}
|
||||
|
||||
static inline uint32_t cpu_ll_read_dedic_gpio_in(void)
|
||||
{
|
||||
uint32_t value = 0;
|
||||
|
@@ -73,6 +73,11 @@ extern "C" {
|
||||
*/
|
||||
#define cpu_hal_break() cpu_ll_break()
|
||||
|
||||
/**
|
||||
* Wait for interrupt.
|
||||
*/
|
||||
#define cpu_hal_waiti() cpu_ll_waiti()
|
||||
|
||||
#if SOC_CPU_BREAKPOINTS_NUM > 0
|
||||
|
||||
/**
|
||||
|
@@ -14,6 +14,8 @@
|
||||
#include "soc/soc.h"
|
||||
#include "soc/interrupt_reg.h"
|
||||
#include "riscv/rvruntime-frames.h"
|
||||
#include "soc/soc_caps.h"
|
||||
#include "sdkconfig.h"
|
||||
|
||||
|
||||
.equ SAVE_REGS, 32
|
||||
@@ -243,6 +245,22 @@ _interrupt_handler:
|
||||
li t0, 0x8
|
||||
csrrs t0, mstatus, t0
|
||||
|
||||
#ifdef CONFIG_PM_TRACE
|
||||
li a0, 0 /* = ESP_PM_TRACE_IDLE */
|
||||
#if SOC_CPU_CORES_NUM == 1
|
||||
li a1, 0 /* No need to check core ID on single core hardware */
|
||||
#else
|
||||
csrr a1, mhartid
|
||||
#endif
|
||||
la t0, esp_pm_trace_exit
|
||||
jalr t0 /* absolute jump, avoid the 1 MiB range constraint */
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_PM_ENABLE
|
||||
la t0, esp_pm_impl_isr_hook
|
||||
jalr t0 /* absolute jump, avoid the 1 MiB range constraint */
|
||||
#endif
|
||||
|
||||
/* call the C dispatcher */
|
||||
mv a0, sp /* argument 1, stack pointer */
|
||||
csrr a1, mcause /* argument 2, interrupt number */
|
||||
|
@@ -43,6 +43,7 @@ menu "Example Configuration"
|
||||
choice EXAMPLE_MAX_CPU_FREQ
|
||||
prompt "Maximum CPU frequency"
|
||||
default EXAMPLE_MAX_CPU_FREQ_80
|
||||
depends on PM_ENABLE
|
||||
help
|
||||
Maximum CPU frequency to use for dynamic frequency scaling.
|
||||
|
||||
@@ -52,6 +53,7 @@ menu "Example Configuration"
|
||||
bool "160 MHz"
|
||||
config EXAMPLE_MAX_CPU_FREQ_240
|
||||
bool "240 MHz"
|
||||
depends on IDF_TARGET_ESP32 || IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32S3
|
||||
endchoice
|
||||
|
||||
config EXAMPLE_MAX_CPU_FREQ_MHZ
|
||||
@@ -64,19 +66,20 @@ menu "Example Configuration"
|
||||
choice EXAMPLE_MIN_CPU_FREQ
|
||||
prompt "Minimum CPU frequency"
|
||||
default EXAMPLE_MIN_CPU_FREQ_10M
|
||||
depends on PM_ENABLE
|
||||
help
|
||||
Minimum CPU frequency to use for dynamic frequency scaling.
|
||||
Should be set to XTAL frequency or XTAL frequency divided by integer.
|
||||
|
||||
config EXAMPLE_MIN_CPU_FREQ_40M
|
||||
bool "40 MHz (use with 40MHz XTAL)"
|
||||
depends on IDF_TARGET_ESP32S2 || ESP32_XTAL_FREQ_40 || ESP32_XTAL_FREQ_AUTO
|
||||
depends on IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32C3 || ESP32_XTAL_FREQ_40 || ESP32_XTAL_FREQ_AUTO
|
||||
config EXAMPLE_MIN_CPU_FREQ_20M
|
||||
bool "20 MHz (use with 40MHz XTAL)"
|
||||
depends on IDF_TARGET_ESP32S2 || ESP32_XTAL_FREQ_40 || ESP32_XTAL_FREQ_AUTO
|
||||
depends on IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32C3 || ESP32_XTAL_FREQ_40 || ESP32_XTAL_FREQ_AUTO
|
||||
config EXAMPLE_MIN_CPU_FREQ_10M
|
||||
bool "10 MHz (use with 40MHz XTAL)"
|
||||
depends on IDF_TARGET_ESP32S2 || ESP32_XTAL_FREQ_40 || ESP32_XTAL_FREQ_AUTO
|
||||
depends on IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32C3 || ESP32_XTAL_FREQ_40 || ESP32_XTAL_FREQ_AUTO
|
||||
config EXAMPLE_MIN_CPU_FREQ_26M
|
||||
bool "26 MHz (use with 26MHz XTAL)"
|
||||
depends on ESP32_XTAL_FREQ_26 || ESP32_XTAL_FREQ_AUTO
|
||||
|
@@ -99,6 +99,8 @@ void app_main(void)
|
||||
esp_pm_config_esp32_t pm_config = {
|
||||
#elif CONFIG_IDF_TARGET_ESP32S2
|
||||
esp_pm_config_esp32s2_t pm_config = {
|
||||
#elif CONFIG_IDF_TARGET_ESP32C3
|
||||
esp_pm_config_esp32c3_t pm_config = {
|
||||
#endif
|
||||
.max_freq_mhz = CONFIG_EXAMPLE_MAX_CPU_FREQ_MHZ,
|
||||
.min_freq_mhz = CONFIG_EXAMPLE_MIN_CPU_FREQ_MHZ,
|
||||
|
Reference in New Issue
Block a user