diff --git a/components/esp_hw_support/include/esp_private/sleep_retention.h b/components/esp_hw_support/include/esp_private/sleep_retention.h index 076373c8f4..5b4e95e338 100644 --- a/components/esp_hw_support/include/esp_private/sleep_retention.h +++ b/components/esp_hw_support/include/esp_private/sleep_retention.h @@ -59,6 +59,11 @@ typedef enum { */ esp_err_t sleep_retention_entries_create(const sleep_retention_entries_config_t retent[], int num, regdma_link_priority_t priority, sleep_retention_module_t module); +/** + * @brief Dump the initialization status of all modules. +*/ +void sleep_retention_dump_modules(FILE *out); + /** * @brief Dump all runtime sleep retention linked lists */ @@ -139,6 +144,23 @@ esp_err_t sleep_retention_module_allocate(sleep_retention_module_t module); */ esp_err_t sleep_retention_module_free(sleep_retention_module_t module); +/** + * @brief Force take the power lock so that during sleep the power domain won't be powered off. + * + * @return + * - ESP_OK if success + * - other value when the internal `sleep_retention_module_init` fails. +*/ +esp_err_t sleep_retention_power_lock_acquire(void); + +/** + * @brief Release the power lock so that the peripherals' power domain can be powered off. + * Please note that there is an internal reference counter and the power domain will be kept on until same number + * of `sleep_retention_power_lock_release` is called as `sleep_retention_power_lock_acquire`. + * @return always ESP_OK +*/ +esp_err_t sleep_retention_power_lock_release(void); + /** * @brief Get all initialized modules that require sleep retention * diff --git a/components/esp_hw_support/sleep_retention.c b/components/esp_hw_support/sleep_retention.c index e437a6fe58..7e01204a6e 100644 --- a/components/esp_hw_support/sleep_retention.c +++ b/components/esp_hw_support/sleep_retention.c @@ -29,6 +29,8 @@ #endif static __attribute__((unused)) const char *TAG = "sleep"; +static int acquire_cnt; //for the force acquire lock + struct sleep_retention_module_object { sleep_retention_module_callbacks_t cbs; /* A callback list that can extend more sleep retention event callbacks */ @@ -319,6 +321,23 @@ static void sleep_retention_entries_stats(void) _lock_release_recursive(&s_retention.lock); } +void sleep_retention_dump_modules(FILE *out) +{ + uint32_t inited_modules = sleep_retention_get_inited_modules(); + uint32_t created_modules = sleep_retention_get_created_modules(); + for (int i = SLEEP_RETENTION_MODULE_MIN; i <= SLEEP_RETENTION_MODULE_MAX; i++) { + bool inited = (inited_modules & BIT(i)) != 0; + bool created = (created_modules & BIT(i)) != 0; + bool is_top = (TOP_DOMAIN_PERIPHERALS_BM & BIT(i)) != 0; + + const char* status = !inited? "-": + created? "CREATED": + "INITED"; + const char* domain = is_top? "TOP": "-"; + fprintf(out, "%2d: %4s %8s\n", i, domain, status); + } +} + void sleep_retention_dump_entries(FILE *out) { _lock_acquire_recursive(&s_retention.lock); @@ -820,6 +839,42 @@ esp_err_t sleep_retention_module_free(sleep_retention_module_t module) return err; } +static esp_err_t empty_create(void *args) +{ + return ESP_OK; +} + +esp_err_t sleep_retention_power_lock_acquire(void) +{ + _lock_acquire_recursive(&s_retention.lock); + if (acquire_cnt == 0) { + sleep_retention_module_init_param_t init_param = { + .cbs = { .create = {.handle = empty_create},}, + }; + esp_err_t ret = sleep_retention_module_init(SLEEP_RETENTION_MODULE_NULL, &init_param); + if (ret != ESP_OK) { + _lock_release_recursive(&s_retention.lock); + return ret; + } + } + acquire_cnt++; + _lock_release_recursive(&s_retention.lock); + return ESP_OK; +} + +esp_err_t sleep_retention_power_lock_release(void) +{ + esp_err_t ret = ESP_OK; + _lock_acquire_recursive(&s_retention.lock); + acquire_cnt--; + assert(acquire_cnt >= 0); + if (acquire_cnt == 0) { + ret = sleep_retention_module_deinit(SLEEP_RETENTION_MODULE_NULL); + } + _lock_release_recursive(&s_retention.lock); + return ret; +} + void IRAM_ATTR sleep_retention_do_extra_retention(bool backup_or_restore) { if (s_retention.highpri < SLEEP_RETENTION_REGDMA_LINK_HIGHEST_PRIORITY || diff --git a/components/esp_hw_support/test_apps/.build-test-rules.yml b/components/esp_hw_support/test_apps/.build-test-rules.yml index a13010cf3a..0585429eba 100644 --- a/components/esp_hw_support/test_apps/.build-test-rules.yml +++ b/components/esp_hw_support/test_apps/.build-test-rules.yml @@ -42,6 +42,11 @@ components/esp_hw_support/test_apps/rtc_power_modes: temporary: true reason: the other targets are not tested yet +components/esp_hw_support/test_apps/sleep_retention: + enable: + - if: SOC_PAU_SUPPORTED == 1 and CONFIG_NAME != "xip_psram" + - if: SOC_PAU_SUPPORTED == 1 and (SOC_SPIRAM_XIP_SUPPORTED == 1 and CONFIG_NAME == "xip_psram") + components/esp_hw_support/test_apps/vad_wakeup: disable: - if: SOC_LP_VAD_SUPPORTED != 1 diff --git a/components/esp_hw_support/test_apps/sleep_retention/CMakeLists.txt b/components/esp_hw_support/test_apps/sleep_retention/CMakeLists.txt new file mode 100644 index 0000000000..47e7d817bd --- /dev/null +++ b/components/esp_hw_support/test_apps/sleep_retention/CMakeLists.txt @@ -0,0 +1,10 @@ +# This is the project CMakeLists.txt file for the test subproject +cmake_minimum_required(VERSION 3.16) + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) + +# "Trim" the build. Include the minimal set of components, main, and anything it depends on. We also depend on esp_psram +# as we set CONFIG_SPIRAM_... options. +set(COMPONENTS main esp_psram) + +project(test_retention) diff --git a/components/esp_hw_support/test_apps/sleep_retention/README.md b/components/esp_hw_support/test_apps/sleep_retention/README.md new file mode 100644 index 0000000000..77eda1867c --- /dev/null +++ b/components/esp_hw_support/test_apps/sleep_retention/README.md @@ -0,0 +1,2 @@ +| Supported Targets | ESP32-C5 | ESP32-C6 | ESP32-C61 | ESP32-H2 | ESP32-P4 | +| ----------------- | -------- | -------- | --------- | -------- | -------- | diff --git a/components/esp_hw_support/test_apps/sleep_retention/main/CMakeLists.txt b/components/esp_hw_support/test_apps/sleep_retention/main/CMakeLists.txt new file mode 100644 index 0000000000..d285f1b552 --- /dev/null +++ b/components/esp_hw_support/test_apps/sleep_retention/main/CMakeLists.txt @@ -0,0 +1,7 @@ +set(srcs "test_app_main.c" "test_retention.c") + +# In order for the cases defined by `TEST_CASE` to be linked into the final elf, +# the component can be registered as WHOLE_ARCHIVE +idf_component_register(SRCS ${srcs} + PRIV_REQUIRES unity esp_mm esp_psram + WHOLE_ARCHIVE) diff --git a/components/esp_hw_support/test_apps/sleep_retention/main/test_app_main.c b/components/esp_hw_support/test_apps/sleep_retention/main/test_app_main.c new file mode 100644 index 0000000000..027d2b69c0 --- /dev/null +++ b/components/esp_hw_support/test_apps/sleep_retention/main/test_app_main.c @@ -0,0 +1,40 @@ +/* + * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "unity.h" +#include "unity_test_runner.h" +#include "esp_heap_caps.h" + +#define TEST_MEMORY_LEAK_THRESHOLD (-300) + +static size_t before_free_8bit; +static size_t before_free_32bit; + +static void check_leak(size_t before_free, size_t after_free, const char *type) +{ + ssize_t delta = after_free - before_free; + printf("MALLOC_CAP_%s: Before %u bytes free, After %u bytes free (delta %d)\n", type, before_free, after_free, delta); + TEST_ASSERT_MESSAGE(delta >= TEST_MEMORY_LEAK_THRESHOLD, "memory leak"); +} + +void setUp(void) +{ + before_free_8bit = heap_caps_get_free_size(MALLOC_CAP_8BIT); + before_free_32bit = heap_caps_get_free_size(MALLOC_CAP_32BIT); +} + +void tearDown(void) +{ + size_t after_free_8bit = heap_caps_get_free_size(MALLOC_CAP_8BIT); + size_t after_free_32bit = heap_caps_get_free_size(MALLOC_CAP_32BIT); + check_leak(before_free_8bit, after_free_8bit, "8BIT"); + check_leak(before_free_32bit, after_free_32bit, "32BIT"); +} + +void app_main(void) +{ + unity_run_menu(); +} diff --git a/components/esp_hw_support/test_apps/sleep_retention/main/test_retention.c b/components/esp_hw_support/test_apps/sleep_retention/main/test_retention.c new file mode 100644 index 0000000000..72a4e5b3e8 --- /dev/null +++ b/components/esp_hw_support/test_apps/sleep_retention/main/test_retention.c @@ -0,0 +1,37 @@ +/* + * SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "esp_log.h" +#include "sdkconfig.h" +#include "unity.h" +#include "esp_private/sleep_sys_periph.h" +#include "esp_private/sleep_retention.h" +#include "esp_sleep.h" +#include "esp_private/sleep_cpu.h" + +const char TAG[] = "retention"; + +TEST_CASE("retention: can go to retention", "[retention]") +{ + // Prepare a TOP PD sleep + TEST_ESP_OK(esp_sleep_enable_timer_wakeup(1 * 1000 * 1000)); + sleep_cpu_configure(true); + + TEST_ASSERT_EQUAL_INT32(true, peripheral_domain_pd_allowed()); + sleep_retention_dump_modules(stdout); + vTaskDelay(1000/portTICK_PERIOD_MS); + + ESP_LOGI(TAG, "Going to sleep..."); + esp_light_sleep_start(); + + ESP_LOGI(TAG, "After wakeup"); + TEST_ASSERT_EQUAL_INT32(true, peripheral_domain_pd_allowed()); + sleep_retention_dump_modules(stdout); + + sleep_cpu_configure(false); +} diff --git a/components/esp_hw_support/test_apps/sleep_retention/pytest_retention.py b/components/esp_hw_support/test_apps/sleep_retention/pytest_retention.py new file mode 100644 index 0000000000..3a4ca50f1e --- /dev/null +++ b/components/esp_hw_support/test_apps/sleep_retention/pytest_retention.py @@ -0,0 +1,35 @@ +# SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD +# SPDX-License-Identifier: CC0-1.0 +import functools +from typing import Callable +from typing import Dict +from typing import List + +import pytest +from pytest_embedded import Dut + + +def target_list(targets: List[str]) -> Callable: + + def decorator(func: Callable) -> Callable: + + @functools.wraps(func) + def wrapper(*args: List, **kwargs: Dict) -> Callable: + return func(*args, **kwargs) # type: ignore + + for target in targets: + wrapper = pytest.mark.__getattr__(target)(wrapper) + + return wrapper + + return decorator + + +# SOC_PAU_SUPPORTED == 1 +retention_targets = ['esp32c6', 'esp32h2', 'esp32p4', 'esp32c5', 'esp32c61'] + + +@target_list(retention_targets) +@pytest.mark.generic +def test_sleep_retention(dut: Dut) -> None: + dut.run_all_single_board_cases() diff --git a/components/esp_hw_support/test_apps/sleep_retention/sdkconfig.ci.defaults b/components/esp_hw_support/test_apps/sleep_retention/sdkconfig.ci.defaults new file mode 100644 index 0000000000..e69de29bb2 diff --git a/components/esp_hw_support/test_apps/sleep_retention/sdkconfig.ci.xip_psram b/components/esp_hw_support/test_apps/sleep_retention/sdkconfig.ci.xip_psram new file mode 100644 index 0000000000..358f51460b --- /dev/null +++ b/components/esp_hw_support/test_apps/sleep_retention/sdkconfig.ci.xip_psram @@ -0,0 +1,2 @@ +CONFIG_SPIRAM=y +CONFIG_SPIRAM_XIP_FROM_PSRAM=y diff --git a/components/esp_hw_support/test_apps/sleep_retention/sdkconfig.defaults b/components/esp_hw_support/test_apps/sleep_retention/sdkconfig.defaults new file mode 100644 index 0000000000..6cefbfaaf5 --- /dev/null +++ b/components/esp_hw_support/test_apps/sleep_retention/sdkconfig.defaults @@ -0,0 +1,6 @@ +CONFIG_FREERTOS_HZ=1000 +CONFIG_ESP_TASK_WDT=n + +CONFIG_PM_ENABLE=y +CONFIG_PM_POWER_DOWN_CPU_IN_LIGHT_SLEEP=y +CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP=y diff --git a/components/soc/esp32c5/include/soc/retention_periph_defs.h b/components/soc/esp32c5/include/soc/retention_periph_defs.h index f65bfde521..6d7b045e0b 100644 --- a/components/soc/esp32c5/include/soc/retention_periph_defs.h +++ b/components/soc/esp32c5/include/soc/retention_periph_defs.h @@ -15,6 +15,7 @@ extern "C" { typedef enum periph_retention_module { SLEEP_RETENTION_MODULE_MIN = 0, + SLEEP_RETENTION_MODULE_NULL = SLEEP_RETENTION_MODULE_MIN, /* This module is for all peripherals that can't survive from PD_TOP to call init only. Shouldn't have any dependency. */ /* clock module, which includes system and modem */ SLEEP_RETENTION_MODULE_CLOCK_SYSTEM = 1, SLEEP_RETENTION_MODULE_CLOCK_MODEM = 2, @@ -54,6 +55,8 @@ typedef enum periph_retention_module { } periph_retention_module_t; typedef enum periph_retention_module_bitmap { + SLEEP_RETENTION_MODULE_BM_NULL = BIT(SLEEP_RETENTION_MODULE_NULL), + /* clock module, which includes system and modem */ SLEEP_RETENTION_MODULE_BM_CLOCK_SYSTEM = BIT(SLEEP_RETENTION_MODULE_CLOCK_SYSTEM), SLEEP_RETENTION_MODULE_BM_CLOCK_MODEM = BIT(SLEEP_RETENTION_MODULE_CLOCK_MODEM), @@ -112,6 +115,7 @@ typedef enum periph_retention_module_bitmap { | SLEEP_RETENTION_MODULE_BM_PARLIO0 \ | SLEEP_RETENTION_MODULE_BM_GPSPI2 \ | SLEEP_RETENTION_MODULE_BM_LEDC \ + | SLEEP_RETENTION_MODULE_BM_NULL \ ) #ifdef __cplusplus } diff --git a/components/soc/esp32c6/include/soc/retention_periph_defs.h b/components/soc/esp32c6/include/soc/retention_periph_defs.h index 2b1854eced..f0f629f8f5 100644 --- a/components/soc/esp32c6/include/soc/retention_periph_defs.h +++ b/components/soc/esp32c6/include/soc/retention_periph_defs.h @@ -15,6 +15,7 @@ extern "C" { typedef enum periph_retention_module { SLEEP_RETENTION_MODULE_MIN = 0, + SLEEP_RETENTION_MODULE_NULL = SLEEP_RETENTION_MODULE_MIN, /* This module is for all peripherals that can't survive from PD_TOP to call init only. Shouldn't have any dependency. */ /* clock module, which includes system and modem */ SLEEP_RETENTION_MODULE_CLOCK_SYSTEM = 1, SLEEP_RETENTION_MODULE_CLOCK_MODEM = 2, @@ -55,6 +56,8 @@ typedef enum periph_retention_module { } periph_retention_module_t; typedef enum periph_retention_module_bitmap { + SLEEP_RETENTION_MODULE_BM_NULL = BIT(SLEEP_RETENTION_MODULE_NULL), + /* clock module, which includes system and modem */ SLEEP_RETENTION_MODULE_BM_CLOCK_SYSTEM = BIT(SLEEP_RETENTION_MODULE_CLOCK_SYSTEM), SLEEP_RETENTION_MODULE_BM_CLOCK_MODEM = BIT(SLEEP_RETENTION_MODULE_CLOCK_MODEM), @@ -114,6 +117,7 @@ typedef enum periph_retention_module_bitmap { | SLEEP_RETENTION_MODULE_BM_PARLIO0 \ | SLEEP_RETENTION_MODULE_BM_GPSPI2 \ | SLEEP_RETENTION_MODULE_BM_LEDC \ + | SLEEP_RETENTION_MODULE_BM_NULL \ ) #ifdef __cplusplus diff --git a/components/soc/esp32c61/include/soc/retention_periph_defs.h b/components/soc/esp32c61/include/soc/retention_periph_defs.h index f0a8f61f6a..4c6a134f22 100644 --- a/components/soc/esp32c61/include/soc/retention_periph_defs.h +++ b/components/soc/esp32c61/include/soc/retention_periph_defs.h @@ -15,6 +15,7 @@ extern "C" { typedef enum periph_retention_module { SLEEP_RETENTION_MODULE_MIN = 0, + SLEEP_RETENTION_MODULE_NULL = SLEEP_RETENTION_MODULE_MIN, /* This module is for all peripherals that can't survive from PD_TOP to call init only. Shouldn't have any dependency. */ /* clock module, which includes system and modem */ SLEEP_RETENTION_MODULE_CLOCK_SYSTEM = 1, SLEEP_RETENTION_MODULE_CLOCK_MODEM = 2, @@ -47,6 +48,8 @@ typedef enum periph_retention_module { } periph_retention_module_t; typedef enum periph_retention_module_bitmap { + SLEEP_RETENTION_MODULE_BM_NULL = BIT(SLEEP_RETENTION_MODULE_NULL), + /* clock module, which includes system and modem */ SLEEP_RETENTION_MODULE_BM_CLOCK_SYSTEM = BIT(SLEEP_RETENTION_MODULE_CLOCK_SYSTEM), SLEEP_RETENTION_MODULE_BM_CLOCK_MODEM = BIT(SLEEP_RETENTION_MODULE_CLOCK_MODEM), @@ -90,6 +93,7 @@ typedef enum periph_retention_module_bitmap { | SLEEP_RETENTION_MODULE_BM_ETM0 \ | SLEEP_RETENTION_MODULE_BM_GPSPI2 \ | SLEEP_RETENTION_MODULE_BM_LEDC \ + | SLEEP_RETENTION_MODULE_BM_NULL \ ) #ifdef __cplusplus diff --git a/components/soc/esp32h2/include/soc/retention_periph_defs.h b/components/soc/esp32h2/include/soc/retention_periph_defs.h index 766db48639..0b41e23c8d 100644 --- a/components/soc/esp32h2/include/soc/retention_periph_defs.h +++ b/components/soc/esp32h2/include/soc/retention_periph_defs.h @@ -15,6 +15,7 @@ extern "C" { typedef enum periph_retention_module { SLEEP_RETENTION_MODULE_MIN = 0, + SLEEP_RETENTION_MODULE_NULL = SLEEP_RETENTION_MODULE_MIN, /* This module is for all peripherals that can't survive from PD_TOP to call init only. Shouldn't have any dependency. */ /* clock module, which includes system and modem */ SLEEP_RETENTION_MODULE_CLOCK_SYSTEM = 1, SLEEP_RETENTION_MODULE_CLOCK_MODEM = 2, @@ -53,6 +54,8 @@ typedef enum periph_retention_module { } periph_retention_module_t; typedef enum periph_retention_module_bitmap { + SLEEP_RETENTION_MODULE_BM_NULL = BIT(SLEEP_RETENTION_MODULE_NULL), + /* clock module, which includes system and modem */ SLEEP_RETENTION_MODULE_BM_CLOCK_SYSTEM = BIT(SLEEP_RETENTION_MODULE_CLOCK_SYSTEM), SLEEP_RETENTION_MODULE_BM_CLOCK_MODEM = BIT(SLEEP_RETENTION_MODULE_CLOCK_MODEM), @@ -110,6 +113,7 @@ typedef enum periph_retention_module_bitmap { | SLEEP_RETENTION_MODULE_BM_PARLIO0 \ | SLEEP_RETENTION_MODULE_BM_GPSPI2 \ | SLEEP_RETENTION_MODULE_BM_LEDC \ + | SLEEP_RETENTION_MODULE_BM_NULL \ ) #ifdef __cplusplus diff --git a/components/soc/esp32p4/include/soc/retention_periph_defs.h b/components/soc/esp32p4/include/soc/retention_periph_defs.h index 39dd77b19e..1f3c144aaf 100644 --- a/components/soc/esp32p4/include/soc/retention_periph_defs.h +++ b/components/soc/esp32p4/include/soc/retention_periph_defs.h @@ -15,6 +15,7 @@ extern "C" { typedef enum periph_retention_module { SLEEP_RETENTION_MODULE_MIN = 0, + SLEEP_RETENTION_MODULE_NULL = SLEEP_RETENTION_MODULE_MIN, /* This module is for all peripherals that can't survive from PD_TOP to call init only. Shouldn't have any dependency. */ /* clock module, which includes system and modem */ SLEEP_RETENTION_MODULE_CLOCK_SYSTEM = 1, /* digital peripheral module, which includes Interrupt Matrix, HP_SYSTEM, @@ -58,6 +59,8 @@ typedef enum periph_retention_module { } periph_retention_module_t; typedef enum periph_retention_module_bitmap { + SLEEP_RETENTION_MODULE_BM_NULL = BIT(SLEEP_RETENTION_MODULE_NULL), + /* clock module, which includes system and modem */ SLEEP_RETENTION_MODULE_BM_CLOCK_SYSTEM = BIT(SLEEP_RETENTION_MODULE_CLOCK_SYSTEM), /* digital peripheral module, which includes Interrupt Matrix, HP_SYSTEM, @@ -130,6 +133,7 @@ typedef enum periph_retention_module_bitmap { | SLEEP_RETENTION_MODULE_BM_GPSPI2 \ | SLEEP_RETENTION_MODULE_BM_GPSPI3 \ | SLEEP_RETENTION_MODULE_BM_LEDC \ + | SLEEP_RETENTION_MODULE_BM_NULL \ ) #ifdef __cplusplus