mirror of
https://github.com/espressif/esp-idf.git
synced 2024-10-05 20:47:46 -04:00
e9cbf56d36
The previous SMP freertos round robin would skip over tasks when time slicing. This commit implements a Best Effort Round Robin where selected tasks are put to the back of the list, thus makes the time slicing more fair. - Documentation has been updated accordingly. - Tidy up vTaskSwitchContext() to match v10.4.3 more - Increased esp_ipc task stack size to avoid overflow Closes https://github.com/espressif/esp-idf/issues/7256
178 lines
6.9 KiB
C
178 lines
6.9 KiB
C
// Copyright 2015-2021 Espressif Systems (Shanghai) PTE LTD
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
#include <stdbool.h>
|
|
#include "freertos/FreeRTOS.h"
|
|
#include "freertos/task.h"
|
|
#include "freertos/queue.h"
|
|
#include "esp_rom_sys.h"
|
|
#include "hal/interrupt_controller_hal.h"
|
|
#include "unity.h"
|
|
#include "test_utils.h"
|
|
|
|
/*
|
|
Test Best Effort Round Robin Scheduling:
|
|
|
|
The following test case tests the "Best Effort Round Robin Scheduling" that fixes the skipping behavior found in older
|
|
versions of the ESP-IDF SMP FreeRTOS (see docs for more details about Best Effort Round Robin Scheduling).
|
|
|
|
This test...
|
|
- Only runs under dual core configuration
|
|
- Will disable the tick interrupts of both cores
|
|
|
|
Test flow as follows:
|
|
|
|
1. Stop preemption on core 0 by raising the priority of the unity task
|
|
2. Stop preemption on core 0 by creating a blocker task
|
|
3. Disable tick interrupts on both cores
|
|
4. Create N spin tasks on each core, each with a sequential task_code
|
|
5. Unblock those spin tasks in a sequential order
|
|
6. Lower priority of unity task and stop the blocker task so that spin tasks are run
|
|
7. Each time a spin task is run (i.e., an iteration) it will send its task code to a queue
|
|
8. Spin tasks will clean themselves up
|
|
9. The queue should contain the task codes of the spin tasks in the order they were started in, thus showing that round
|
|
robin schedules the tasks in a sequential order.
|
|
*/
|
|
|
|
#if !defined(CONFIG_FREERTOS_UNICORE) && (defined(CONFIG_FREERTOS_CORETIMER_0) || defined(CONFIG_FREERTOS_CORETIMER_1))
|
|
|
|
#define SPIN_TASK_PRIO (CONFIG_UNITY_FREERTOS_PRIORITY + 1)
|
|
#define SPIN_TASK_NUM_ITER 3
|
|
#define TASK_STACK_SIZE 1024
|
|
#define NUM_PINNED_SPIN_TASK_PER_CORE 3
|
|
#if defined(CONFIG_FREERTOS_CORETIMER_0)
|
|
#define TICK_INTR_IDX 6
|
|
#else //defined(CONFIG_FREERTOS_CORETIMER_1)
|
|
#define TICK_INTR_IDX 15
|
|
#endif
|
|
|
|
static QueueHandle_t core0_run_order_queue;
|
|
static QueueHandle_t core1_run_order_queue;
|
|
static uint32_t total_iter_count[portNUM_PROCESSORS] = {0};
|
|
|
|
static void spin_task(void *arg)
|
|
{
|
|
uint32_t task_code = (uint32_t)arg;
|
|
QueueHandle_t run_order_queue = ((task_code >> 4) == 0) ? core0_run_order_queue : core1_run_order_queue;
|
|
|
|
//Wait to be started
|
|
ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
|
|
|
|
for (int i = 0; i < SPIN_TASK_NUM_ITER; i++) {
|
|
xQueueSend(run_order_queue, &task_code, 0);
|
|
//No need for critical sections as tick interrupt is disabled
|
|
total_iter_count[xPortGetCoreID()]++;
|
|
taskYIELD();
|
|
}
|
|
|
|
//Last iteration of the last spin task on this core. Reenable this core's tick interrupt
|
|
if (total_iter_count[xPortGetCoreID()] == (NUM_PINNED_SPIN_TASK_PER_CORE * SPIN_TASK_NUM_ITER)) {
|
|
interrupt_controller_hal_enable_interrupts(1 <<TICK_INTR_IDX);
|
|
}
|
|
vTaskDelete(NULL);
|
|
}
|
|
|
|
static void blocker_task(void *arg)
|
|
{
|
|
volatile bool *exit_loop = (volatile bool *)arg;
|
|
|
|
//Disable tick interrupts on core 1 the duration of the test
|
|
taskDISABLE_INTERRUPTS();
|
|
interrupt_controller_hal_disable_interrupts(1 << TICK_INTR_IDX);
|
|
taskENABLE_INTERRUPTS();
|
|
|
|
while (!*exit_loop) {
|
|
;
|
|
}
|
|
|
|
//Wait to be resumed
|
|
vTaskSuspend(NULL);
|
|
|
|
//Reenable tick interrupt on core 1
|
|
taskDISABLE_INTERRUPTS();
|
|
interrupt_controller_hal_enable_interrupts(1 << TICK_INTR_IDX);
|
|
taskENABLE_INTERRUPTS();
|
|
|
|
vTaskDelete(NULL);
|
|
}
|
|
|
|
TEST_CASE("Test FreeRTOS Scheduling Round Robin", "[freertos]")
|
|
{
|
|
core0_run_order_queue = xQueueCreate(SPIN_TASK_NUM_ITER * NUM_PINNED_SPIN_TASK_PER_CORE, sizeof(uint32_t));
|
|
core1_run_order_queue = xQueueCreate(SPIN_TASK_NUM_ITER * NUM_PINNED_SPIN_TASK_PER_CORE, sizeof(uint32_t));
|
|
|
|
/* Increase priority of unity task so that the spin tasks don't preempt us
|
|
during task creation. */
|
|
vTaskPrioritySet(NULL, SPIN_TASK_PRIO + 1);
|
|
/* Create a task on core 1 of the same priority to block core 1 */
|
|
volatile bool suspend_blocker = false;
|
|
TaskHandle_t blocker_task_hdl;
|
|
xTaskCreatePinnedToCore(blocker_task, "blk", TASK_STACK_SIZE, (void *)&suspend_blocker, SPIN_TASK_PRIO + 1, &blocker_task_hdl, 1);
|
|
|
|
//Disable tick interrupts on core 0 the duration of the test
|
|
taskDISABLE_INTERRUPTS();
|
|
interrupt_controller_hal_disable_interrupts(1 << TICK_INTR_IDX);
|
|
taskENABLE_INTERRUPTS();
|
|
|
|
TaskHandle_t core0_task_hdls[NUM_PINNED_SPIN_TASK_PER_CORE];
|
|
TaskHandle_t core1_task_hdls[NUM_PINNED_SPIN_TASK_PER_CORE];
|
|
|
|
for (int i = 0; i < NUM_PINNED_SPIN_TASK_PER_CORE; i++) {
|
|
//Create a spin task pinned to core 0
|
|
xTaskCreatePinnedToCore(spin_task, "spin", TASK_STACK_SIZE, (void *)(0x00 + i), SPIN_TASK_PRIO, &core0_task_hdls[i], 0);
|
|
//Create a spin task pinned to core 1
|
|
xTaskCreatePinnedToCore(spin_task, "spin", TASK_STACK_SIZE, (void *)(0x10 + i), SPIN_TASK_PRIO, &core1_task_hdls[i], 1);
|
|
}
|
|
|
|
/* Start the tasks in a particular order. This order should be reflected in
|
|
in the round robin scheduling on each core */
|
|
for (int i = 0; i < NUM_PINNED_SPIN_TASK_PER_CORE; i++) {
|
|
//Start a spin task on core 0
|
|
xTaskNotifyGive(core0_task_hdls[i]);
|
|
//Start a spin task on core 1
|
|
xTaskNotifyGive(core1_task_hdls[i]);
|
|
}
|
|
|
|
//Lower priority of this task and stop blocker task to allow the spin tasks to be scheduled
|
|
suspend_blocker = true;
|
|
vTaskPrioritySet(NULL, UNITY_FREERTOS_PRIORITY);
|
|
|
|
//Give a enough delay to allow all iterations of the round robin to occur
|
|
esp_rom_delay_us(10000);
|
|
|
|
for (int i = 0; i < SPIN_TASK_NUM_ITER; i++) {
|
|
for (int j = 0; j < NUM_PINNED_SPIN_TASK_PER_CORE; j++) {
|
|
uint32_t core0_entry;
|
|
uint32_t core1_entry;
|
|
TEST_ASSERT_EQUAL(pdTRUE, xQueueReceive(core0_run_order_queue, &core0_entry, 0));
|
|
TEST_ASSERT_EQUAL(pdTRUE, xQueueReceive(core1_run_order_queue, &core1_entry, 0));
|
|
TEST_ASSERT_EQUAL(0x00 + j, core0_entry);
|
|
TEST_ASSERT_EQUAL(0x10 + j, core1_entry);
|
|
}
|
|
}
|
|
|
|
//Resume the blocker task for cleanup
|
|
vTaskResume(blocker_task_hdl);
|
|
//Reenable tick interrupt on core 0
|
|
taskDISABLE_INTERRUPTS();
|
|
interrupt_controller_hal_enable_interrupts(1 << TICK_INTR_IDX);
|
|
taskENABLE_INTERRUPTS();
|
|
vTaskDelay(10); //Wait for blocker task to clean up
|
|
//Clean up queues
|
|
vQueueDelete(core0_run_order_queue);
|
|
vQueueDelete(core1_run_order_queue);
|
|
}
|
|
|
|
#endif //!defined(CONFIG_FREERTOS_UNICORE) && (efined(CONFIG_FREERTOS_CORETIMER_0) || defined(CONFIG_FREERTOS_CORETIMER_1))
|