esp-idf/components/freertos/FreeRTOS-Kernel-V10.5.1/idf_changes.md
Darian Leung db64e51e53 feat(freertos): Add SMP kernel changes to FreeRTOS v10.5.1
This commit adds the key kernel changes to the v10.5.1 kernel to support
dual-core SMP. These changes are temporarily documented in the `idf_changes.md`
document. This commit...

- Added changes to kernel data structures to support multiple cores
- Changes to scheduling algorithm support SMP scheduling.
- Changes to each FreeRTOS function to support multiple cores and SMP
scheduling algorithm.
2023-09-18 18:32:54 +08:00

6.3 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 and SPDX-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 and croutine.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()
  • 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
  • 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 be 1 for vanilla, or 2 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 task
  • xPendingReadyList: Each core has its own list to pend ready tasks if the scheduler is suspended on the core
  • xYieldPending: Each core has its own flag to track whether it has a pending yield
  • xIdleTaskHandle: Each core now has its own idle task
  • uxSchedulerSuspended: Each core can independently suspend scheduling on its core
  • ulTaskSwitchedInTime: 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() and xTaskCreateStaticPinnedToCore() to create tasks with a core affinity
    • In single-core, core affinity is ignored. Same behavior as xTaskCreate()
  • xTaskGetCoreID() to get a task's affinity
  • Add ForCore() versions of the following API
    • xTaskGetIdleTaskHandleForCore()
    • xTaskGetCurrentTaskHandleForCore()
    • ulTaskGetIdleRunTimeCounterForCore()

API Modifications

SMP Modifications

Added the following macros that abstract away single-core and SMP differences:

  • taskYIELD_CORE() triggers a particular core to yield
  • taskIS_YIELD_REQUIRED()/taskIS_YIELD_REQUIRED_USING_PRIORITY() check if current core requires a yield after a task is unblocked
  • taskIS_AFFINITY_COMPATIBLE() check if a task has compatible affinity
  • taskIS_CURRENTLY_RUNNING()/taskIS_CURRENTLY_RUNNING_ON_CORE() checks if a task is running on either core
  • taskCAN_BE_SCHEDULED() checks if an unblocked task can be scheduled on any core
  • taskIS_SCHEDULER_SUSPENDED() checks if the scheduler on the current core is suspended
  • taskSELECT_HIGHEST_PRIORITY_TASK() selects the highest priority task to execute for the current core
  • prvGetTCBFromHandle() updated in SMP to call xTaskGetCurrentTaskHandle() when the handle is NULL. 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 to 0
  • 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
  • xTaskResumeAll()
    • Limit catching up of tick counts to core 0 (given only core 0 calls xTaskIncrementTick())
  • 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.
  • 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.
  • prvAddCurrentTaskToDelayedList()
    • Added extra check to see if current blocking task has already been deleted by the other core.

Single-Core Modifications

Single Core Differences

List of differences between Vanilla FreeRTOS V10.5.1 and building the dual-core SMP kernel with congigNUMBER_OF_CORES == 1.