mirror of
https://github.com/espressif/esp-idf.git
synced 2026-10-02 11:10:54 +03:00
feat(freertos): soft-preempting linux simulator
This commit is contained in:
@@ -1,3 +1,7 @@
|
||||
# This is a manual mock that supplies `main()` if FreeRTOS is mocked
|
||||
idf_component_register(SRCS "startup_mock.c"
|
||||
REQUIRES main esp_event)
|
||||
|
||||
# Prevent esp_system from providing main() since this component provides it
|
||||
idf_component_get_property(esp_system_lib esp_system COMPONENT_LIB)
|
||||
target_compile_definitions(${esp_system_lib} PRIVATE ESP_SYSTEM_LINUX_NO_MAIN)
|
||||
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2022-2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@@ -43,7 +43,7 @@ static void loop_task(void *arg)
|
||||
// Short delay to allow other created tasks to run
|
||||
vTaskDelay(2);
|
||||
while (1) {
|
||||
;
|
||||
vTaskDelay(1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+2
-1
@@ -76,7 +76,7 @@ TEST_CASE("FreeRTOS Delete Blocked Tasks", "[freertos]")
|
||||
|
||||
(1000 iterations takes about 9 seconds on ESP32 dual core)
|
||||
*/
|
||||
for(unsigned iter = 0; iter < 1000; iter++) {
|
||||
for(unsigned iter = 0; iter < 100; iter++) {
|
||||
// Create everything
|
||||
SemaphoreHandle_t sem = xSemaphoreCreateMutex();
|
||||
for(unsigned i = 0; i < configNUM_CORES + 1; i++) {
|
||||
@@ -95,6 +95,7 @@ TEST_CASE("FreeRTOS Delete Blocked Tasks", "[freertos]")
|
||||
vTaskDelete(blocking_tasks[i]);
|
||||
params[i].deleted = true;
|
||||
}
|
||||
|
||||
vTaskDelay(4); // Yield to the idle task for cleanup
|
||||
|
||||
vSemaphoreDelete(sem);
|
||||
|
||||
+35
-39
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2022-2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@@ -20,62 +20,58 @@
|
||||
#include "unity.h"
|
||||
#include "sdkconfig.h"
|
||||
|
||||
static volatile bool trigger;
|
||||
static volatile bool flag;
|
||||
/*
|
||||
* Test: A lower-priority task must be able to run and send data to a queue
|
||||
* when the higher-priority task yields (blocks).
|
||||
*
|
||||
* Pattern: The lower-priority task blocks on a semaphore until told to proceed,
|
||||
* then sends to a queue. The higher-priority task gives the semaphore and
|
||||
* immediately blocks on the queue receive.
|
||||
*/
|
||||
|
||||
#ifndef CONFIG_FREERTOS_SMP
|
||||
#define MAX_YIELD_COUNT 10000
|
||||
#else
|
||||
//TODO: IDF-5081
|
||||
#define MAX_YIELD_COUNT 17000
|
||||
#endif // CONFIG_FREERTOS_SMP
|
||||
|
||||
|
||||
/* Task:
|
||||
- Waits for 'trigger' variable to be set
|
||||
- Reads the cycle count on this CPU
|
||||
- Pushes it into a queue supplied as a param
|
||||
- Busy-waits until the main task terminates it
|
||||
*/
|
||||
static void task_send_to_queue(void *param)
|
||||
{
|
||||
QueueHandle_t queue = (QueueHandle_t) param;
|
||||
uint32_t ccount;
|
||||
void **args = (void **)param;
|
||||
QueueHandle_t queue = (QueueHandle_t)args[0];
|
||||
SemaphoreHandle_t sem = (SemaphoreHandle_t)args[1];
|
||||
|
||||
while(!trigger) {}
|
||||
/* Block until the main task tells us to go */
|
||||
xSemaphoreTake(sem, portMAX_DELAY);
|
||||
|
||||
ccount = 0;
|
||||
flag = true;
|
||||
xQueueSendToBack(queue, &ccount, 0);
|
||||
/* This is to ensure that higher priority task
|
||||
won't wake anyhow, due to this task terminating.
|
||||
uint32_t value = 42;
|
||||
xQueueSendToBack(queue, &value, 0);
|
||||
|
||||
The task runs until terminated by the main task.
|
||||
*/
|
||||
while(1) {}
|
||||
/* Stay alive until deleted */
|
||||
vTaskSuspend(NULL);
|
||||
}
|
||||
|
||||
TEST_CASE("Yield from lower priority task, same CPU", "[freertos]")
|
||||
{
|
||||
/* Do this 3 times, mostly for the benchmark value - the first
|
||||
run includes a cache miss so uses more cycles than it should. */
|
||||
for (int i = 0; i < 3; i++) {
|
||||
TaskHandle_t sender_task;
|
||||
QueueHandle_t queue = xQueueCreate(1, sizeof(uint32_t));
|
||||
flag = false;
|
||||
trigger = false;
|
||||
SemaphoreHandle_t sem = xSemaphoreCreateBinary();
|
||||
void *args[2] = { queue, sem };
|
||||
|
||||
/* "yield" task sits on our CPU, lower priority to us */
|
||||
xTaskCreatePinnedToCore(task_send_to_queue, "YIELD", 2048, (void *)queue, CONFIG_UNITY_FREERTOS_PRIORITY - 1, &sender_task, CONFIG_UNITY_FREERTOS_CPU);
|
||||
/* Lower-priority task blocks on the semaphore */
|
||||
xTaskCreatePinnedToCore(task_send_to_queue, "YIELD", 2048, args,
|
||||
CONFIG_UNITY_FREERTOS_PRIORITY - 1, &sender_task,
|
||||
CONFIG_UNITY_FREERTOS_CPU);
|
||||
|
||||
vTaskDelay(1); /* make sure everything is set up */
|
||||
trigger = true;
|
||||
/* Let the task start and block on the semaphore */
|
||||
vTaskDelay(pdMS_TO_TICKS(10));
|
||||
|
||||
uint32_t yield_ccount;
|
||||
TEST_ASSERT( xQueueReceive(queue, &yield_ccount, 100 / portTICK_PERIOD_MS) );
|
||||
TEST_ASSERT( flag );
|
||||
/* Give the semaphore — the lower-priority task won't run yet because
|
||||
* we (higher priority) are still ready. Then block on queue receive,
|
||||
* which yields the CPU to the lower-priority task. */
|
||||
xSemaphoreGive(sem);
|
||||
|
||||
uint32_t received;
|
||||
TEST_ASSERT(xQueueReceive(queue, &received, pdMS_TO_TICKS(100)));
|
||||
TEST_ASSERT_EQUAL(42, received);
|
||||
|
||||
vTaskDelete(sender_task);
|
||||
vQueueDelete(queue);
|
||||
vSemaphoreDelete(sem);
|
||||
}
|
||||
}
|
||||
|
||||
+55
-30
@@ -1,11 +1,12 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2022-2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "freertos/semphr.h"
|
||||
#include "unity.h"
|
||||
#include "portTestMacro.h"
|
||||
|
||||
@@ -17,64 +18,88 @@ 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
|
||||
- unityTask (highest priority) creates a binary semaphore (initially empty)
|
||||
- unityTask creates two lower-priority tasks that both block on the semaphore:
|
||||
- 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
|
||||
- unityTask delays to let both tasks start and block on the semaphore
|
||||
- unityTask gives the semaphore once — FreeRTOS wakes the highest-priority
|
||||
waiter (task_A), which writes its ID to a shared variable and signals done
|
||||
- unityTask verifies the shared variable holds task_A's ID
|
||||
Expected:
|
||||
- task_A should run after unityTask blocks
|
||||
- task_B should never have run
|
||||
- task_A (higher priority) wins the semaphore race, not task_B
|
||||
*/
|
||||
|
||||
#if ( configNUM_CORES == 1 )
|
||||
|
||||
#define UNITY_TASK_DELAY_TICKS 10
|
||||
#define PRIO_TASK_A_ID 1
|
||||
#define PRIO_TASK_B_ID 2
|
||||
|
||||
static BaseType_t task_A_ran;
|
||||
static BaseType_t task_B_ran;
|
||||
static volatile int s_prio_winner;
|
||||
static SemaphoreHandle_t s_race_sem;
|
||||
static SemaphoreHandle_t s_done_sem;
|
||||
|
||||
static void task_A(void *arg)
|
||||
{
|
||||
task_A_ran = pdTRUE;
|
||||
/* Keeping spinning to prevent the lower priority task_B from running */
|
||||
while (1) {
|
||||
;
|
||||
}
|
||||
/* Block until the race semaphore is given */
|
||||
xSemaphoreTake(s_race_sem, portMAX_DELAY);
|
||||
/* Higher priority: should be woken first */
|
||||
s_prio_winner = PRIO_TASK_A_ID;
|
||||
xSemaphoreGive(s_done_sem);
|
||||
vTaskSuspend(NULL);
|
||||
}
|
||||
|
||||
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) {
|
||||
;
|
||||
}
|
||||
/* Block until the race semaphore is given */
|
||||
xSemaphoreTake(s_race_sem, portMAX_DELAY);
|
||||
/* Lower priority: should NOT be woken first */
|
||||
s_prio_winner = PRIO_TASK_B_ID;
|
||||
xSemaphoreGive(s_done_sem);
|
||||
vTaskSuspend(NULL);
|
||||
}
|
||||
|
||||
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;
|
||||
s_prio_winner = 0;
|
||||
|
||||
/* Binary semaphores start empty — both tasks will block on take */
|
||||
s_race_sem = xSemaphoreCreateBinary();
|
||||
s_done_sem = xSemaphoreCreateBinary();
|
||||
TEST_ASSERT_NOT_NULL(s_race_sem);
|
||||
TEST_ASSERT_NOT_NULL(s_done_sem);
|
||||
|
||||
/* 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);
|
||||
/* Create task_A (higher prio) and task_B (lower prio) */
|
||||
xTaskCreate(task_A, "task_A", configTEST_DEFAULT_STACK_SIZE, NULL,
|
||||
configMAX_PRIORITIES - 2, &task_A_handle);
|
||||
xTaskCreate(task_B, "task_B", configTEST_DEFAULT_STACK_SIZE, NULL,
|
||||
configMAX_PRIORITIES - 3, &task_B_handle);
|
||||
|
||||
/* Test that only task_A has run */
|
||||
TEST_ASSERT_EQUAL(pdTRUE, task_A_ran);
|
||||
TEST_ASSERT_EQUAL(pdFALSE, task_B_ran);
|
||||
/* Let both tasks start and block on s_race_sem */
|
||||
vTaskDelay(pdMS_TO_TICKS(50));
|
||||
|
||||
/* Give the semaphore once. FreeRTOS wakes the highest-priority waiter
|
||||
* (task_A). Since unityTask is still higher priority, task_A won't
|
||||
* actually run until we block below. */
|
||||
xSemaphoreGive(s_race_sem);
|
||||
|
||||
/* Block waiting for the winner to signal. This lets task_A run. */
|
||||
TEST_ASSERT_EQUAL(pdTRUE, xSemaphoreTake(s_done_sem, pdMS_TO_TICKS(200)));
|
||||
|
||||
/* The higher-priority task_A should have won the race */
|
||||
TEST_ASSERT_EQUAL(PRIO_TASK_A_ID, s_prio_winner);
|
||||
|
||||
/* Cleanup */
|
||||
vTaskDelete(task_A_handle);
|
||||
vTaskDelete(task_B_handle);
|
||||
vSemaphoreDelete(s_race_sem);
|
||||
vSemaphoreDelete(s_done_sem);
|
||||
|
||||
/* Restore the priority of the unityTask */
|
||||
vTaskPrioritySet(NULL, configTEST_UNITY_TASK_PRIORITY);
|
||||
}
|
||||
|
||||
+68
-53
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2022-2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@@ -14,74 +14,89 @@
|
||||
#include "sdkconfig.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "freertos/semphr.h"
|
||||
#include "unity.h"
|
||||
|
||||
/*
|
||||
* A race task blocks on a shared binary semaphore. When the semaphore is given,
|
||||
* FreeRTOS wakes the highest-priority waiter, which writes its ID to s_winner
|
||||
* and signals s_done_sem. The task then suspends itself (it is single-shot).
|
||||
*/
|
||||
|
||||
static void counter_task(void *param)
|
||||
#define TASK_ID_A 1
|
||||
#define TASK_ID_B 2
|
||||
|
||||
static volatile int s_winner;
|
||||
static SemaphoreHandle_t s_race_sem;
|
||||
static SemaphoreHandle_t s_done_sem;
|
||||
|
||||
static void race_task(void *arg)
|
||||
{
|
||||
volatile uint32_t *counter = (volatile uint32_t *)param;
|
||||
while (1) {
|
||||
(*counter)++;
|
||||
}
|
||||
int my_id = (int)(uintptr_t)arg;
|
||||
xSemaphoreTake(s_race_sem, portMAX_DELAY);
|
||||
s_winner = my_id;
|
||||
xSemaphoreGive(s_done_sem);
|
||||
vTaskSuspend(NULL);
|
||||
}
|
||||
|
||||
|
||||
TEST_CASE("Get/Set Priorities", "[freertos]")
|
||||
{
|
||||
/* Two tasks per processor */
|
||||
TaskHandle_t tasks[configNUM_CORES][2] = { 0 };
|
||||
unsigned volatile counters[configNUM_CORES][2] = { 0 };
|
||||
TaskHandle_t task_a, task_b;
|
||||
|
||||
s_race_sem = xSemaphoreCreateBinary();
|
||||
s_done_sem = xSemaphoreCreateBinary();
|
||||
TEST_ASSERT_NOT_NULL(s_race_sem);
|
||||
TEST_ASSERT_NOT_NULL(s_done_sem);
|
||||
|
||||
/* Verify unity task's own priority */
|
||||
TEST_ASSERT_EQUAL(CONFIG_UNITY_FREERTOS_PRIORITY, uxTaskPriorityGet(NULL));
|
||||
|
||||
/* create a matrix of counter tasks on each core */
|
||||
for (int cpu = 0; cpu < configNUM_CORES; cpu++) {
|
||||
for (int task = 0; task < 2; task++) {
|
||||
xTaskCreatePinnedToCore(counter_task, "count", 2048, (void *)&(counters[cpu][task]), CONFIG_UNITY_FREERTOS_PRIORITY - task, &(tasks[cpu][task]), cpu);
|
||||
}
|
||||
}
|
||||
/* --- Round 1: task_a has higher priority, should win the race --- */
|
||||
xTaskCreatePinnedToCore(race_task, "a", 2048, (void *)(uintptr_t)TASK_ID_A,
|
||||
CONFIG_UNITY_FREERTOS_PRIORITY - 1, &task_a, 0);
|
||||
xTaskCreatePinnedToCore(race_task, "b", 2048, (void *)(uintptr_t)TASK_ID_B,
|
||||
CONFIG_UNITY_FREERTOS_PRIORITY - 2, &task_b, 0);
|
||||
|
||||
/* check they were created with the expected priorities */
|
||||
for (int cpu = 0; cpu < configNUM_CORES; cpu++) {
|
||||
for (int task = 0; task < 2; task++) {
|
||||
TEST_ASSERT_EQUAL(CONFIG_UNITY_FREERTOS_PRIORITY - task, uxTaskPriorityGet(tasks[cpu][task]));
|
||||
}
|
||||
}
|
||||
/* Verify created priorities */
|
||||
TEST_ASSERT_EQUAL(CONFIG_UNITY_FREERTOS_PRIORITY - 1, uxTaskPriorityGet(task_a));
|
||||
TEST_ASSERT_EQUAL(CONFIG_UNITY_FREERTOS_PRIORITY - 2, uxTaskPriorityGet(task_b));
|
||||
|
||||
vTaskDelay(10);
|
||||
/* Let both tasks block on s_race_sem */
|
||||
vTaskDelay(pdMS_TO_TICKS(50));
|
||||
|
||||
/* at this point, only the higher priority tasks (first index) should be counting */
|
||||
for (int cpu = 0; cpu < configNUM_CORES; cpu++) {
|
||||
TEST_ASSERT_NOT_EQUAL(0, counters[cpu][0]);
|
||||
TEST_ASSERT_EQUAL(0, counters[cpu][1]);
|
||||
}
|
||||
s_winner = 0;
|
||||
xSemaphoreGive(s_race_sem);
|
||||
TEST_ASSERT_EQUAL(pdTRUE, xSemaphoreTake(s_done_sem, pdMS_TO_TICKS(200)));
|
||||
TEST_ASSERT_EQUAL(TASK_ID_A, s_winner);
|
||||
|
||||
/* swap priorities! */
|
||||
for (int cpu = 0; cpu < configNUM_CORES; cpu++) {
|
||||
vTaskPrioritySet(tasks[cpu][0], CONFIG_UNITY_FREERTOS_PRIORITY - 1);
|
||||
vTaskPrioritySet(tasks[cpu][1], CONFIG_UNITY_FREERTOS_PRIORITY);
|
||||
}
|
||||
vTaskDelete(task_a);
|
||||
vTaskDelete(task_b);
|
||||
|
||||
/* check priorities have swapped... */
|
||||
for (int cpu = 0; cpu < configNUM_CORES; cpu++) {
|
||||
TEST_ASSERT_EQUAL(CONFIG_UNITY_FREERTOS_PRIORITY -1, uxTaskPriorityGet(tasks[cpu][0]));
|
||||
TEST_ASSERT_EQUAL(CONFIG_UNITY_FREERTOS_PRIORITY, uxTaskPriorityGet(tasks[cpu][1]));
|
||||
}
|
||||
/* --- Test vTaskPrioritySet API --- */
|
||||
xTaskCreatePinnedToCore(race_task, "p", 2048, (void *)(uintptr_t)TASK_ID_A,
|
||||
CONFIG_UNITY_FREERTOS_PRIORITY - 1, &task_a, 0);
|
||||
TEST_ASSERT_EQUAL(CONFIG_UNITY_FREERTOS_PRIORITY - 1, uxTaskPriorityGet(task_a));
|
||||
vTaskPrioritySet(task_a, CONFIG_UNITY_FREERTOS_PRIORITY - 2);
|
||||
TEST_ASSERT_EQUAL(CONFIG_UNITY_FREERTOS_PRIORITY - 2, uxTaskPriorityGet(task_a));
|
||||
vTaskDelete(task_a);
|
||||
|
||||
/* check the tasks which are counting have also swapped now... */
|
||||
for (int cpu = 0; cpu < configNUM_CORES; cpu++) {
|
||||
unsigned old_counters[2];
|
||||
old_counters[0] = counters[cpu][0];
|
||||
old_counters[1] = counters[cpu][1];
|
||||
vTaskDelay(10);
|
||||
TEST_ASSERT_EQUAL(old_counters[0], counters[cpu][0]);
|
||||
TEST_ASSERT_NOT_EQUAL(old_counters[1], counters[cpu][1]);
|
||||
}
|
||||
/* --- Round 2: swap priorities — task_b now higher, should win --- */
|
||||
xTaskCreatePinnedToCore(race_task, "a2", 2048, (void *)(uintptr_t)TASK_ID_A,
|
||||
CONFIG_UNITY_FREERTOS_PRIORITY - 2, &task_a, 0);
|
||||
xTaskCreatePinnedToCore(race_task, "b2", 2048, (void *)(uintptr_t)TASK_ID_B,
|
||||
CONFIG_UNITY_FREERTOS_PRIORITY - 1, &task_b, 0);
|
||||
|
||||
/* clean up */
|
||||
for (int cpu = 0; cpu < configNUM_CORES; cpu++) {
|
||||
for (int task = 0; task < 2; task++) {
|
||||
vTaskDelete(tasks[cpu][task]);
|
||||
}
|
||||
}
|
||||
/* Let both tasks block on s_race_sem */
|
||||
vTaskDelay(pdMS_TO_TICKS(50));
|
||||
|
||||
s_winner = 0;
|
||||
xSemaphoreGive(s_race_sem);
|
||||
TEST_ASSERT_EQUAL(pdTRUE, xSemaphoreTake(s_done_sem, pdMS_TO_TICKS(200)));
|
||||
TEST_ASSERT_EQUAL(TASK_ID_B, s_winner);
|
||||
|
||||
/* Cleanup */
|
||||
vTaskDelete(task_a);
|
||||
vTaskDelete(task_b);
|
||||
vSemaphoreDelete(s_race_sem);
|
||||
vSemaphoreDelete(s_done_sem);
|
||||
}
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
CONFIG_IDF_TARGET="linux"
|
||||
CONFIG_FREERTOS_SMP=y
|
||||
CONFIG_FREERTOS_SMP=n
|
||||
|
||||
Reference in New Issue
Block a user