diff --git a/components/esp_pm/CMakeLists.txt b/components/esp_pm/CMakeLists.txt index f57c21df69..b802986ec1 100644 --- a/components/esp_pm/CMakeLists.txt +++ b/components/esp_pm/CMakeLists.txt @@ -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) diff --git a/components/esp_pm/pm_impl_riscv_temp.c b/components/esp_pm/include/esp32c3/pm.h similarity index 51% rename from components/esp_pm/pm_impl_riscv_temp.c rename to components/esp_pm/include/esp32c3/pm.h index 59c94b50d8..7ceecd12c1 100644 --- a/components/esp_pm/pm_impl_riscv_temp.c +++ b/components/esp_pm/include/esp32c3/pm.h @@ -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 +#include +#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 diff --git a/components/esp_pm/include/esp_pm.h b/components/esp_pm/include/esp_pm.h index d15a904bee..4d5146316b 100644 --- a/components/esp_pm/include/esp_pm.h +++ b/components/esp_pm/include/esp_pm.h @@ -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 diff --git a/components/esp_pm/pm_impl.c b/components/esp_pm/pm_impl.c index 81caf32c0c..b2b8a2effc 100644 --- a/components/esp_pm/pm_impl.c +++ b/components/esp_pm/pm_impl.c @@ -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 } diff --git a/components/esp_pm/pm_trace.c b/components/esp_pm/pm_trace.c index 5cdb666d67..291897aa11 100644 --- a/components/esp_pm/pm_trace.c +++ b/components/esp_pm/pm_trace.c @@ -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) diff --git a/components/freertos/port/riscv/port.c b/components/freertos/port/riscv/port.c index 95d5ace0a4..6c6ce2b583 100644 --- a/components/freertos/port/riscv/port.c +++ b/components/freertos/port/riscv/port.c @@ -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) diff --git a/components/hal/esp32/include/hal/cpu_ll.h b/components/hal/esp32/include/hal/cpu_ll.h index f93a5e9355..7a83ddbc43 100644 --- a/components/hal/esp32/include/hal/cpu_ll.h +++ b/components/hal/esp32/include/hal/cpu_ll.h @@ -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 diff --git a/components/hal/esp32c3/include/hal/cpu_ll.h b/components/hal/esp32c3/include/hal/cpu_ll.h index fb8d9b32bc..1ac07fdab0 100644 --- a/components/hal/esp32c3/include/hal/cpu_ll.h +++ b/components/hal/esp32c3/include/hal/cpu_ll.h @@ -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 diff --git a/components/hal/esp32s2/include/hal/cpu_ll.h b/components/hal/esp32s2/include/hal/cpu_ll.h index d52d4489ad..c70955eba2 100644 --- a/components/hal/esp32s2/include/hal/cpu_ll.h +++ b/components/hal/esp32s2/include/hal/cpu_ll.h @@ -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 diff --git a/components/hal/esp32s3/include/hal/cpu_ll.h b/components/hal/esp32s3/include/hal/cpu_ll.h index 6753f60954..ea50870f93 100644 --- a/components/hal/esp32s3/include/hal/cpu_ll.h +++ b/components/hal/esp32s3/include/hal/cpu_ll.h @@ -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; diff --git a/components/hal/include/hal/cpu_hal.h b/components/hal/include/hal/cpu_hal.h index a930771187..e6b7c62d2f 100644 --- a/components/hal/include/hal/cpu_hal.h +++ b/components/hal/include/hal/cpu_hal.h @@ -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 /** diff --git a/components/riscv/vectors.S b/components/riscv/vectors.S index 6580517fa8..a0ba847a15 100644 --- a/components/riscv/vectors.S +++ b/components/riscv/vectors.S @@ -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 */ diff --git a/examples/wifi/power_save/main/Kconfig.projbuild b/examples/wifi/power_save/main/Kconfig.projbuild index 0d733fbadf..dce8517002 100644 --- a/examples/wifi/power_save/main/Kconfig.projbuild +++ b/examples/wifi/power_save/main/Kconfig.projbuild @@ -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 diff --git a/examples/wifi/power_save/main/power_save.c b/examples/wifi/power_save/main/power_save.c index 6e6203b9d6..ccb7889f19 100644 --- a/examples/wifi/power_save/main/power_save.c +++ b/examples/wifi/power_save/main/power_save.c @@ -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,