|
| 1 | +/* |
| 2 | + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD |
| 3 | + * |
| 4 | + * SPDX-License-Identifier: Apache-2.0 |
| 5 | + */ |
| 6 | +#include <stdio.h> |
| 7 | +#include "unity.h" |
| 8 | +#include "freertos/FreeRTOS.h" |
| 9 | +#include "freertos/task.h" |
| 10 | +#include "freertos/semphr.h" |
| 11 | +#include "esp_pm.h" |
| 12 | +#include "esp_private/esp_clk.h" |
| 13 | + |
| 14 | +#include "sdkconfig.h" |
| 15 | + |
| 16 | +#if CONFIG_FREERTOS_USE_TICKLESS_IDLE && CONFIG_PM_ENABLE |
| 17 | + |
| 18 | +#define MHZ (1000000) |
| 19 | + |
| 20 | +static SemaphoreHandle_t test_sem; |
| 21 | +static volatile bool test_timeout_occurred = false; |
| 22 | + |
| 23 | +static void producer_task(void *arg) |
| 24 | +{ |
| 25 | + while (1) { |
| 26 | + xSemaphoreGive(test_sem); |
| 27 | + vTaskDelay(pdMS_TO_TICKS(5)); // Give semaphore every 5ms |
| 28 | + } |
| 29 | +} |
| 30 | + |
| 31 | +static void consumer_task(void *arg) |
| 32 | +{ |
| 33 | + while (1) { |
| 34 | + // Try to take semaphore with 1 second timeout |
| 35 | + // Should never timeout as producer is giving the semaphore every 5ms |
| 36 | + if (xSemaphoreTake(test_sem, pdMS_TO_TICKS(1000)) == pdFALSE) { |
| 37 | + test_timeout_occurred = true; // Mark that timeout occurred |
| 38 | + break; |
| 39 | + } |
| 40 | + } |
| 41 | + |
| 42 | + vTaskSuspend(NULL); |
| 43 | +} |
| 44 | + |
| 45 | +/** |
| 46 | + * Test case to verify timing accuracy during tickless idle mode |
| 47 | + * |
| 48 | + * This test verifies that FreeRTOS maintains accurate timing when the system |
| 49 | + * enters and exits tickless idle mode, particularly when combined with scheduler |
| 50 | + * suspension operations. |
| 51 | + * |
| 52 | + * Test Setup: |
| 53 | + * - Configures the system to use tickless idle mode |
| 54 | + * - Creates a producer-consumer pair using a semaphore: |
| 55 | + * * Producer gives semaphore every 5ms |
| 56 | + * * Consumer takes semaphore with 1-second timeout |
| 57 | + * - Periodically suspend and resume the scheduler |
| 58 | + * |
| 59 | + * Expected Behavior: |
| 60 | + * - The consumer should never timeout as the producer gives the semaphore |
| 61 | + * every 5ms, well within the 1-second timeout window |
| 62 | + * - Any timing inaccuracies during tickless idle would manifest as |
| 63 | + * unexpected semaphore timeouts |
| 64 | + * - The test passes if no timeouts occur. |
| 65 | + */ |
| 66 | +TEST_CASE("Test semaphore timeout during tickless idle", "[freertos]") |
| 67 | +{ |
| 68 | + // Configure tickless idle |
| 69 | + esp_pm_config_t pm_config = { |
| 70 | + .max_freq_mhz = esp_clk_cpu_freq() / MHZ, |
| 71 | + .min_freq_mhz = esp_clk_cpu_freq() / MHZ, |
| 72 | + .light_sleep_enable = true, |
| 73 | + }; |
| 74 | + TEST_ESP_OK(esp_pm_configure(&pm_config)); |
| 75 | + |
| 76 | + // Create test semaphore |
| 77 | + test_sem = xSemaphoreCreateBinary(); |
| 78 | + TEST_ASSERT_NOT_NULL(test_sem); |
| 79 | + |
| 80 | + test_timeout_occurred = false; |
| 81 | + |
| 82 | + // Create producer and consumer tasks |
| 83 | + TaskHandle_t producer_handle, consumer_handle; |
| 84 | + TEST_ASSERT_EQUAL(pdPASS, xTaskCreate(producer_task, "producer", 2048, NULL, 1, &producer_handle)); |
| 85 | + TEST_ASSERT_EQUAL(pdPASS, xTaskCreate(consumer_task, "consumer", 2048, NULL, 2, &consumer_handle)); |
| 86 | + |
| 87 | + // Run the test for a while to ensure tickless idle mode has |
| 88 | + // enough time to enter and exit multiple times |
| 89 | + vTaskDelay(pdMS_TO_TICKS(10000)); |
| 90 | + |
| 91 | + // Suspend scheduler briefly |
| 92 | + vTaskSuspendAll(); |
| 93 | + |
| 94 | + // Resume scheduler. This unrolls pended tick events. |
| 95 | + // This should not cause any unexpected timeouts if |
| 96 | + // tickless idle mode is working correctly. |
| 97 | + xTaskResumeAll(); |
| 98 | + |
| 99 | + // Verify no timeout occurred |
| 100 | + TEST_ASSERT_FALSE(test_timeout_occurred); |
| 101 | + |
| 102 | + // Disable power management before exit to prevent memory leaks |
| 103 | + esp_pm_config_t default_config = { |
| 104 | + .max_freq_mhz = CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ, |
| 105 | + .min_freq_mhz = CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ, |
| 106 | + .light_sleep_enable = false, |
| 107 | + }; |
| 108 | + esp_pm_configure(&default_config); |
| 109 | + |
| 110 | + // Cleanup |
| 111 | + vTaskDelete(producer_handle); |
| 112 | + vTaskDelete(consumer_handle); |
| 113 | + vSemaphoreDelete(test_sem); |
| 114 | +} |
| 115 | + |
| 116 | +#endif /* CONFIG_FREERTOS_USE_TICKLESS_IDLE && CONFIG_PM_ENABLE */ |
0 commit comments