This commit adds the ability to set the core affinity for the FreeRTOS Timer Service task when multi-core configuration is enabled. This commit also adds a Kconfig option to set the core affinity of the timer task via the menuconfig. Closes https://github.com/espressif/esp-idf/issues/10315
9.8 KiB
ESP-IDF Changes
This document is used to track all changes made the to FreeRTOS V10.5.1 source code when adding dual core SMP support or IDF additional features.
Todo: Add these to ESP-IDF docs once v10.5.1 becomes default kernel (IDF-8203)
License Headers
- Added
SPDX-FileCopyrightText
andSPDX-FileContributor
tags to all files to pass ESP-IDF pre-commit checks. - Changed kernel version tag to
FreeRTOS Kernel V10.5.1 (ESP-IDF SMP modified)
in all files to indicate files are different from upstream V10.5.1.
Omitted Features
- Removed
croutine.c
andcroutine.h
Changes from Upstream Main Branch not Included in v10.5.1
- Added ...GetStaticBuffers functions that were upstreamed but not included in v10.5.1
Kernel SMP Changes
List of changes to the Vanilla FreeRTOS V10.5.1 kernel in order to support dual-core SMP
Scheduling Behavior Changes
- The kernel now executes two tasks concurrently
- The kernel now creates two IDLE tasks (pinned to each core)
- Tasks can be pinned to either core, or have no affinity (can run on both cores)
- Each core receives a tick interrupt, but only core 0 increments the tick count and unblocks timed out tasks
- Core 0 calls
xTaskIncrementTick()
- Core 1 calls
xTaskIncrementTickOtherCores()
- Core 0 calls
- Each core independently calls
vTaskSwitchContext()
to pick the highest priority task it can currently run- In single-core scheduling algorithm
taskSELECT_HIGHEST_PRIORITY_TASK()
unchanged - In SMP,
prvSelectHighestPriorityTaskSMP()
is called. This will select the highest priority ready state task that...- Has a compatible core affinity
- Is not being run by another core
- In single-core scheduling algorithm
- Each core can suspend scheduling independently (i.e.,
vTaskSuspendAll()
)
Configuration
Following configurations have been added
- Added
configNUMBER_OF_CORES
to specify the number of cores to build. Can be1
for vanilla, or2
for SMP, error otherwise - Disable
configUSE_PORT_OPTIMISED_TASK_SELECTION
for SMP
Data Structure Changes (tasks.c
)
The following data fields have been expanded to have configNUMBER_OF_CORES
copies:
pxCurrentTCBs
: Each core now has its own currently running taskxPendingReadyList
: Each core has its own list to pend ready tasks if the scheduler is suspended on the corexYieldPending
: Each core has its own flag to track whether it has a pending yieldxIdleTaskHandle
: Each core now has its own idle taskuxSchedulerSuspended
: Each core can independently suspend scheduling on its coreulTaskSwitchedInTime
: Each core tracks its own "task switched in" time
Their access is now indexed by a xCoreID
if in SMP, or set to 0
in single core.
The following data structures have been added:
TCB_t.xCoreID
: All tasks now store their core affinity in a TCB member. Always set to 0 in single-core
API Additions
The following APIs have been added to support SMP
xTaskCreatePinnedToCore()
andxTaskCreateStaticPinnedToCore()
to create tasks with a core affinity- In single-core, core affinity is ignored. Same behavior as
xTaskCreate()
- In single-core, core affinity is ignored. Same behavior as
xTaskGetCoreID()
to get a task's affinity- Add
ForCore()
versions of the following APIxTaskGetIdleTaskHandleForCore()
xTaskGetCurrentTaskHandleForCore()
ulTaskGetIdleRunTimeCounterForCore()
API Modifications
Added the following macros that abstract away single-core and SMP differences:
taskYIELD_CORE()
triggers a particular core to yieldtaskIS_YIELD_REQUIRED()
/taskIS_YIELD_REQUIRED_USING_PRIORITY()
check if current core requires a yield after a task is unblockedtaskIS_AFFINITY_COMPATIBLE()
check if a task has compatible affinitytaskIS_CURRENTLY_RUNNING()
/taskIS_CURRENTLY_RUNNING_ON_CORE()
checks if a task is running on either coretaskCAN_BE_SCHEDULED()
checks if an unblocked task can be scheduled on any coretaskIS_SCHEDULER_SUSPENDED()
checks if the scheduler on the current core is suspendedtaskSELECT_HIGHEST_PRIORITY_TASK()
selects the highest priority task to execute for the current coreprvGetTCBFromHandle()
updated in SMP to callxTaskGetCurrentTaskHandle()
when the handle isNULL
. Done so for thread safety (in case the current task switches cores at the same time).
The following functions were modified to accommodate SMP behavior:
prvInitialiseNewTask()
- Added
xCoreID
argument to pin task on creation - For single-core,
xCoreID
is hard coded to0
- Added
prvAddNewTaskToReadyList()
- Checks if new task can be scheduled on core 1
vTaskDelete()
- Checks if the deleted task is currently running on the other core.
- If so, sends a yield to the other core.
vTaskPrioritySet()
- Checks if the task is currently running on the both cores, and yields the appropriate core if so
vTaskSuspend()
- Checks if the task is currently running on the other core, and yields the other core if so.
prvTaskIsTaskSuspended()
- Checks the
xPendingReadyList
of both cores to see if a task is suspended
- Checks the
xTaskResumeAll()
- Limit catching up of tick counts to core 0 (given only core 0 calls
xTaskIncrementTick()
)
- Limit catching up of tick counts to core 0 (given only core 0 calls
xTaskIncrementTick()
- Limited to core 0
vTaskSwitchContext()
- Switches context for current core
xTaskRemoveFromEventList()
- Created SMP copy of the function
- Checks if
pxEventList
has already been emptied by the other core before removing - Checks if task can be scheduled on both cores, adds it to the appropriate core's pending list if it can't be scheduled.
- Checks if
- Created SMP copy of the function
vTaskRemoveFromUnorderedEventList()
- In SMP, check if the task can be scheduled before adding it to the appropriate list. Whereas in single-core, the scheduler is always suspended thus the unblocked task always goes onto the pending ready list.
eTaskConfirmSleepModeStatus()
- Updated logic to determine whether sleep is possible in SMP by checking the status of both cores.
prvCheckTasksWaitingTermination()
- Updated logic so that we don't delete tasks on
xTasksWaitingTermination
which are still currently running on the other core.
- Updated logic so that we don't delete tasks on
xTaskGetCurrentTaskHandle()
- In SMP, the function will now disables interrupts to ensure that the calling task does not switch cores while fetching the current core's TCB.
xTaskGetSchedulerState()
- In SMP, the function now disables interrupts to ensure that the calling task does not switch cores while checking its own copy of
uxSchedulerSuspended
.
- In SMP, the function now disables interrupts to ensure that the calling task does not switch cores while checking its own copy of
prvAddCurrentTaskToDelayedList()
- Added extra check to see if current blocking task has already been deleted by the other core.
Critical Section Changes
- Granular Locks: The following objects are now given their own spinlocks
- Kernel objects (i.e.,
tasks.c
):xKernelLock
- Each queue:
xQueueLock
- Queue Registry:
xQueueRegistryLock
- Each event group:
xEventGroupLock
- Each stream buffer:
xStreamBufferLock
- All timers:
xTimerLock
- Kernel objects (i.e.,
- Critical sections now target the appropriate spinlocks
- Added missing critical sections for SMP (see
..._SMP_ONLY()
critical section calls) - Queues no longer use queue locks (see
queueUSE_LOCKS
)- Queues now just use critical sections and skips queue locking
- Queue functions can now execute within a single critical section block
Single Core Differences
List of differences between Vanilla FreeRTOS V10.5.1 and building the dual-core SMP kernel with congigNUMBER_OF_CORES == 1
.
prvAddNewTaskToReadyList()
- Extended critical section so that SMP can check for yields while still inside critical section
vTaskStepTick()
- Extended critical section so that SMP can access
xTickCount
while still inside critical section
- Extended critical section so that SMP can access
Header File & Doxygen Changes
List of changes made to Vanilla FreeRTOS V10.5.1 header files to allow for building in ESP-IDF documentation build system.
-
Removed leading header name line (e.g.,
xxx.h
) in doxygen comment blocks. For example:/** * xxx.h * * Documentation from some func */ void some_func(void);
-
Removed leading
@code{c}
blocks in containing redundant function prototypes. For example:/** * @code{c} * void some_func(int var_a, int var_b); * @endcode * * Documentation from some func */ void some_func(int var_a, int var_b);
-
Added
/** @cond !DOC_EXCLUDE_HEADER_SECTION */
and/** @endcond */
labels to exclude various doxygen sections from being included into documentation builds. These excluded sections include:- In doxygen blocks that describe multiple related set of functions/macros, only the function/macro that matches the doxygen blocks parameters is included. For example:
/** * Description that covers both some_func() and some_func_extra() * * @param var_a var_a description * @param var_b var_b description */ /** @cond !DOC_EXCLUDE_HEADER_SECTION */ #define some_func(var_a) #define some_func_generic(var_a, NULL) /** @endcond */ #define some_func_extra(var_a, var_b) #define some_func_generic(var_a, var_b)
- In functions/macros that are not meant to be directly called by users (i.e., internal), such as the various
Generic
variants of functions
- In doxygen blocks that describe multiple related set of functions/macros, only the function/macro that matches the doxygen blocks parameters is included. For example:
-
Some types/functions/macros are manually documented, thus are documented with regular comment blocks (i.e.,
/* */
) instead of doxygen comment blocks (i.e.,/** */
). Some of these blocks are changed into doxygen blocks.
Changes backported to IDF-FreeRTOS Kernel from upstream kernel beyond v10.5.1 LTS release
tasks.c
- Backported a change where the IDLE tasks are created with the core ID as a suffix in the task name.
timers.c
- Backported configTIMER_SERVICE_TASK_CORE_AFFINITY config option to enable configurability of the Timer Service task's core affinity.
- The change also entails updating the task creation APIs to use IDF-FreeRTOS task creation APIs, adding a assert check for valid affinity values and dropping the use of configUSE_CORE_AFFINITY.