From 6d9ed9f905856790acfe7a2bb811ff7c739f2854 Mon Sep 17 00:00:00 2001 From: Sudeep Mohanty Date: Thu, 27 Mar 2025 08:17:54 +0100 Subject: [PATCH] test(freertos): Added a unit test for tickless idle tick jump scenario This commit adds a unit test to test that tickless idle mode does not cause an unexpected jump in tick count after tickless idle mode is exited. --- .../test_apps/freertos/misc/CMakeLists.txt | 2 +- .../freertos/misc/test_tickless_idle.c | 116 ++++++++++++++++++ .../test_apps/freertos/pytest_freertos.py | 3 +- .../freertos/sdkconfig.ci.tickless_idle | 2 + 4 files changed, 121 insertions(+), 2 deletions(-) create mode 100644 components/freertos/test_apps/freertos/misc/test_tickless_idle.c create mode 100644 components/freertos/test_apps/freertos/sdkconfig.ci.tickless_idle diff --git a/components/freertos/test_apps/freertos/misc/CMakeLists.txt b/components/freertos/test_apps/freertos/misc/CMakeLists.txt index 66163e4806..4611f8e925 100644 --- a/components/freertos/test_apps/freertos/misc/CMakeLists.txt +++ b/components/freertos/test_apps/freertos/misc/CMakeLists.txt @@ -3,5 +3,5 @@ # In order for the cases defined by `TEST_CASE` in "misc" to be linked into # the final elf, the component can be registered as WHOLE_ARCHIVE idf_component_register(SRC_DIRS "." - PRIV_REQUIRES unity test_utils esp_timer + PRIV_REQUIRES unity test_utils esp_timer esp_pm WHOLE_ARCHIVE) diff --git a/components/freertos/test_apps/freertos/misc/test_tickless_idle.c b/components/freertos/test_apps/freertos/misc/test_tickless_idle.c new file mode 100644 index 0000000000..7ed64ddf35 --- /dev/null +++ b/components/freertos/test_apps/freertos/misc/test_tickless_idle.c @@ -0,0 +1,116 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include +#include "unity.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/semphr.h" +#include "esp_pm.h" +#include "esp_private/esp_clk.h" + +#include "sdkconfig.h" + +#if CONFIG_FREERTOS_USE_TICKLESS_IDLE && CONFIG_PM_ENABLE + +#define MHZ (1000000) + +static SemaphoreHandle_t test_sem; +static volatile bool test_timeout_occurred = false; + +static void producer_task(void *arg) +{ + while (1) { + xSemaphoreGive(test_sem); + vTaskDelay(pdMS_TO_TICKS(5)); // Give semaphore every 5ms + } +} + +static void consumer_task(void *arg) +{ + while (1) { + // Try to take semaphore with 1 second timeout + // Should never timeout as producer is giving the semaphore every 5ms + if (xSemaphoreTake(test_sem, pdMS_TO_TICKS(1000)) == pdFALSE) { + test_timeout_occurred = true; // Mark that timeout occurred + break; + } + } + + vTaskSuspend(NULL); +} + +/** + * Test case to verify timing accuracy during tickless idle mode + * + * This test verifies that FreeRTOS maintains accurate timing when the system + * enters and exits tickless idle mode, particularly when combined with scheduler + * suspension operations. + * + * Test Setup: + * - Configures the system to use tickless idle mode + * - Creates a producer-consumer pair using a semaphore: + * * Producer gives semaphore every 5ms + * * Consumer takes semaphore with 1-second timeout + * - Periodically suspend and resume the scheduler + * + * Expected Behavior: + * - The consumer should never timeout as the producer gives the semaphore + * every 5ms, well within the 1-second timeout window + * - Any timing inaccuracies during tickless idle would manifest as + * unexpected semaphore timeouts + * - The test passes if no timeouts occur. + */ +TEST_CASE("Test semaphore timeout during tickless idle", "[freertos]") +{ + // Configure tickless idle + esp_pm_config_t pm_config = { + .max_freq_mhz = esp_clk_cpu_freq() / MHZ, + .min_freq_mhz = esp_clk_cpu_freq() / MHZ, + .light_sleep_enable = true, + }; + TEST_ESP_OK(esp_pm_configure(&pm_config)); + + // Create test semaphore + test_sem = xSemaphoreCreateBinary(); + TEST_ASSERT_NOT_NULL(test_sem); + + test_timeout_occurred = false; + + // Create producer and consumer tasks + TaskHandle_t producer_handle, consumer_handle; + TEST_ASSERT_EQUAL(pdPASS, xTaskCreate(producer_task, "producer", 2048, NULL, 1, &producer_handle)); + TEST_ASSERT_EQUAL(pdPASS, xTaskCreate(consumer_task, "consumer", 2048, NULL, 2, &consumer_handle)); + + // Run the test for a while to ensure tickless idle mode has + // enough time to enter and exit multiple times + vTaskDelay(pdMS_TO_TICKS(10000)); + + // Suspend scheduler briefly + vTaskSuspendAll(); + + // Resume scheduler. This unrolls pended tick events. + // This should not cause any unexpected timeouts if + // tickless idle mode is working correctly. + xTaskResumeAll(); + + // Verify no timeout occurred + TEST_ASSERT_FALSE(test_timeout_occurred); + + // Disable power management before exit to prevent memory leaks + esp_pm_config_t default_config = { + .max_freq_mhz = CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ, + .min_freq_mhz = CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ, + .light_sleep_enable = false, + }; + esp_pm_configure(&default_config); + + // Cleanup + vTaskDelete(producer_handle); + vTaskDelete(consumer_handle); + vSemaphoreDelete(test_sem); +} + +#endif /* CONFIG_FREERTOS_USE_TICKLESS_IDLE && CONFIG_PM_ENABLE */ diff --git a/components/freertos/test_apps/freertos/pytest_freertos.py b/components/freertos/test_apps/freertos/pytest_freertos.py index d21d9dad70..b3cc933d41 100644 --- a/components/freertos/test_apps/freertos/pytest_freertos.py +++ b/components/freertos/test_apps/freertos/pytest_freertos.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD +# SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD # SPDX-License-Identifier: Apache-2.0 import pytest from pytest_embedded import Dut @@ -10,6 +10,7 @@ CONFIGS = [ pytest.param('release', marks=[pytest.mark.supported_targets]), pytest.param('single_core', marks=[pytest.mark.esp32, pytest.mark.esp32p4]), pytest.param('smp', marks=[pytest.mark.supported_targets, pytest.mark.temp_skip_ci(targets=['esp32h2', 'esp32p4'], reason='test failed/TBD IDF-8113')]), + pytest.param('tickless_idle', marks=[pytest.mark.supported_targets]), ] diff --git a/components/freertos/test_apps/freertos/sdkconfig.ci.tickless_idle b/components/freertos/test_apps/freertos/sdkconfig.ci.tickless_idle new file mode 100644 index 0000000000..e8945648db --- /dev/null +++ b/components/freertos/test_apps/freertos/sdkconfig.ci.tickless_idle @@ -0,0 +1,2 @@ +CONFIG_PM_ENABLE=y +CONFIG_FREERTOS_USE_TICKLESS_IDLE=y