|
|
@@ -4,7 +4,7 @@ ESP-IDF FreeRTOS SMP Changes
|
|
|
Overview
|
|
|
--------
|
|
|
|
|
|
-.. only:: esp32
|
|
|
+.. only:: not CONFIG_FREERTOS_UNICORE
|
|
|
|
|
|
The vanilla FreeRTOS is designed to run on a single core. However the ESP32 is
|
|
|
dual core containing a Protocol CPU (known as **CPU 0** or **PRO_CPU**) and an
|
|
|
@@ -25,7 +25,7 @@ see :doc:`ESP-IDF FreeRTOS Additions<../api-reference/system/freertos_additions>
|
|
|
port of FreeRTOS v8.2.0, a number of FreeRTOS v9.0.0 features have been backported
|
|
|
to ESP-IDF.
|
|
|
|
|
|
-.. only:: esp32
|
|
|
+.. only:: not CONFIG_FREERTOS_UNICORE
|
|
|
|
|
|
:ref:`tasks-and-task-creation`: Use :cpp:func:`xTaskCreatePinnedToCore` or
|
|
|
:cpp:func:`xTaskCreateStaticPinnedToCore` to create tasks in ESP-IDF FreeRTOS. The
|
|
|
@@ -155,133 +155,133 @@ ESP-IDF FreeRTOS (see :ref:`backported-features`).
|
|
|
|
|
|
For more details see :component_file:`freertos/tasks.c`
|
|
|
|
|
|
-The ESP-IDF FreeRTOS task creation functions are nearly identical to their
|
|
|
-vanilla counterparts with the exception of the extra parameter known as
|
|
|
-``xCoreID``. This parameter specifies the core on which the task should run on
|
|
|
+The ESP-IDF FreeRTOS task creation functions are nearly identical to their
|
|
|
+vanilla counterparts with the exception of the extra parameter known as
|
|
|
+``xCoreID``. This parameter specifies the core on which the task should run on
|
|
|
and can be one of the following values.
|
|
|
|
|
|
- ``0`` pins the task to **PRO_CPU**
|
|
|
- ``1`` pins the task to **APP_CPU**
|
|
|
- ``tskNO_AFFINITY`` allows the task to be run on both CPUs
|
|
|
|
|
|
-For example ``xTaskCreatePinnedToCore(tsk_callback, “APP_CPU Task”, 1000, NULL, 10, NULL, 1)``
|
|
|
-creates a task of priority 10 that is pinned to **APP_CPU** with a stack size
|
|
|
-of 1000 bytes. It should be noted that the ``uxStackDepth`` parameter in
|
|
|
-vanilla FreeRTOS specifies a task’s stack depth in terms of the number of
|
|
|
+For example ``xTaskCreatePinnedToCore(tsk_callback, “APP_CPU Task”, 1000, NULL, 10, NULL, 1)``
|
|
|
+creates a task of priority 10 that is pinned to **APP_CPU** with a stack size
|
|
|
+of 1000 bytes. It should be noted that the ``uxStackDepth`` parameter in
|
|
|
+vanilla FreeRTOS specifies a task’s stack depth in terms of the number of
|
|
|
words, whereas ESP-IDF FreeRTOS specifies the stack depth in terms of bytes.
|
|
|
|
|
|
-Note that the vanilla FreeRTOS functions :cpp:func:`xTaskCreate` and
|
|
|
-:cpp:func:`xTaskCreateStatic` have been defined in ESP-IDF FreeRTOS as inline functions which call
|
|
|
+Note that the vanilla FreeRTOS functions :cpp:func:`xTaskCreate` and
|
|
|
+:cpp:func:`xTaskCreateStatic` have been defined in ESP-IDF FreeRTOS as inline functions which call
|
|
|
:cpp:func:`xTaskCreatePinnedToCore` and :cpp:func:`xTaskCreateStaticPinnedToCore`
|
|
|
-respectively with ``tskNO_AFFINITY`` as the ``xCoreID`` value.
|
|
|
+respectively with ``tskNO_AFFINITY`` as the ``xCoreID`` value.
|
|
|
|
|
|
-Each Task Control Block (TCB) in ESP-IDF stores the ``xCoreID`` as a member.
|
|
|
-Hence when each core calls the scheduler to select a task to run, the
|
|
|
-``xCoreID`` member will allow the scheduler to determine if a given task is
|
|
|
+Each Task Control Block (TCB) in ESP-IDF stores the ``xCoreID`` as a member.
|
|
|
+Hence when each core calls the scheduler to select a task to run, the
|
|
|
+``xCoreID`` member will allow the scheduler to determine if a given task is
|
|
|
permitted to run on the core that called it.
|
|
|
|
|
|
Scheduling
|
|
|
----------
|
|
|
|
|
|
-The vanilla FreeRTOS implements scheduling in the ``vTaskSwitchContext()``
|
|
|
+The vanilla FreeRTOS implements scheduling in the ``vTaskSwitchContext()``
|
|
|
function. This function is responsible for selecting the highest priority task
|
|
|
-to run from a list of tasks in the Ready state known as the Ready Tasks List
|
|
|
-(described in the next section). In ESP-IDF FreeRTOS, each core will call
|
|
|
-``vTaskSwitchContext()`` independently to select a task to run from the
|
|
|
-Ready Tasks List which is shared between both cores. There are several
|
|
|
-differences in scheduling behavior between vanilla and ESP-IDF FreeRTOS such as
|
|
|
-differences in Round Robin scheduling, scheduler suspension, and tick interrupt
|
|
|
-synchronicity.
|
|
|
+to run from a list of tasks in the Ready state known as the Ready Tasks List
|
|
|
+(described in the next section). In ESP-IDF FreeRTOS, each core will call
|
|
|
+``vTaskSwitchContext()`` independently to select a task to run from the
|
|
|
+Ready Tasks List which is shared between both cores. There are several
|
|
|
+differences in scheduling behavior between vanilla and ESP-IDF FreeRTOS such as
|
|
|
+differences in Round Robin scheduling, scheduler suspension, and tick interrupt
|
|
|
+synchronicity.
|
|
|
|
|
|
.. _round-robin-scheduling:
|
|
|
|
|
|
Round Robin Scheduling
|
|
|
^^^^^^^^^^^^^^^^^^^^^^
|
|
|
|
|
|
-Given multiple tasks in the Ready state and of the same priority, vanilla
|
|
|
+Given multiple tasks in the Ready state and of the same priority, vanilla
|
|
|
FreeRTOS implements Round Robin scheduling between each task. This will result
|
|
|
-in running those tasks in turn each time the scheduler is called
|
|
|
-(e.g. every tick interrupt). On the other hand, the ESP-IDF FreeRTOS scheduler
|
|
|
-may skip tasks when Round Robin scheduling multiple Ready state tasks of the
|
|
|
+in running those tasks in turn each time the scheduler is called
|
|
|
+(e.g. every tick interrupt). On the other hand, the ESP-IDF FreeRTOS scheduler
|
|
|
+may skip tasks when Round Robin scheduling multiple Ready state tasks of the
|
|
|
same priority.
|
|
|
|
|
|
-The issue of skipping tasks during Round Robin scheduling arises from the way
|
|
|
-the Ready Tasks List is implemented in FreeRTOS. In vanilla FreeRTOS,
|
|
|
-``pxReadyTasksList`` is used to store a list of tasks that are in the Ready
|
|
|
-state. The list is implemented as an array of length ``configMAX_PRIORITIES``
|
|
|
-where each element of the array is a linked list. Each linked list is of type
|
|
|
-``List_t`` and contains TCBs of tasks of the same priority that are in the
|
|
|
-Ready state. The following diagram illustrates the ``pxReadyTasksList``
|
|
|
+The issue of skipping tasks during Round Robin scheduling arises from the way
|
|
|
+the Ready Tasks List is implemented in FreeRTOS. In vanilla FreeRTOS,
|
|
|
+``pxReadyTasksList`` is used to store a list of tasks that are in the Ready
|
|
|
+state. The list is implemented as an array of length ``configMAX_PRIORITIES``
|
|
|
+where each element of the array is a linked list. Each linked list is of type
|
|
|
+``List_t`` and contains TCBs of tasks of the same priority that are in the
|
|
|
+Ready state. The following diagram illustrates the ``pxReadyTasksList``
|
|
|
structure.
|
|
|
|
|
|
.. figure:: ../../_static/freertos-ready-task-list.png
|
|
|
:align: center
|
|
|
:alt: Vanilla FreeRTOS Ready Task List Structure
|
|
|
-
|
|
|
- Illustration of FreeRTOS Ready Task List Data Structure
|
|
|
|
|
|
+ Illustration of FreeRTOS Ready Task List Data Structure
|
|
|
|
|
|
-Each linked list also contains a ``pxIndex`` which points to the last TCB
|
|
|
-returned when the list was queried. This index allows the ``vTaskSwitchContext()``
|
|
|
-to start traversing the list at the TCB immediately after ``pxIndex`` hence
|
|
|
+
|
|
|
+Each linked list also contains a ``pxIndex`` which points to the last TCB
|
|
|
+returned when the list was queried. This index allows the ``vTaskSwitchContext()``
|
|
|
+to start traversing the list at the TCB immediately after ``pxIndex`` hence
|
|
|
implementing Round Robin Scheduling between tasks of the same priority.
|
|
|
|
|
|
-In ESP-IDF FreeRTOS, the Ready Tasks List is shared between cores hence
|
|
|
-``pxReadyTasksList`` will contain tasks pinned to different cores. When a core
|
|
|
-calls the scheduler, it is able to look at the ``xCoreID`` member of each TCB
|
|
|
-in the list to determine if a task is allowed to run on calling the core. The
|
|
|
+In ESP-IDF FreeRTOS, the Ready Tasks List is shared between cores hence
|
|
|
+``pxReadyTasksList`` will contain tasks pinned to different cores. When a core
|
|
|
+calls the scheduler, it is able to look at the ``xCoreID`` member of each TCB
|
|
|
+in the list to determine if a task is allowed to run on calling the core. The
|
|
|
ESP-IDF FreeRTOS ``pxReadyTasksList`` is illustrated below.
|
|
|
|
|
|
.. figure:: ../../_static/freertos-ready-task-list-smp.png
|
|
|
:align: center
|
|
|
:alt: ESP-IDF FreeRTOS Ready Task List Structure
|
|
|
-
|
|
|
+
|
|
|
Illustration of FreeRTOS Ready Task List Data Structure in ESP-IDF
|
|
|
-
|
|
|
-Therefore when **PRO_CPU** calls the scheduler, it will only consider the tasks
|
|
|
-in blue or purple. Whereas when **APP_CPU** calls the scheduler, it will only
|
|
|
+
|
|
|
+Therefore when **PRO_CPU** calls the scheduler, it will only consider the tasks
|
|
|
+in blue or purple. Whereas when **APP_CPU** calls the scheduler, it will only
|
|
|
consider the tasks in orange or purple.
|
|
|
|
|
|
-Although each TCB has an ``xCoreID`` in ESP-IDF FreeRTOS, the linked list of
|
|
|
-each priority only has a single ``pxIndex``. Therefore when the scheduler is
|
|
|
-called from a particular core and traverses the linked list, it will skip all
|
|
|
-TCBs pinned to the other core and point the pxIndex at the selected task. If
|
|
|
-the other core then calls the scheduler, it will traverse the linked list
|
|
|
+Although each TCB has an ``xCoreID`` in ESP-IDF FreeRTOS, the linked list of
|
|
|
+each priority only has a single ``pxIndex``. Therefore when the scheduler is
|
|
|
+called from a particular core and traverses the linked list, it will skip all
|
|
|
+TCBs pinned to the other core and point the pxIndex at the selected task. If
|
|
|
+the other core then calls the scheduler, it will traverse the linked list
|
|
|
starting at the TCB immediately after ``pxIndex``. Therefore, TCBs skipped on
|
|
|
-the previous scheduler call from the other core would not be considered on the
|
|
|
-current scheduler call. This issue is demonstrated in the following
|
|
|
+the previous scheduler call from the other core would not be considered on the
|
|
|
+current scheduler call. This issue is demonstrated in the following
|
|
|
illustration.
|
|
|
|
|
|
.. figure:: ../../_static/freertos-ready-task-list-smp-pxIndex.png
|
|
|
:align: center
|
|
|
:alt: ESP-IDF pxIndex Behavior
|
|
|
-
|
|
|
+
|
|
|
Illustration of pxIndex behavior in ESP-IDF FreeRTOS
|
|
|
|
|
|
-Referring to the illustration above, assume that priority 9 is the highest
|
|
|
-priority, and none of the tasks in priority 9 will block hence will always be
|
|
|
+Referring to the illustration above, assume that priority 9 is the highest
|
|
|
+priority, and none of the tasks in priority 9 will block hence will always be
|
|
|
either in the running or Ready state.
|
|
|
|
|
|
-1) **PRO_CPU** calls the scheduler and selects Task A to run, hence moves
|
|
|
+1) **PRO_CPU** calls the scheduler and selects Task A to run, hence moves
|
|
|
``pxIndex`` to point to Task A
|
|
|
|
|
|
-2) **APP_CPU** calls the scheduler and starts traversing from the task after
|
|
|
-``pxIndex`` which is Task B. However Task B is not selected to run as it is not
|
|
|
-pinned to **APP_CPU** hence it is skipped and Task C is selected instead.
|
|
|
+2) **APP_CPU** calls the scheduler and starts traversing from the task after
|
|
|
+``pxIndex`` which is Task B. However Task B is not selected to run as it is not
|
|
|
+pinned to **APP_CPU** hence it is skipped and Task C is selected instead.
|
|
|
``pxIndex`` now points to Task C
|
|
|
|
|
|
-3) **PRO_CPU** calls the scheduler and starts traversing from Task D. It skips
|
|
|
-Task D and selects Task E to run and points ``pxIndex`` to Task E. Notice that
|
|
|
-Task B isn’t traversed because it was skipped the last time **APP_CPU** called
|
|
|
+3) **PRO_CPU** calls the scheduler and starts traversing from Task D. It skips
|
|
|
+Task D and selects Task E to run and points ``pxIndex`` to Task E. Notice that
|
|
|
+Task B isn’t traversed because it was skipped the last time **APP_CPU** called
|
|
|
the scheduler to traverse the list.
|
|
|
|
|
|
-4) The same situation with Task D will occur if **APP_CPU** calls the
|
|
|
+4) The same situation with Task D will occur if **APP_CPU** calls the
|
|
|
scheduler again as ``pxIndex`` now points to Task E
|
|
|
|
|
|
One solution to the issue of task skipping is to ensure that every task will
|
|
|
enter a blocked state so that they are removed from the Ready Task List.
|
|
|
-Another solution is to distribute tasks across multiple priorities such that
|
|
|
-a given priority will not be assigned multiple tasks that are pinned to
|
|
|
+Another solution is to distribute tasks across multiple priorities such that
|
|
|
+a given priority will not be assigned multiple tasks that are pinned to
|
|
|
different cores.
|
|
|
|
|
|
.. _scheduler-suspension:
|
|
|
@@ -289,22 +289,22 @@ different cores.
|
|
|
Scheduler Suspension
|
|
|
^^^^^^^^^^^^^^^^^^^^
|
|
|
|
|
|
-In vanilla FreeRTOS, suspending the scheduler via :cpp:func:`vTaskSuspendAll` will
|
|
|
-prevent calls of ``vTaskSwitchContext`` from context switching until the
|
|
|
-scheduler has been resumed with :cpp:func:`xTaskResumeAll`. However servicing ISRs
|
|
|
+In vanilla FreeRTOS, suspending the scheduler via :cpp:func:`vTaskSuspendAll` will
|
|
|
+prevent calls of ``vTaskSwitchContext`` from context switching until the
|
|
|
+scheduler has been resumed with :cpp:func:`xTaskResumeAll`. However servicing ISRs
|
|
|
are still permitted. Therefore any changes in task states as a result from the
|
|
|
-current running task or ISRSs will not be executed until the scheduler is
|
|
|
-resumed. Scheduler suspension in vanilla FreeRTOS is a common protection method
|
|
|
-against simultaneous access of data shared between tasks, whilst still allowing
|
|
|
+current running task or ISRSs will not be executed until the scheduler is
|
|
|
+resumed. Scheduler suspension in vanilla FreeRTOS is a common protection method
|
|
|
+against simultaneous access of data shared between tasks, whilst still allowing
|
|
|
ISRs to be serviced.
|
|
|
|
|
|
In ESP-IDF FreeRTOS, :cpp:func:`xTaskSuspendAll` will only prevent calls of
|
|
|
``vTaskSwitchContext()`` from switching contexts on the core that called for the
|
|
|
-suspension. Hence if **PRO_CPU** calls :cpp:func:`vTaskSuspendAll`, **APP_CPU** will
|
|
|
-still be able to switch contexts. If data is shared between tasks that are
|
|
|
-pinned to different cores, scheduler suspension is **NOT** a valid method of
|
|
|
-protection against simultaneous access. Consider using critical sections
|
|
|
-(disables interrupts) or semaphores (does not disable interrupts) instead when
|
|
|
+suspension. Hence if **PRO_CPU** calls :cpp:func:`vTaskSuspendAll`, **APP_CPU** will
|
|
|
+still be able to switch contexts. If data is shared between tasks that are
|
|
|
+pinned to different cores, scheduler suspension is **NOT** a valid method of
|
|
|
+protection against simultaneous access. Consider using critical sections
|
|
|
+(disables interrupts) or semaphores (does not disable interrupts) instead when
|
|
|
protecting shared resources in ESP-IDF FreeRTOS.
|
|
|
|
|
|
In general, it's better to use other RTOS primitives like mutex semaphores to protect
|
|
|
@@ -313,34 +313,34 @@ against data shared between tasks, rather than :cpp:func:`vTaskSuspendAll`.
|
|
|
|
|
|
.. _tick-interrupt-synchronicity:
|
|
|
|
|
|
-Tick Interrupt Synchronicity
|
|
|
+Tick Interrupt Synchronicity
|
|
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
|
|
|
|
-In ESP-IDF FreeRTOS, tasks on different cores that unblock on the same tick
|
|
|
-count might not run at exactly the same time due to the scheduler calls from
|
|
|
-each core being independent, and the tick interrupts to each core being
|
|
|
+In ESP-IDF FreeRTOS, tasks on different cores that unblock on the same tick
|
|
|
+count might not run at exactly the same time due to the scheduler calls from
|
|
|
+each core being independent, and the tick interrupts to each core being
|
|
|
unsynchronized.
|
|
|
|
|
|
-In vanilla FreeRTOS the tick interrupt triggers a call to
|
|
|
-:cpp:func:`xTaskIncrementTick` which is responsible for incrementing the tick
|
|
|
-counter, checking if tasks which have called :cpp:func:`vTaskDelay` have fulfilled
|
|
|
-their delay period, and moving those tasks from the Delayed Task List to the
|
|
|
-Ready Task List. The tick interrupt will then call the scheduler if a context
|
|
|
+In vanilla FreeRTOS the tick interrupt triggers a call to
|
|
|
+:cpp:func:`xTaskIncrementTick` which is responsible for incrementing the tick
|
|
|
+counter, checking if tasks which have called :cpp:func:`vTaskDelay` have fulfilled
|
|
|
+their delay period, and moving those tasks from the Delayed Task List to the
|
|
|
+Ready Task List. The tick interrupt will then call the scheduler if a context
|
|
|
switch is necessary.
|
|
|
|
|
|
-In ESP-IDF FreeRTOS, delayed tasks are unblocked with reference to the tick
|
|
|
-interrupt on PRO_CPU as PRO_CPU is responsible for incrementing the shared tick
|
|
|
-count. However tick interrupts to each core might not be synchronized (same
|
|
|
-frequency but out of phase) hence when PRO_CPU receives a tick interrupt,
|
|
|
-APP_CPU might not have received it yet. Therefore if multiple tasks of the same
|
|
|
-priority are unblocked on the same tick count, the task pinned to PRO_CPU will
|
|
|
-run immediately whereas the task pinned to APP_CPU must wait until APP_CPU
|
|
|
-receives its out of sync tick interrupt. Upon receiving the tick interrupt,
|
|
|
+In ESP-IDF FreeRTOS, delayed tasks are unblocked with reference to the tick
|
|
|
+interrupt on PRO_CPU as PRO_CPU is responsible for incrementing the shared tick
|
|
|
+count. However tick interrupts to each core might not be synchronized (same
|
|
|
+frequency but out of phase) hence when PRO_CPU receives a tick interrupt,
|
|
|
+APP_CPU might not have received it yet. Therefore if multiple tasks of the same
|
|
|
+priority are unblocked on the same tick count, the task pinned to PRO_CPU will
|
|
|
+run immediately whereas the task pinned to APP_CPU must wait until APP_CPU
|
|
|
+receives its out of sync tick interrupt. Upon receiving the tick interrupt,
|
|
|
APP_CPU will then call for a context switch and finally switches contexts to
|
|
|
the newly unblocked task.
|
|
|
|
|
|
-Therefore, task delays should **NOT** be used as a method of synchronization
|
|
|
-between tasks in ESP-IDF FreeRTOS. Instead, consider using a counting semaphore
|
|
|
+Therefore, task delays should **NOT** be used as a method of synchronization
|
|
|
+between tasks in ESP-IDF FreeRTOS. Instead, consider using a counting semaphore
|
|
|
to unblock multiple tasks at the same time.
|
|
|
|
|
|
|
|
|
@@ -349,47 +349,47 @@ to unblock multiple tasks at the same time.
|
|
|
Critical Sections & Disabling Interrupts
|
|
|
----------------------------------------
|
|
|
|
|
|
-Vanilla FreeRTOS implements critical sections in ``vTaskEnterCritical`` which
|
|
|
-disables the scheduler and calls ``portDISABLE_INTERRUPTS``. This prevents
|
|
|
-context switches and servicing of ISRs during a critical section. Therefore,
|
|
|
-critical sections are used as a valid protection method against simultaneous
|
|
|
+Vanilla FreeRTOS implements critical sections in ``vTaskEnterCritical`` which
|
|
|
+disables the scheduler and calls ``portDISABLE_INTERRUPTS``. This prevents
|
|
|
+context switches and servicing of ISRs during a critical section. Therefore,
|
|
|
+critical sections are used as a valid protection method against simultaneous
|
|
|
access in vanilla FreeRTOS.
|
|
|
|
|
|
-.. only:: esp32
|
|
|
+.. only:: not CONFIG_FREERTOS_UNICORE
|
|
|
|
|
|
- On the other hand, the ESP32 has no hardware method for cores to disable each
|
|
|
- other’s interrupts. Calling ``portDISABLE_INTERRUPTS()`` will have no effect on
|
|
|
- the interrupts of the other core. Therefore, disabling interrupts is **NOT**
|
|
|
- a valid protection method against simultaneous access to shared data as it
|
|
|
- leaves the other core free to access the data even if the current core has
|
|
|
+ On the other hand, the ESP32 has no hardware method for cores to disable each
|
|
|
+ other’s interrupts. Calling ``portDISABLE_INTERRUPTS()`` will have no effect on
|
|
|
+ the interrupts of the other core. Therefore, disabling interrupts is **NOT**
|
|
|
+ a valid protection method against simultaneous access to shared data as it
|
|
|
+ leaves the other core free to access the data even if the current core has
|
|
|
disabled its own interrupts.
|
|
|
|
|
|
-.. only:: esp32s2
|
|
|
+.. only:: CONFIG_FREERTOS_UNICORE
|
|
|
|
|
|
ESP-IDF contains some modifications to work with dual core concurrency,
|
|
|
and the dual core API is used even on a single core only chip.
|
|
|
|
|
|
For this reason, ESP-IDF FreeRTOS implements critical sections using special mutexes,
|
|
|
-referred by portMUX_Type objects on top of specific spinlock component
|
|
|
-and calls to enter or exit a critical must provide a spinlock object that
|
|
|
-is associated with a shared resource requiring access protection.
|
|
|
+referred by portMUX_Type objects on top of specific spinlock component
|
|
|
+and calls to enter or exit a critical must provide a spinlock object that
|
|
|
+is associated with a shared resource requiring access protection.
|
|
|
When entering a critical section in ESP-IDF FreeRTOS, the calling core will disable
|
|
|
-its scheduler and interrupts similar to the vanilla FreeRTOS implementation. However,
|
|
|
-the calling core will also take the locks whilst the other core is left unaffected during
|
|
|
-the critical section. If the other core attempts to take the spinlock, it
|
|
|
-will spin until the lock is released. Therefore, the ESP-IDF FreeRTOS
|
|
|
+its scheduler and interrupts similar to the vanilla FreeRTOS implementation. However,
|
|
|
+the calling core will also take the locks whilst the other core is left unaffected during
|
|
|
+the critical section. If the other core attempts to take the spinlock, it
|
|
|
+will spin until the lock is released. Therefore, the ESP-IDF FreeRTOS
|
|
|
implementation of critical sections allows a core to have protected access to a
|
|
|
-shared resource without disabling the other core. The other core will only be
|
|
|
+shared resource without disabling the other core. The other core will only be
|
|
|
affected if it tries to concurrently access the same resource.
|
|
|
|
|
|
The ESP-IDF FreeRTOS critical section functions have been modified as follows…
|
|
|
|
|
|
- - ``taskENTER_CRITICAL(mux)``, ``taskENTER_CRITICAL_ISR(mux)``,
|
|
|
- ``portENTER_CRITICAL(mux)``, ``portENTER_CRITICAL_ISR(mux)`` are all macro
|
|
|
- defined to call :cpp:func:`vTaskEnterCritical`
|
|
|
+ - ``taskENTER_CRITICAL(mux)``, ``taskENTER_CRITICAL_ISR(mux)``,
|
|
|
+ ``portENTER_CRITICAL(mux)``, ``portENTER_CRITICAL_ISR(mux)`` are all macro
|
|
|
+ defined to call :cpp:func:`vTaskEnterCritical`
|
|
|
|
|
|
- - ``taskEXIT_CRITICAL(mux)``, ``taskEXIT_CRITICAL_ISR(mux)``,
|
|
|
- ``portEXIT_CRITICAL(mux)``, ``portEXIT_CRITICAL_ISR(mux)`` are all macro
|
|
|
+ - ``taskEXIT_CRITICAL(mux)``, ``taskEXIT_CRITICAL_ISR(mux)``,
|
|
|
+ ``portEXIT_CRITICAL(mux)``, ``portEXIT_CRITICAL_ISR(mux)`` are all macro
|
|
|
defined to call :cpp:func:`vTaskExitCritical`
|
|
|
|
|
|
- ``portENTER_CRITICAL_SAFE(mux)``, ``portEXIT_CRITICAL_SAFE(mux)`` macro identifies
|
|
|
@@ -400,14 +400,14 @@ The ESP-IDF FreeRTOS critical section functions have been modified as follows…
|
|
|
For more details see :component_file:`soc/include/soc/spinlock.h`
|
|
|
and :component_file:`freertos/tasks.c`
|
|
|
|
|
|
-It should be noted that when modifying vanilla FreeRTOS code to be ESP-IDF
|
|
|
-FreeRTOS compatible, it is trivial to modify the type of critical section
|
|
|
-called as they are all defined to call the same function. As long as the same
|
|
|
-spinlock is provided upon entering and exiting, the type of call should not
|
|
|
+It should be noted that when modifying vanilla FreeRTOS code to be ESP-IDF
|
|
|
+FreeRTOS compatible, it is trivial to modify the type of critical section
|
|
|
+called as they are all defined to call the same function. As long as the same
|
|
|
+spinlock is provided upon entering and exiting, the type of call should not
|
|
|
matter.
|
|
|
|
|
|
|
|
|
-.. only:: esp32
|
|
|
+.. only:: not CONFIG_FREERTOS_UNICORE
|
|
|
|
|
|
.. _floating-points:
|
|
|
|
|
|
@@ -415,16 +415,16 @@ matter.
|
|
|
-------------------------
|
|
|
|
|
|
ESP-IDF FreeRTOS implements Lazy Context Switching for FPUs. In other words,
|
|
|
- the state of a core's FPU registers are not immediately saved when a context
|
|
|
+ the state of a core's FPU registers are not immediately saved when a context
|
|
|
switch occurs. Therefore, tasks that utilize ``float`` must be pinned to a
|
|
|
particular core upon creation. If not, ESP-IDF FreeRTOS will automatically pin
|
|
|
- the task in question to whichever core the task was running on upon the task's
|
|
|
+ the task in question to whichever core the task was running on upon the task's
|
|
|
first use of ``float``. Likewise due to Lazy Context Switching, only interrupt
|
|
|
- service routines of lowest priority (that is it the Level 1) can use ``float``,
|
|
|
+ service routines of lowest priority (that is it the Level 1) can use ``float``,
|
|
|
higher priority interrupts do not support FPU usage.
|
|
|
|
|
|
ESP32 does not support hardware acceleration for double precision floating point
|
|
|
- arithmetic (``double``). Instead ``double`` is implemented via software hence the
|
|
|
+ arithmetic (``double``). Instead ``double`` is implemented via software hence the
|
|
|
behavioral restrictions with regards to ``float`` do not apply to ``double``. Note
|
|
|
that due to the lack of hardware acceleration, ``double`` operations may consume
|
|
|
significantly larger amount of CPU time in comparison to ``float``.
|
|
|
@@ -495,7 +495,7 @@ The ESP-IDF FreeRTOS can be configured in the project configuration menu
|
|
|
highlights some of the ESP-IDF FreeRTOS configuration options. For a full list of
|
|
|
ESP-IDF FreeRTOS configurations, see :doc:`FreeRTOS <../api-reference/kconfig>`
|
|
|
|
|
|
-.. only:: esp32
|
|
|
+.. only:: not CONFIG_FREERTOS_UNICORE
|
|
|
|
|
|
:ref:`CONFIG_FREERTOS_UNICORE` will run ESP-IDF FreeRTOS only
|
|
|
on **PRO_CPU**. Note that this is **not equivalent to running vanilla
|
|
|
@@ -504,9 +504,9 @@ ESP-IDF FreeRTOS configurations, see :doc:`FreeRTOS <../api-reference/kconfig>`
|
|
|
effects of running ESP-IDF FreeRTOS on a single core, search for
|
|
|
occurences of ``CONFIG_FREERTOS_UNICORE`` in the ESP-IDF components.
|
|
|
|
|
|
-.. only:: esp32s2
|
|
|
+.. only:: CONFIG_FREERTOS_UNICORE
|
|
|
|
|
|
- As ESP32-S2 is a single core SoC, the config item :ref:`CONFIG_FREERTOS_UNICORE` is
|
|
|
+ As {IDF_TARGET_NAME} is a single core SoC, the config item :ref:`CONFIG_FREERTOS_UNICORE` is
|
|
|
always set. This means ESP-IDF only runs on the single CPU. Note that this is **not
|
|
|
equivalent to running vanilla FreeRTOS**. Behaviors of multiple components in ESP-IDF
|
|
|
will be modified. For more details regarding the effects of running ESP-IDF FreeRTOS
|