diff --git a/components/esp_hw_support/sleep_cpu.c b/components/esp_hw_support/sleep_cpu.c index 93e750452b..948d5a99d2 100644 --- a/components/esp_hw_support/sleep_cpu.c +++ b/components/esp_hw_support/sleep_cpu.c @@ -14,6 +14,7 @@ #include "esp_check.h" #include "esp_sleep.h" #include "esp_log.h" +#include "esp_crc.h" #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "esp_heap_caps.h" @@ -25,6 +26,12 @@ #include "hal/rtc_hal.h" #endif +#if CONFIG_PM_CHECK_SLEEP_RETENTION_FRAME +#include "esp_private/system_internal.h" +#include "hal/clk_gate_ll.h" +#include "hal/uart_hal.h" +#endif + #include "soc/rtc_periph.h" #ifdef CONFIG_IDF_TARGET_ESP32S3 @@ -557,6 +564,32 @@ static IRAM_ATTR void cpu_domain_dev_regs_restore(cpu_domain_dev_sleep_frame_t * } } +#if CONFIG_PM_CHECK_SLEEP_RETENTION_FRAME +static void update_retention_frame_crc(uint32_t *frame_ptr, uint32_t frame_check_size, uint32_t *frame_crc_ptr) +{ + *(frame_crc_ptr) = esp_crc32_le(0, (void *)frame_ptr, frame_check_size); +} + +static void validate_retention_frame_crc(uint32_t *frame_ptr, uint32_t frame_check_size, uint32_t *frame_crc_ptr) +{ + if(*(frame_crc_ptr) != esp_crc32_le(0, (void *)(frame_ptr), frame_check_size)){ + // resume uarts + for (int i = 0; i < SOC_UART_NUM; ++i) { +#ifndef CONFIG_IDF_TARGET_ESP32 + if (!periph_ll_periph_enabled(PERIPH_UART0_MODULE + i)) { + continue; + } +#endif + uart_ll_force_xon(i); + } + + /* Since it is still in the critical now, use ESP_EARLY_LOG */ + ESP_EARLY_LOGE(TAG, "Sleep retention frame is corrupted"); + esp_restart_noos(); + } +} +#endif + extern RvCoreCriticalSleepFrame * rv_core_critical_regs_save(void); extern RvCoreCriticalSleepFrame * rv_core_critical_regs_restore(void); typedef uint32_t (* sleep_cpu_entry_cb_t)(uint32_t, uint32_t, uint32_t, bool); @@ -566,9 +599,18 @@ static IRAM_ATTR esp_err_t do_cpu_retention(sleep_cpu_entry_cb_t goto_sleep, { RvCoreCriticalSleepFrame * frame = rv_core_critical_regs_save(); if ((frame->pmufunc & 0x3) == 0x1) { +#if CONFIG_PM_CHECK_SLEEP_RETENTION_FRAME + /* Minus 2 * sizeof(long) is for bypass `pmufunc` and `frame_crc` field */ + update_retention_frame_crc((uint32_t*)frame, RV_SLEEP_CTX_FRMSZ - 2 * sizeof(long), (uint32_t *)(&frame->frame_crc)); +#endif REG_WRITE(LIGHT_SLEEP_WAKE_STUB_ADDR_REG, (uint32_t)rv_core_critical_regs_restore); return (*goto_sleep)(wakeup_opt, reject_opt, lslp_mem_inf_fpu, dslp); } +#if CONFIG_PM_CHECK_SLEEP_RETENTION_FRAME + else { + validate_retention_frame_crc((uint32_t*)frame, RV_SLEEP_CTX_FRMSZ - 2 * sizeof(long), (uint32_t *)(&frame->frame_crc)); + } +#endif return ESP_OK; } @@ -588,8 +630,17 @@ esp_err_t IRAM_ATTR esp_sleep_cpu_retention(uint32_t (*goto_sleep)(uint32_t, uin cpu_domain_dev_regs_save(s_cpu_retention.retent.cache_config_frame); RvCoreNonCriticalSleepFrame *frame = rv_core_noncritical_regs_save(); +#if CONFIG_PM_CHECK_SLEEP_RETENTION_FRAME + /* Minus sizeof(long) is for bypass `frame_crc` field */ + update_retention_frame_crc((uint32_t*)frame, sizeof(RvCoreNonCriticalSleepFrame) - sizeof(long), (uint32_t *)(&frame->frame_crc)); +#endif + esp_err_t err = do_cpu_retention(goto_sleep, wakeup_opt, reject_opt, lslp_mem_inf_fpu, dslp); +#if CONFIG_PM_CHECK_SLEEP_RETENTION_FRAME + validate_retention_frame_crc((uint32_t*)frame, sizeof(RvCoreNonCriticalSleepFrame) - sizeof(long), (uint32_t *)(&frame->frame_crc)); +#endif + rv_core_noncritical_regs_restore(frame); cpu_domain_dev_regs_restore(s_cpu_retention.retent.cache_config_frame); cpu_domain_dev_regs_restore(s_cpu_retention.retent.intpri_frame); diff --git a/components/esp_hw_support/sleep_cpu_asm.S b/components/esp_hw_support/sleep_cpu_asm.S index c62756d889..3a24e917db 100644 --- a/components/esp_hw_support/sleep_cpu_asm.S +++ b/components/esp_hw_support/sleep_cpu_asm.S @@ -53,11 +53,12 @@ rv_core_critical_regs_save: sw t2, RV_SLP_CTX_T2(t0) sw s0, RV_SLP_CTX_S0(t0) sw s1, RV_SLP_CTX_S1(t0) - sw a0, RV_SLP_CTX_A0(t0) - /* !! WARNING, do not use the a0 register below, a0 carries important sleep - * information and will be returned as the return value !! */ + /* a0 is caller saved, so it does not need to be saved, but it should be the + pointer value of RvCoreCriticalSleepFrame for return. + */ mv a0, t0 + sw a0, RV_SLP_CTX_A0(t0) sw a1, RV_SLP_CTX_A1(t0) sw a2, RV_SLP_CTX_A2(t0) diff --git a/components/esp_pm/.build-test-rules.yml b/components/esp_pm/.build-test-rules.yml index 12b9c2ccaf..421533b5af 100644 --- a/components/esp_pm/.build-test-rules.yml +++ b/components/esp_pm/.build-test-rules.yml @@ -2,6 +2,6 @@ components/esp_pm/test_apps/esp_pm: disable: - - if: IDF_TARGET in ["esp32c6", "esp32h2"] + - if: IDF_TARGET in ["esp32h2"] temporary: true reason: Not supported yet diff --git a/components/esp_pm/Kconfig b/components/esp_pm/Kconfig index 0b53ea500f..dbb21c28f0 100644 --- a/components/esp_pm/Kconfig +++ b/components/esp_pm/Kconfig @@ -85,6 +85,14 @@ menu "Power Management" bool default n + config PM_CHECK_SLEEP_RETENTION_FRAME + bool + depends on PM_POWER_DOWN_CPU_IN_LIGHT_SLEEP + default n if !IDF_CI_BUILD + help + This option is invisible to users, and it is only used for ci testing, + enabling it in the application will increase the sleep and wake-up time overhead + config PM_POWER_DOWN_CPU_IN_LIGHT_SLEEP bool "Power down CPU in light sleep" depends on IDF_TARGET_ESP32C3 || IDF_TARGET_ESP32S3 || IDF_TARGET_ESP32C6 diff --git a/components/esp_pm/test_apps/esp_pm/README.md b/components/esp_pm/test_apps/esp_pm/README.md index b5be4985c5..7e7523ec85 100644 --- a/components/esp_pm/test_apps/esp_pm/README.md +++ b/components/esp_pm/test_apps/esp_pm/README.md @@ -1,2 +1,2 @@ -| Supported Targets | ESP32 | ESP32-C2 | ESP32-C3 | ESP32-S2 | ESP32-S3 | -| ----------------- | ----- | -------- | -------- | -------- | -------- | +| Supported Targets | ESP32 | ESP32-C2 | ESP32-C3 | ESP32-C6 | ESP32-S2 | ESP32-S3 | +| ----------------- | ----- | -------- | -------- | -------- | -------- | -------- | diff --git a/components/esp_pm/test_apps/esp_pm/main/test_pm.c b/components/esp_pm/test_apps/esp_pm/main/test_pm.c index af98d2c54a..7678d46d81 100644 --- a/components/esp_pm/test_apps/esp_pm/main/test_pm.c +++ b/components/esp_pm/test_apps/esp_pm/main/test_pm.c @@ -62,7 +62,7 @@ static void switch_freq(int mhz) } } -#if CONFIG_IDF_TARGET_ESP32C3 +#if CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32C6 static const int test_freqs[] = {40, CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ, 80, 40, 80, 10, 80, 20, 40}; #elif CONFIG_IDF_TARGET_ESP32C2 static const int test_freqs[] = {CONFIG_XTAL_FREQ, CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ, 80, CONFIG_XTAL_FREQ, 80, diff --git a/components/esp_pm/test_apps/esp_pm/pytest_esp_pm.py b/components/esp_pm/test_apps/esp_pm/pytest_esp_pm.py index 18f66db83a..fea31fc4b8 100644 --- a/components/esp_pm/test_apps/esp_pm/pytest_esp_pm.py +++ b/components/esp_pm/test_apps/esp_pm/pytest_esp_pm.py @@ -5,9 +5,9 @@ import pytest from pytest_embedded import Dut CONFIGS = [ - pytest.param('default', marks=[pytest.mark.supported_targets, pytest.mark.temp_skip_ci(targets=['esp32c6', 'esp32h2'], reason='c6/h2 support TBD')]), - pytest.param('limits', marks=[pytest.mark.supported_targets, pytest.mark.temp_skip_ci(targets=['esp32c6', 'esp32h2'], reason='c6/h2 support TBD')]), - pytest.param('options', marks=[pytest.mark.supported_targets, pytest.mark.temp_skip_ci(targets=['esp32c6', 'esp32h2'], reason='c6/h2 support TBD')]), + pytest.param('default', marks=[pytest.mark.supported_targets, pytest.mark.temp_skip_ci(targets=['esp32h2'], reason='h2 support TBD')]), + pytest.param('limits', marks=[pytest.mark.supported_targets, pytest.mark.temp_skip_ci(targets=['esp32h2'], reason='h2 support TBD')]), + pytest.param('options', marks=[pytest.mark.supported_targets, pytest.mark.temp_skip_ci(targets=['esp32h2'], reason='h2 support TBD')]), ] diff --git a/components/riscv/include/riscv/rvsleep-frames.h b/components/riscv/include/riscv/rvsleep-frames.h index 06b2b75d8c..929d581c62 100644 --- a/components/riscv/include/riscv/rvsleep-frames.h +++ b/components/riscv/include/riscv/rvsleep-frames.h @@ -7,6 +7,8 @@ #ifndef __RVSLEEP_FRAMES_H__ #define __RVSLEEP_FRAMES_H__ +#include "sdkconfig.h" + /* Align a value up to nearest n-byte boundary, where n is a power of 2. */ #define ALIGNUP(n, val) (((val) + (n) - 1) & -(n)) @@ -86,6 +88,9 @@ STRUCT_BEGIN * to sleep or has just been awakened. We use the * lowest 2 bits as indication infomation, 3 means * being awakened, 1 means going to sleep */ +#if CONFIG_PM_CHECK_SLEEP_RETENTION_FRAME + STRUCT_FIELD (long, 4, RV_SLP_CSF_CTX_CRC, frame_crc) /* Used to check RvCoreCriticalSleepFrame integrity */ +#endif STRUCT_END(RvCoreCriticalSleepFrame) #if defined(_ASMLANGUAGE) || defined(__ASSEMBLER__) @@ -148,6 +153,9 @@ STRUCT_BEGIN STRUCT_FIELD (long, 4, RV_SLP_CTX_UGPIO_OEN, ugpio_oen) STRUCT_FIELD (long, 4, RV_SLP_CTX_UGPIO_IN, ugpio_in) STRUCT_FIELD (long, 4, RV_SLP_CTX_UGPIO_OUT, ugpio_out) +#if CONFIG_PM_CHECK_SLEEP_RETENTION_FRAME + STRUCT_FIELD (long, 4, RV_SLP_NCSF_CTX_CRC, frame_crc) /* Used to check RvCoreNonCriticalSleepFrame integrity */ +#endif STRUCT_END(RvCoreNonCriticalSleepFrame) #endif /* #ifndef __RVSLEEP_FRAMES_H__ */