mirror of
https://github.com/espressif/esp-idf.git
synced 2025-07-31 19:24:33 +02:00
Merge branch 'fix/freertos_tickless_idle_tick_jump_v5.2' into 'release/v5.2'
fix(freertos): Fixed tickless idle tick count accounting (v5.2) See merge request espressif/esp-idf!38512
This commit is contained in:
@@ -3063,19 +3063,7 @@ char * pcTaskGetName( TaskHandle_t xTaskToQuery ) /*lint !e971 Unqualified char
|
|||||||
/* Arrange for xTickCount to reach xNextTaskUnblockTime in
|
/* Arrange for xTickCount to reach xNextTaskUnblockTime in
|
||||||
* xTaskIncrementTick() when the scheduler resumes. This ensures
|
* xTaskIncrementTick() when the scheduler resumes. This ensures
|
||||||
* that any delayed tasks are resumed at the correct time. */
|
* that any delayed tasks are resumed at the correct time. */
|
||||||
#if ( configNUMBER_OF_CORES > 1 )
|
configASSERT( taskIS_SCHEDULER_SUSPENDED() == pdTRUE );
|
||||||
{
|
|
||||||
/* In SMP, the entire tickless idle handling block
|
|
||||||
* is replaced with a critical section, taking the kernel lock. */
|
|
||||||
configASSERT( taskIS_SCHEDULER_SUSPENDED() == pdFALSE );
|
|
||||||
}
|
|
||||||
#else /* configNUMBER_OF_CORES > 1 */
|
|
||||||
{
|
|
||||||
/* In single-core, the entire tickless idle handling block
|
|
||||||
* is done with scheduler suspended. */
|
|
||||||
configASSERT( taskIS_SCHEDULER_SUSPENDED() == pdTRUE );
|
|
||||||
}
|
|
||||||
#endif /* configNUMBER_OF_CORES > 1 */
|
|
||||||
configASSERT( xTicksToJump != ( TickType_t ) 0 );
|
configASSERT( xTicksToJump != ( TickType_t ) 0 );
|
||||||
|
|
||||||
xPendedTicks++;
|
xPendedTicks++;
|
||||||
@@ -4332,31 +4320,37 @@ static portTASK_FUNCTION( prvIdleTask, pvParameters )
|
|||||||
|
|
||||||
if( xExpectedIdleTime >= configEXPECTED_IDLE_TIME_BEFORE_SLEEP )
|
if( xExpectedIdleTime >= configEXPECTED_IDLE_TIME_BEFORE_SLEEP )
|
||||||
{
|
{
|
||||||
prvENTER_CRITICAL_OR_SUSPEND_ALL( &xKernelLock );
|
/* In SMP mode, the entire tickless idle handling block
|
||||||
|
* must be done with the kernel lock held. */
|
||||||
|
prvENTER_CRITICAL_SMP_ONLY( &xKernelLock );
|
||||||
{
|
{
|
||||||
/* Now the scheduler is suspended, the expected idle
|
vTaskSuspendAll();
|
||||||
* time can be sampled again, and this time its value can
|
|
||||||
* be used. */
|
|
||||||
configASSERT( xNextTaskUnblockTime >= xTickCount );
|
|
||||||
xExpectedIdleTime = prvGetExpectedIdleTime();
|
|
||||||
|
|
||||||
/* Define the following macro to set xExpectedIdleTime to 0
|
|
||||||
* if the application does not want
|
|
||||||
* portSUPPRESS_TICKS_AND_SLEEP() to be called. */
|
|
||||||
configPRE_SUPPRESS_TICKS_AND_SLEEP_PROCESSING( xExpectedIdleTime );
|
|
||||||
|
|
||||||
if( xExpectedIdleTime >= configEXPECTED_IDLE_TIME_BEFORE_SLEEP )
|
|
||||||
{
|
{
|
||||||
traceLOW_POWER_IDLE_BEGIN();
|
/* Now the scheduler is suspended, the expected idle
|
||||||
portSUPPRESS_TICKS_AND_SLEEP( xExpectedIdleTime );
|
* time can be sampled again, and this time its value can
|
||||||
traceLOW_POWER_IDLE_END();
|
* be used. */
|
||||||
}
|
configASSERT( xNextTaskUnblockTime >= xTickCount );
|
||||||
else
|
xExpectedIdleTime = prvGetExpectedIdleTime();
|
||||||
{
|
|
||||||
mtCOVERAGE_TEST_MARKER();
|
/* Define the following macro to set xExpectedIdleTime to 0
|
||||||
|
* if the application does not want
|
||||||
|
* portSUPPRESS_TICKS_AND_SLEEP() to be called. */
|
||||||
|
configPRE_SUPPRESS_TICKS_AND_SLEEP_PROCESSING( xExpectedIdleTime );
|
||||||
|
|
||||||
|
if( xExpectedIdleTime >= configEXPECTED_IDLE_TIME_BEFORE_SLEEP )
|
||||||
|
{
|
||||||
|
traceLOW_POWER_IDLE_BEGIN();
|
||||||
|
portSUPPRESS_TICKS_AND_SLEEP( xExpectedIdleTime );
|
||||||
|
traceLOW_POWER_IDLE_END();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mtCOVERAGE_TEST_MARKER();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
( void ) xTaskResumeAll();
|
||||||
}
|
}
|
||||||
( void ) prvEXIT_CRITICAL_OR_RESUME_ALL( &xKernelLock );
|
prvEXIT_CRITICAL_SMP_ONLY( &xKernelLock );
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@@ -3,5 +3,5 @@
|
|||||||
# In order for the cases defined by `TEST_CASE` in "misc" to be linked into
|
# 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
|
# the final elf, the component can be registered as WHOLE_ARCHIVE
|
||||||
idf_component_register(SRC_DIRS "."
|
idf_component_register(SRC_DIRS "."
|
||||||
PRIV_REQUIRES unity test_utils
|
PRIV_REQUIRES unity test_utils esp_pm
|
||||||
WHOLE_ARCHIVE)
|
WHOLE_ARCHIVE)
|
||||||
|
116
components/freertos/test_apps/freertos/misc/test_tickless_idle.c
Normal file
116
components/freertos/test_apps/freertos/misc/test_tickless_idle.c
Normal file
@@ -0,0 +1,116 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
#include <stdio.h>
|
||||||
|
#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 */
|
@@ -10,7 +10,14 @@ CONFIGS = [
|
|||||||
pytest.param('psram', marks=[pytest.mark.esp32]),
|
pytest.param('psram', marks=[pytest.mark.esp32]),
|
||||||
pytest.param('release', marks=[pytest.mark.supported_targets]),
|
pytest.param('release', marks=[pytest.mark.supported_targets]),
|
||||||
pytest.param('single_core', marks=[pytest.mark.esp32]),
|
pytest.param('single_core', marks=[pytest.mark.esp32]),
|
||||||
pytest.param('smp', marks=[pytest.mark.supported_targets, pytest.mark.temp_skip_ci(targets=['esp32h2', 'esp32p4'], reason='test failed/TBD IDF-8113')]),
|
# TODO: [ESP32C5] IDF-10335
|
||||||
|
# TODO: [ESP32C61] IDF-11146
|
||||||
|
pytest.param('smp', marks=[
|
||||||
|
pytest.mark.supported_targets,
|
||||||
|
pytest.mark.temp_skip_ci(targets=['esp32p4', 'esp32c5', 'esp32c61'],
|
||||||
|
reason='test failed/TBD IDF-8113')
|
||||||
|
]),
|
||||||
|
pytest.param('tickless_idle', marks=[pytest.mark.supported_targets]),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
@@ -0,0 +1,2 @@
|
|||||||
|
CONFIG_PM_ENABLE=y
|
||||||
|
CONFIG_FREERTOS_USE_TICKLESS_IDLE=y
|
Reference in New Issue
Block a user