From 188c21319d241c7abf931d8639a6fb1c4ca3089b Mon Sep 17 00:00:00 2001 From: Darian Leung Date: Mon, 19 Sep 2022 19:17:51 +0800 Subject: [PATCH 1/3] freertos: Add test reorganization README.md and portTestMacro.h This commit adds a README.md containing guidelines on how to refactor the FreeRTOS unit tests for upstreaming. A portTestMacro.h header was also added which contains port implementation specific macros used by the test cases. --- components/freertos/test/CMakeLists.txt | 5 +- .../freertos/test/integration/README.md | 73 +++++++++++++++++++ .../freertos/test/integration/portTestMacro.h | 13 ++++ 3 files changed, 90 insertions(+), 1 deletion(-) create mode 100644 components/freertos/test/integration/README.md create mode 100644 components/freertos/test/integration/portTestMacro.h diff --git a/components/freertos/test/CMakeLists.txt b/components/freertos/test/CMakeLists.txt index 6e81913af4..f7958de644 100644 --- a/components/freertos/test/CMakeLists.txt +++ b/components/freertos/test/CMakeLists.txt @@ -1,3 +1,6 @@ +# 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 integration/queue integration/stream_buffer @@ -6,6 +9,6 @@ idf_component_register(SRC_DIRS integration/event_groups miscellaneous performance port - PRIV_INCLUDE_DIRS . + PRIV_INCLUDE_DIRS . ./integration "${FREERTOS_ORIG_INCLUDE_PATH}" PRIV_REQUIRES cmock test_utils esp_system driver esp_timer) target_compile_options(${COMPONENT_LIB} PRIVATE "-Wno-format") diff --git a/components/freertos/test/integration/README.md b/components/freertos/test/integration/README.md new file mode 100644 index 0000000000..6eabb0d7d8 --- /dev/null +++ b/components/freertos/test/integration/README.md @@ -0,0 +1,73 @@ +# FreeRTOS Tests Guidelines + +The FreeRTOS tests are currently being refactored/reorganized with the goal of being upstreamed. This document describes the set of guidelines to which the tests are refactored/reorganized according to. + +## Unity Port + +These test cases assume that the FreeRTOS port has also ported the [Unity Test Framework](https://github.com/ThrowTheSwitch/Unity). Because each FreeRTOS test case will require the scheduler to be started, the way that each test case is invoked will differ form regular Unity ports. + +Regular Unity ports will assume that the `main()` function invokes each test using the `RUN_TEST()` macro. However, these test cases assume the following about the Unity port: + +- Each test case is invoked from a `UnityTask` instead of `main()`. Thus each test case is run from the context of the `UnityTask`. + - The `UnityTask` is created using `xTaskCreate...()` (and pinned to core 0 if SMP) from the port's startup (i.e., `main()`) + - The port's startup (i.e., `main()`) should also start the scheduler using `vTaskStartScheduler()` + - Note that this is similar to the startup of most FreeRTOS Demos. +- Each test case is defined using the `TEST_CASE(name, ...)` macro. The `VA_ARGS` of the macro allows each port to specify a set of extra arguments (such as test case labels/tags) to be used into their CI pipelines. +- A `portTestMacro.h` must be provided by each port. This header will contain + - Some constants used by test cases such as default task stack sizes (e.g., `configTEST_DEFAULT_STACK_SIZE`) + - Some port implementation specific functions/macros required by test cases such as getting current system time (e.g., `portTEST_GET_TIME()`). + +## Test Organization + +- Test cases are grouped into sub-directories roughly matching the header files of FreeRTOS (e.g., task, queue, semaphore, event groups etc). +- Each source file should ideally test a particular behavior (e.g., priority scheduling, queue send, scheduler suspend). This should usually result in one test case per behavior, thus one test case per source file +- Some test case behaviors may depend on configuration (e.g., priority scheduling in single core vs SMP). In such cases + - If the affect is small, use an `#if (config... == 1)` to wrap the affected areas + - If the affect is large, write a separate test case in a separate source file and wrap the entire test case with `#if (config... == 1)`. + +## Test Case Template + +Each test case should have the following: + +- Test case description describing + - Purpose of the test case + - Test case procedure + - Excepted outcome/behavior of the test case +- The test case code wrapped in its required `config...` macros +- The expected outcomes should be tested using the `TEST_ASSERT_...()` macros provided by unity + +```c +// In test_priority_scheduling.c + +/* +Test Priority Scheduling (Single Core) + +Purpose: + - Test that the single-core scheduler always schedules the highest priority ready task +Procedure: + - Raise the unityTask priority to (configMAX_PRIORITIES - 1) + - unityTask creates the following lower priority tasks + - task_A (configMAX_PRIORITIES - 2) + - task_B (configMAX_PRIORITIES - 3) + - UnityTask blocks for a short period of time to allow task_A to run + - Clean up and restore unityTask's original priority +Expected: + - task_A should run after unityTask blocks + - task_B should never have run +*/ +#if ( configNUM_CORES == 1 ) + +static BaseType_t test_static_var = 0; + +static void test_static_func(void) +{ + ... +} + +TEST_CASE("Tasks: Priority scheduling single core", "[freertos]") +{ + ... +} + +#endif /* configNUM_CORES == 1 */ +``` diff --git a/components/freertos/test/integration/portTestMacro.h b/components/freertos/test/integration/portTestMacro.h new file mode 100644 index 0000000000..42bfb2662c --- /dev/null +++ b/components/freertos/test/integration/portTestMacro.h @@ -0,0 +1,13 @@ +/* + * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#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()) From 882515fcef9ae4d668024bf66614291f0910a046 Mon Sep 17 00:00:00 2001 From: Darian Leung Date: Mon, 19 Sep 2022 20:20:19 +0800 Subject: [PATCH 2/3] freertos: Add priority scheduling unit tests This commit adds a priority scheduling unit test to test that the scheduler always schedules the highest priority ready state task. Single core and SMP variants of the test are both added. --- .../tasks/test_priority_scheduling.c | 82 ++++++++++++++ .../tasks/test_priority_scheduling_smp.c | 102 ++++++++++++++++++ 2 files changed, 184 insertions(+) create mode 100644 components/freertos/test/integration/tasks/test_priority_scheduling.c create mode 100644 components/freertos/test/integration/tasks/test_priority_scheduling_smp.c diff --git a/components/freertos/test/integration/tasks/test_priority_scheduling.c b/components/freertos/test/integration/tasks/test_priority_scheduling.c new file mode 100644 index 0000000000..a9b7e76fa8 --- /dev/null +++ b/components/freertos/test/integration/tasks/test_priority_scheduling.c @@ -0,0 +1,82 @@ +/* + * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "FreeRTOS.h" +#include "task.h" +#include "unity.h" +#include "portTestMacro.h" + +/* ------------------------------------------------------------------------------------------------------------------ */ + +/* +Test Priority Scheduling (Single Core) + +Purpose: + - Test that the single-core scheduler always schedules the highest priority ready task +Procedure: + - Raise the unityTask priority to (configMAX_PRIORITIES - 1) + - unityTask creates the following lower priority tasks + - task_A (configMAX_PRIORITIES - 2) + - task_B (configMAX_PRIORITIES - 3) + - UnityTask blocks for a short period of time to allow task_A to run + - Clean up and restore unityTask's original priority +Expected: + - task_A should run after unityTask blocks + - task_B should never have run +*/ + +#if ( configNUM_CORES == 1 ) + +#define UNITY_TASK_DELAY_TICKS 10 + +static BaseType_t task_A_ran; +static BaseType_t task_B_ran; + +static void task_A(void *arg) +{ + task_A_ran = pdTRUE; + /* Keeping spinning to prevent the lower priority task_B from running */ + while (1) { + ; + } +} + +static void task_B(void *arg) +{ + /* The following should never run due to task_B having a lower priority */ + task_B_ran = pdTRUE; + while (1) { + ; + } +} + +TEST_CASE("Tasks: Test priority scheduling", "[freertos]") +{ + TaskHandle_t task_A_handle; + TaskHandle_t task_B_handle; + task_A_ran = pdFALSE; + task_B_ran = pdFALSE; + + /* Raise the priority of the unityTask */ + vTaskPrioritySet(NULL, configMAX_PRIORITIES - 1); + /* Create task_A and task_B */ + xTaskCreate(task_A, "task_A", configTEST_DEFAULT_STACK_SIZE, (void *)xTaskGetCurrentTaskHandle(), configMAX_PRIORITIES - 2, &task_A_handle); + xTaskCreate(task_B, "task_B", configTEST_DEFAULT_STACK_SIZE, (void *)xTaskGetCurrentTaskHandle(), configMAX_PRIORITIES - 3, &task_B_handle); + + /* Block to allow task_A to be scheduled */ + vTaskDelay(UNITY_TASK_DELAY_TICKS); + + /* Test that only task_A has run */ + TEST_ASSERT_EQUAL(pdTRUE, task_A_ran); + TEST_ASSERT_EQUAL(pdFALSE, task_B_ran); + + vTaskDelete(task_A_handle); + vTaskDelete(task_B_handle); + /* Restore the priority of the unityTask */ + vTaskPrioritySet(NULL, configTEST_UNITY_TASK_PRIORITY); +} + +#endif /* configNUM_CORES == 1 */ diff --git a/components/freertos/test/integration/tasks/test_priority_scheduling_smp.c b/components/freertos/test/integration/tasks/test_priority_scheduling_smp.c new file mode 100644 index 0000000000..2f4b0cad9a --- /dev/null +++ b/components/freertos/test/integration/tasks/test_priority_scheduling_smp.c @@ -0,0 +1,102 @@ +/* + * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "sdkconfig.h" +#include +#include "FreeRTOS.h" +#include "task.h" +#include "semphr.h" +#include "unity.h" +#include "portTestMacro.h" + +/* ------------------------------------------------------------------------------------------------------------------ */ + +/* +Test Priority Scheduling SMP + +Purpose: + - Test that the SMP scheduler always schedules the highest priority ready tasks for each core +Procedure: + - Raise the unityTask priority to (configMAX_PRIORITIES - 1) + - unityTask creates the following lower priority tasks for each core + - task_A (configMAX_PRIORITIES - 2) for each core + - task_B (configMAX_PRIORITIES - 3) for each core + - unityTask blocks for a short period of time to allow all of the task_As to run + - Clean up and restore unityTask's original priority +Expected: + - All of the task_As should be run by the scheduler + - None of the task_Bs should have run +*/ + +#if ( defined( CONFIG_FREERTOS_SMP ) && ( configNUM_CORES > 1 ) && ( configRUN_MULTIPLE_PRIORITIES == 1 ) ) \ + || ( !defined( CONFIG_FREERTOS_SMP ) && ( configNUM_CORES > 1 ) ) + +#define UNITY_TASK_DELAY_TICKS 10 + +static BaseType_t task_A_ran[configNUM_CORES]; +static BaseType_t task_B_ran[configNUM_CORES]; + +static void task_A(void *arg) +{ + BaseType_t task_idx = (BaseType_t) arg; + task_A_ran[task_idx] = pdTRUE; + /* Keeping spinning to prevent the lower priority task_B from running */ + while (1) { + ; + } +} + +static void task_B(void *arg) +{ + /* The following should never be run due to task_B having a lower priority */ + BaseType_t task_idx = (BaseType_t) arg; + task_B_ran[task_idx] = pdTRUE; + while (1) { + ; + } +} + +TEST_CASE("Tasks: Test priority scheduling (SMP)", "[freertos]") +{ + TaskHandle_t task_A_handles[configNUM_CORES]; + TaskHandle_t task_B_handles[configNUM_CORES]; + memset(task_A_ran, pdFALSE, sizeof(task_A_ran)); + memset(task_B_ran, pdFALSE, sizeof(task_B_ran)); + + /* Raise the priority of the unityTask */ + vTaskPrioritySet(NULL, configMAX_PRIORITIES - 1); + + /* Create task_A for each core */ + for (UBaseType_t x = 0; x < configNUM_CORES; x++) { + xTaskCreate(task_A, "task_A", configTEST_DEFAULT_STACK_SIZE, (void *)x, configMAX_PRIORITIES - 2, &task_A_handles[x]); + } + + /* Create task_B for each core */ + for (UBaseType_t x = 0; x < configNUM_CORES; x++) { + xTaskCreate(task_B, "task_B", configTEST_DEFAULT_STACK_SIZE, (void *)x, configMAX_PRIORITIES - 3, &task_B_handles[x]); + } + + /* Block to ensure all the task_As to be scheduled */ + vTaskDelay(UNITY_TASK_DELAY_TICKS); + + /* Check that all the task_As have run, and none of the task_Bs have run */ + for (UBaseType_t x = 0; x < configNUM_CORES; x++) { + TEST_ASSERT_EQUAL(pdTRUE, task_A_ran[x]); + TEST_ASSERT_EQUAL(pdFALSE, task_B_ran[x]); + } + + /* Cleanup */ + for (UBaseType_t x = 0; x < configNUM_CORES; x++) { + vTaskDelete(task_A_handles[x]); + vTaskDelete(task_B_handles[x]); + } + + /* Restore the priority of the unityTask */ + vTaskPrioritySet(NULL, configTEST_UNITY_TASK_PRIORITY); +} + +#endif /* ( defined( CONFIG_FREERTOS_SMP ) && ( configNUM_CORES > 1 ) && ( configRUN_MULTIPLE_PRIORITIES == 1 ) ) + || ( !defined( CONFIG_FREERTOS_SMP ) && ( configNUM_CORES > 1 ) ) */ From c126b910b685c24316c2cbb62cdc34c408192196 Mon Sep 17 00:00:00 2001 From: Darian Leung Date: Mon, 19 Sep 2022 20:52:54 +0800 Subject: [PATCH 3/3] freertos: Expand CONFIG_FREERTOS_SMP description This commit adds more details to the CONFIG_FREERTOS_SMP option's description. --- components/freertos/Kconfig | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/components/freertos/Kconfig b/components/freertos/Kconfig index 7927e5cf75..a97e5ad06e 100644 --- a/components/freertos/Kconfig +++ b/components/freertos/Kconfig @@ -4,11 +4,19 @@ menu "FreeRTOS" # Upstream FreeRTOS configurations go here config FREERTOS_SMP - bool "Run the SMP FreeRTOS kernel instead (FEATURE UNDER DEVELOPMENT)" + bool "Run the Amazon SMP FreeRTOS kernel instead (FEATURE UNDER DEVELOPMENT)" default "n" help - This will cause the FreeRTOS component to compile with the SMP FreeRTOS kernel instead. - THIS FEATURE IS UNDER ACTIVE DEVELOPMENT, users use this at their own risk. + Amazon has released an SMP version of the FreeRTOS Kernel which can be found via the following link: + https://github.com/FreeRTOS/FreeRTOS-Kernel/tree/smp + + IDF has added an experimental port of this SMP kernel located in + components/freertos/FreeRTOS-Kernel-SMP. Enabling this option will cause IDF to use the Amazon SMP + kernel. Note that THIS FEATURE IS UNDER ACTIVE DEVELOPMENT, users use this at their own risk. + + Leaving this option disabled will mean the IDF FreeRTOS kernel is used instead, which is located in: + components/freertos/FreeRTOS-Kernel. Both kernel versions are SMP capable, but differ in + their implementation and features. config FREERTOS_UNICORE # Todo: Replace with CONFIG_NUM_CORES (IDF-4986)