mirror of
				https://github.com/espressif/esp-idf.git
				synced 2025-10-27 11:58:55 +00:00 
			
		
		
		
	 e9cbf56d36
			
		
	
	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))
 |