From 0d9d4f60df699612e4cb73cb10984add38a71c43 Mon Sep 17 00:00:00 2001 From: Darian Leung Date: Wed, 21 Sep 2022 13:20:19 +0800 Subject: [PATCH 1/3] freertos: Add unit test utilities This commit adds "freertos_test_utils.h/c" that contains utility functions used in FreeRTOS unit tests. --- components/freertos/test/CMakeLists.txt | 3 +- .../test/integration/freertos_test_utils.c | 72 +++++++++++++++++++ .../test/integration/freertos_test_utils.h | 32 +++++++++ 3 files changed, 106 insertions(+), 1 deletion(-) create mode 100644 components/freertos/test/integration/freertos_test_utils.c create mode 100644 components/freertos/test/integration/freertos_test_utils.h diff --git a/components/freertos/test/CMakeLists.txt b/components/freertos/test/CMakeLists.txt index f7958de644..ac4364d633 100644 --- a/components/freertos/test/CMakeLists.txt +++ b/components/freertos/test/CMakeLists.txt @@ -1,7 +1,8 @@ # For refactored FreeRTOS unit tests, we need to support #include "xxx.h" of FreeRTOS headers idf_component_get_property(FREERTOS_ORIG_INCLUDE_PATH freertos ORIG_INCLUDE_PATH) -idf_component_register(SRC_DIRS integration/event_groups +idf_component_register(SRC_DIRS integration # For freertos_test_utils.c + integration/event_groups integration/queue integration/stream_buffer integration/tasks diff --git a/components/freertos/test/integration/freertos_test_utils.c b/components/freertos/test/integration/freertos_test_utils.c new file mode 100644 index 0000000000..80ba37c788 --- /dev/null +++ b/components/freertos/test/integration/freertos_test_utils.c @@ -0,0 +1,72 @@ +/* + * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "FreeRTOS.h" +#include "semphr.h" +#include "freertos_test_utils.h" + +#if ( configNUM_CORES > 1 ) + + typedef struct { + const TestFunction_t pxTestCode; + void * const pvTestCodeArg; + const SemaphoreHandle_t xTaskDoneSem; + } TestArgs_t; + + static void test_func_task( void * pvParameters ) + { + TestArgs_t * pxTestArgs = ( TestArgs_t * ) pvParameters; + /* Call the test function */ + pxTestArgs->pxTestCode( pxTestArgs->pvTestCodeArg ); + /* Indicate completion to the creation task and wait to be deleted. */ + xSemaphoreGive( pxTestArgs->xTaskDoneSem ); + vTaskSuspend( NULL ); + } + + void vTestOnAllCores( TestFunction_t pxTestCode, void * pvTestCodeArg, uint32_t ulStackDepth, UBaseType_t uxPriority ) + { + SemaphoreHandle_t xTaskDoneSem = xSemaphoreCreateCounting( configNUM_CORES, 0 ); + TaskHandle_t xTaskHandles[ configNUM_CORES ]; + TestArgs_t xTestArgs = { + .pxTestCode = pxTestCode, + .pvTestCodeArg = pvTestCodeArg, + .xTaskDoneSem = xTaskDoneSem, + }; + + /* Create a separate task on each core to run the test function */ + for( BaseType_t xCoreID = 0; xCoreID < configNUM_CORES; xCoreID++ ) { + #if ( CONFIG_FREERTOS_SMP == 1 ) + xTaskCreateAffinitySet( test_func_task, + "task", + ulStackDepth, + ( void * ) &xTestArgs, + uxPriority, + ( UBaseType_t ) ( 1 << xCoreID ), + &( xTaskHandles[ xCoreID ] ) ); + #else + xTaskCreatePinnedToCore( test_func_task, + "task", + ulStackDepth, + ( void * ) &xTestArgs, + uxPriority, + &( xTaskHandles[ xCoreID ] ), + xCoreID ); + #endif + } + + /* Wait for each tasks to complete test */ + for( BaseType_t xCoreID = 0; xCoreID < configNUM_CORES; xCoreID++ ) { + xSemaphoreTake( xTaskDoneSem, portMAX_DELAY ); + } + + /* Cleanup */ + for( BaseType_t xCoreID = 0; xCoreID < configNUM_CORES; xCoreID++ ) { + vTaskDelete( xTaskHandles[ xCoreID ] ); + } + vSemaphoreDelete( xTaskDoneSem ); + } + +#endif /* ( configNUM_CORES > 1 ) */ diff --git a/components/freertos/test/integration/freertos_test_utils.h b/components/freertos/test/integration/freertos_test_utils.h new file mode 100644 index 0000000000..fee7259d21 --- /dev/null +++ b/components/freertos/test/integration/freertos_test_utils.h @@ -0,0 +1,32 @@ +/* + * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include "FreeRTOS.h" + +#if ( configNUM_CORES > 1 ) + + /** + * @brief Prototype for test function. + * + * A test function can be passed to vTestOnAllCores() which will run the test function from a task on each core. + */ + typedef void (* TestFunction_t)( void * ); + + /** + * @brief Run a test function on each core + * + * This function will internally create a task pinned to each core, where each task will call the provided test + * function. This function will block until all cores finish executing the test function. + * + * @param pxTestCode Test function + * @param pvTestCodeArg Argument provided to test function + * @param ulStackDepth Stack depth of the created tasks + * @param uxPriority Priority of the created tasks + */ + void vTestOnAllCores( TestFunction_t pxTestCode, void * pvTestCodeArg, uint32_t ulStackDepth, UBaseType_t uxPriority ); + +#endif /* ( configNUM_CORES > 1 ) */ From 193e30bbf7cb452caadcee3cd38cb0232ebb6e9f Mon Sep 17 00:00:00 2001 From: Darian Leung Date: Wed, 21 Sep 2022 13:24:14 +0800 Subject: [PATCH 2/3] freertos: Rename vTaskDelayUntil unit test file --- .../tasks/{test_freertos_task_delay_until.c => test_task_delay.c} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename components/freertos/test/integration/tasks/{test_freertos_task_delay_until.c => test_task_delay.c} (100%) diff --git a/components/freertos/test/integration/tasks/test_freertos_task_delay_until.c b/components/freertos/test/integration/tasks/test_task_delay.c similarity index 100% rename from components/freertos/test/integration/tasks/test_freertos_task_delay_until.c rename to components/freertos/test/integration/tasks/test_task_delay.c From aaf7547665d1c03a0cade4bbbbd6023718100603 Mon Sep 17 00:00:00 2001 From: Darian Leung Date: Wed, 21 Sep 2022 13:25:05 +0800 Subject: [PATCH 3/3] freertos: Refactor vTaskDelay() and vTaskDelayUntil() unit tests This commit refactors the existing vTaskDelayUntil() unit test according to the FreeRTOS unit test guidelines, and adds a new vTaskDelay() unit test. --- .../freertos/test/integration/portTestMacro.h | 7 +- .../test/integration/tasks/test_task_delay.c | 198 +++++++++++++----- 2 files changed, 146 insertions(+), 59 deletions(-) diff --git a/components/freertos/test/integration/portTestMacro.h b/components/freertos/test/integration/portTestMacro.h index 42bfb2662c..f46a2048aa 100644 --- a/components/freertos/test/integration/portTestMacro.h +++ b/components/freertos/test/integration/portTestMacro.h @@ -5,9 +5,12 @@ */ #include "test_utils.h" -#include "esp_cpu.h" #define configTEST_DEFAULT_STACK_SIZE 4096 #define configTEST_UNITY_TASK_PRIORITY UNITY_FREERTOS_PRIORITY -#define portTEST_GET_TIME() ((UBaseType_t) esp_cpu_get_cycle_count()) +#define portTEST_REF_CLOCK_TYPE uint32_t +#define portTEST_REF_CLOCK_INIT() ref_clock_init() +#define portTEST_REF_CLOCK_DEINIT() ref_clock_deinit() +#define portTEST_REF_CLOCK_GET_TIME() ((uint32_t) ref_clock_get()) +#define portTEST_TICKS_TO_REF_CLOCK(ticks) ((ticks) * (1000000/configTICK_RATE_HZ)) diff --git a/components/freertos/test/integration/tasks/test_task_delay.c b/components/freertos/test/integration/tasks/test_task_delay.c index 921427e8ea..9a54db40b5 100644 --- a/components/freertos/test/integration/tasks/test_task_delay.c +++ b/components/freertos/test/integration/tasks/test_task_delay.c @@ -4,76 +4,160 @@ * SPDX-License-Identifier: Apache-2.0 */ +#include "FreeRTOS.h" +#include "task.h" +#include "semphr.h" +#include "unity.h" +#include "freertos_test_utils.h" +#include "portTestMacro.h" + +/* ------------------------------------------------------------------------------------------------------------------ */ + /* - Test for FreeRTOS vTaskDelayUntil() function by comparing the delay period of - the function to comparing it to ref clock. +Test vTaskDelay + +Purpose: + - Test that vTaskDelay is accurate +Procedure: + - The test contains TEST_VTASKDELAY_ITERATIONS number of iterations. For each iteration... + - vTaskDelay(1) to align to next tick boundary + - Store current tick count and current time (using ref clock) + - vTaskDelay for TEST_VTASKDELAY_TICKS + - Get post delay tick count and ref clock time + - For single core, run the test directly from the UnityTask + - For SMP, run the test once on each core (using vTestOnAllCores()) +Expected: + - The elapsed ticks should be TEST_VTASKDELAY_TICKS, with 1 tick of error allowed (in case ref clock functions last + long enough to cross a tick boundary). + - The elapsed time should be equivalent to TEST_VTASKDELAY_TICKS tick periods, with 1 tick period of error allowed + (in case ref clock functions last longer that a tick period). */ -#include -#include -#include "freertos/FreeRTOS.h" -#include "freertos/task.h" -#include "freertos/semphr.h" -#include "unity.h" -#include "test_utils.h" +#if ( INCLUDE_vTaskDelay == 1 ) -#define TSK_PRIORITY (UNITY_FREERTOS_PRIORITY + 1) +#define TEST_VTASKDELAY_TICKS 5 // Number of ticks to delay in test +#define TEST_VTASKDELAY_ITERATIONS 5 // Number of iterations in test -#define NO_OF_CYCLES 5 -#define NO_OF_TASKS_PER_CORE 2 -#define TICKS_TO_DELAY 10 -#define TICK_RATE configTICK_RATE_HZ -#define TICK_PERIOD_US (1000000/TICK_RATE) -#define IDEAL_DELAY_PERIOD_MS ((1000*TICKS_TO_DELAY)/TICK_RATE) -#define IDEAL_DELAY_PERIOD_US (IDEAL_DELAY_PERIOD_MS*1000) - -#define TICKS_TO_MS(x) (((x)*1000)/TICK_RATE) -#define REF_TO_ROUND_MS(x) (((x)+500)/1000) - -static SemaphoreHandle_t task_delete_semphr; - -static void delaying_task(void* arg) +static void test_vTaskDelay(void *arg) { - uint64_t ref_prev, ref_current; - TickType_t last_wake_time; - TickType_t ticks_before_delay; + for (int i = 0; i < TEST_VTASKDELAY_ITERATIONS; i++) { + TickType_t tick_start, tick_end; + portTEST_REF_CLOCK_TYPE ref_clock_start, ref_clock_end; - vTaskDelay(1); //Delay until next tick to synchronize operations to tick boundaries + /* Delay until the next tick boundary */ + vTaskDelay(1); - last_wake_time = xTaskGetTickCount(); - ticks_before_delay = last_wake_time; - ref_prev = ref_clock_get(); + /* Get the current tick count and ref clock time */ + tick_start = xTaskGetTickCount(); + ref_clock_start = portTEST_REF_CLOCK_GET_TIME(); - for(int i = 0; i < NO_OF_CYCLES; i++){ - //Delay of TICKS_TO_DELAY - vTaskDelayUntil(&last_wake_time, TICKS_TO_DELAY); - //Get current ref clock - TEST_ASSERT_EQUAL(IDEAL_DELAY_PERIOD_MS, TICKS_TO_MS(xTaskGetTickCount() - ticks_before_delay)); - ref_current = ref_clock_get(); - TEST_ASSERT_UINT32_WITHIN(TICK_PERIOD_US, IDEAL_DELAY_PERIOD_US, (uint32_t)(ref_current - ref_prev)); - ref_prev = ref_current; - ticks_before_delay = last_wake_time; + vTaskDelay(TEST_VTASKDELAY_TICKS); + + /* Get the post delay tick count and ref clock time */ + tick_end = xTaskGetTickCount(); + ref_clock_end = portTEST_REF_CLOCK_GET_TIME(); + + /* Check that elapsed ticks and ref clock is accurate. We allow 1 tick of error in case vTaskDelay() was called + * right before/after the tick boundary. */ + #if ( configUSE_16_BIT_TICKS == 1 ) + TEST_ASSERT_UINT16_WITHIN(1, TEST_VTASKDELAY_TICKS, tick_end - tick_start); + #else + TEST_ASSERT_UINT32_WITHIN(1, TEST_VTASKDELAY_TICKS, tick_end - tick_start); + #endif + TEST_ASSERT_UINT32_WITHIN(portTEST_TICKS_TO_REF_CLOCK(1), + portTEST_TICKS_TO_REF_CLOCK(TEST_VTASKDELAY_TICKS), + ref_clock_end - ref_clock_start); } - - //Delete Task after prescribed number of cycles - xSemaphoreGive(task_delete_semphr); - vTaskDelete(NULL); } -TEST_CASE("Test vTaskDelayUntil", "[freertos]") +TEST_CASE("Tasks: Test vTaskDelay", "[freertos]") { - task_delete_semphr = xQueueCreateCountingSemaphore(NO_OF_TASKS_PER_CORE*portNUM_PROCESSORS, 0); - ref_clock_init(); + portTEST_REF_CLOCK_INIT(); - for(int i = 0; i < portNUM_PROCESSORS; i++){ - xTaskCreatePinnedToCore(delaying_task, "delay_pinned", 1024, NULL, TSK_PRIORITY, NULL, i); - xTaskCreatePinnedToCore(delaying_task, "delay_no_aff", 1024, NULL, TSK_PRIORITY, NULL, tskNO_AFFINITY); - } + #if ( configNUM_CORES > 1 ) + vTestOnAllCores(test_vTaskDelay, NULL, configTEST_DEFAULT_STACK_SIZE, configTEST_UNITY_TASK_PRIORITY - 1); + #else + /* Test vTaskDelay directly on the current core */ + test_vTaskDelay(NULL); + #endif - for(int i = 0; i < NO_OF_TASKS_PER_CORE*portNUM_PROCESSORS; i++){ - xSemaphoreTake(task_delete_semphr, portMAX_DELAY); - } - //Cleanup - vSemaphoreDelete(task_delete_semphr); - ref_clock_deinit(); + portTEST_REF_CLOCK_DEINIT(); } + +#endif /* ( INCLUDE_vTaskDelay == 1 ) */ + +/* ------------------------------------------------------------------------------------------------------------------ */ + +/* +Test vTaskDelayUntil + +Purpose: + - Test that vTaskDelayUntil is accurate +Procedure: + - The test contains TEST_VTASKDELAYUNTIL_ITERATIONS number of iterations. For each iteration... + - vTaskDelay(1) to align to next tick boundary + - Store current tick count and current time (using ref clock) + - Call vTaskDelayUntil() for TEST_VTASKDELAYUNTIL_TICKS, using the stored tick count as the previous wake time + - Get post delay tick count and ref clock time + - For single core, run the test directly from the UnityTask + - For SMP, run the test once on each core (using vTestOnAllCores()) +Expected: + - The elapsed ticks should be exactly TEST_VTASKDELAYUNTIL_TICKS since vTaskDelayUntil() is relative to the previous + wake time + - The elapsed time should be equivalent to TEST_VTASKDELAYUNTIL_TICKS tick periods, with 1 tick period of error + allowed (in case ref clock functions last longer that a tick period). +*/ + +#if ( INCLUDE_xTaskDelayUntil == 1 ) + +#define TEST_VTASKDELAYUNTIL_TICKS 5 // Number of ticks to delay in test +#define TEST_VTASKDELAYUNTIL_ITERATIONS 5 // Number of iterations in test + +static void test_vTaskDelayUntil(void *arg) +{ + /* Delay until the next tick boundary */ + vTaskDelay(1); + + for (int i = 0; i < TEST_VTASKDELAYUNTIL_ITERATIONS; i++) { + TickType_t tick_start, tick_end, last_wake_tick; + portTEST_REF_CLOCK_TYPE ref_clock_start, ref_clock_end; + + /* Get the current tick count and ref clock time */ + tick_start = xTaskGetTickCount(); + last_wake_tick = tick_start; + ref_clock_start = portTEST_REF_CLOCK_GET_TIME(); + + vTaskDelayUntil(&last_wake_tick, TEST_VTASKDELAYUNTIL_TICKS); + + /* Get the post delay tick count and ref clock time */ + tick_end = xTaskGetTickCount(); + ref_clock_end = portTEST_REF_CLOCK_GET_TIME(); + + /* Check that the elapsed ticks is accurate. Elapsed ticks should be exact as vTaskDelayUntil() executes a + * delay relative to last_wake_tick. */ + TEST_ASSERT_EQUAL(TEST_VTASKDELAYUNTIL_TICKS, tick_end - tick_start); + TEST_ASSERT_EQUAL(tick_end, last_wake_tick); + + /* Check that the elapsed ref clock time is accurate. We allow 1 tick time worth of error to account for the + * the execution time of the ref clock functions. */ + TEST_ASSERT_UINT32_WITHIN(portTEST_TICKS_TO_REF_CLOCK(1), + portTEST_TICKS_TO_REF_CLOCK(TEST_VTASKDELAYUNTIL_TICKS), + ref_clock_end - ref_clock_start); + } +} + +TEST_CASE("Tasks: Test vTaskDelayUntil", "[freertos]") +{ + portTEST_REF_CLOCK_INIT(); + + #if ( configNUM_CORES > 1 ) + vTestOnAllCores(test_vTaskDelayUntil, NULL, configTEST_DEFAULT_STACK_SIZE, configTEST_UNITY_TASK_PRIORITY - 1); + #else + /* Test vTaskDelay directly on the current core */ + test_vTaskDelayUntil(NULL); + #endif + + portTEST_REF_CLOCK_DEINIT(); +} + +#endif /* ( INCLUDE_xTaskDelayUntil == 1 ) */