docs: Refactor FreeRTOS documentation
This commit refactors the FreeRTOS documentation as follows:
- Rewrite FreeRTOS SMP changes document (ESP-IDF FreeRTOS SMP)
- Reorganized sections in kernel behavior changes
- Rewrote descriptions of each kernel behavior changes
- Added notes about using ESP-IDF FreeRTOS for single core targets
- Moved TLSP callback section to FreeRTOS Additions document
- Moved FreeRTOS configuration section to FreeRTOS API document
- Added notes about FreeRTOS applications in ESP-IDF
This commit is contained in:
committed by
Marius Vikhammer
parent
d76cb9f456
commit
39fd7525b6
@@ -7,11 +7,87 @@ Overview
|
||||
This section contains documentation of FreeRTOS types, functions, and macros. It is automatically generated from FreeRTOS header files.
|
||||
|
||||
.. note::
|
||||
ESP-IDF FreeRTOS is based on the Xtensa port of FreeRTOS v10.4.3
|
||||
ESP-IDF FreeRTOS is based on Vanilla FreeRTOS v10.4.3
|
||||
|
||||
For more information about FreeRTOS features specific to ESP-IDF, see :doc:`ESP-IDF FreeRTOS SMP Changes<../../api-guides/freertos-smp>`
|
||||
and :doc:`ESP-IDF FreeRTOS Additions<freertos_additions>`.
|
||||
- For more information about the SMP changes of ESP-IDF FreeRTOS, see :doc:`/api-guides/freertos-smp`
|
||||
- For more information about the features added to ESP-IDF FreeRTOS, see :doc:`/api-reference/system/freertos_additions`.
|
||||
|
||||
Configuration
|
||||
-------------
|
||||
|
||||
Vanilla FreeRTOS allows ports and applications to configure the kernel by adding various ``#define config...`` macros to ``FreeRTOSConfig.h``. Through these macros, the kernel's scheduling behavior and various kernel features can be enabled or disabled. **However, in ESP-IDF FreeRTOS, the ``FreeRTOSConfig.h`` file is considered a private and must not be modified by users**. Any FreeRTOS configuration that is exposed to the user will be done so via menuconfig.
|
||||
|
||||
ESP-IDF FreeRTOS can be configured in the project configuration menu (``idf.py menuconfig``) under ``Component Config/FreeRTOS``. The following section highlights some of the ESP-IDF FreeRTOS configuration options. For a full list of ESP-IDF FreeRTOS configurations, see :doc:`/api-reference/kconfig`
|
||||
|
||||
- :ref:`CONFIG_FREERTOS_UNICORE` will run ESP-IDF FreeRTOS only on CPU0. Note that this is **not equivalent to running Vanilla FreeRTOS**. Futhermore, this option may affect behavior of components other than :component:`freertos`. For more details regarding the effects of running ESP-IDF FreeRTOS on a single core, refer to :ref:`freertos-smp-single-core`. Alternatively, users can also search for occurrences of ``CONFIG_FREERTOS_UNICORE`` in the ESP-IDF components.
|
||||
|
||||
- :ref:`CONFIG_FREERTOS_ASSERT_ON_UNTESTED_FUNCTION` will trigger a halt in functions in ESP-IDF FreeRTOS that have not been fully tested in an SMP context.
|
||||
|
||||
- :ref:`CONFIG_FREERTOS_TASK_FUNCTION_WRAPPER` will enclose all task functions within a wrapper function. In the case that a task function mistakenly returns (i.e. does not call :cpp:func:`vTaskDelete`), the call flow will return to the wrapper function. The wrapper function will then log an error and abort the application, as illustrated below::
|
||||
|
||||
E (25) FreeRTOS: FreeRTOS task should not return. Aborting now!
|
||||
abort() was called at PC 0x40085c53 on core 0
|
||||
|
||||
.. only:: CONFIG_FREERTOS_UNICORE
|
||||
|
||||
.. note::
|
||||
As {IDF_TARGET_NAME} is a single core SoC, the :ref:`CONFIG_FREERTOS_UNICORE` configuration is always set.
|
||||
|
||||
|
||||
.. _freertos-applications:
|
||||
|
||||
ESP-IDF FreeRTOS Applications
|
||||
-----------------------------
|
||||
|
||||
Unlike Vanilla FreeRTOS, users must not call :cpp:func:`vTaskStartScheduler`. Instead, ESP-IDF FreeRTOS is started automatically. The entry point is a user defined ``void app_main(void)`` function.
|
||||
|
||||
- Typically, users would spawn the rest of their applications task from ``app_main``.
|
||||
- The ``app_main`` function is allowed to return at any point (i.e., before the application terminates).
|
||||
- The ``app_main`` function is called from the ``main`` task.
|
||||
|
||||
The ``main`` task is one of multiple tasks that are automatically spawned by ESP-IDF during startup. These tasks are:
|
||||
|
||||
.. only:: not CONFIG_FREERTOS_UNICORE
|
||||
|
||||
.. list-table:: List of Tasks Created During Startup
|
||||
:widths: 25 25 5 50
|
||||
:header-rows: 1
|
||||
|
||||
* - Task Name
|
||||
- Affinity
|
||||
- Priority
|
||||
- Description
|
||||
* - Main Task (``main``)
|
||||
- CPU0
|
||||
- 1
|
||||
- Task that simply calls ``app_main``. This task will self delete when ``app_main`` returns
|
||||
* - Idle Tasks (``IDLEx``)
|
||||
- CPU0 and CPU1
|
||||
- 0
|
||||
- Idle tasks created for (and pinned to) each CPU
|
||||
* - IPC Tasks (``ipcx``)
|
||||
- CPU0 and CPU1
|
||||
- 24
|
||||
- IPC tasks created for (and pinned to ) each CPU. IPC tasks are used to implement the IPC feature. See :doc:`/api-reference/system/ipc` for more details.
|
||||
|
||||
.. only:: CONFIG_FREERTOS_UNICORE
|
||||
|
||||
.. list-table:: List of Tasks Created During Startup
|
||||
:widths: 25 25 5 50
|
||||
:header-rows: 1
|
||||
|
||||
* - Task Name
|
||||
- Affinity
|
||||
- Priority
|
||||
- Description
|
||||
* - Main Task (``main``)
|
||||
- CPU0
|
||||
- 1
|
||||
- Task that simply calls ``app_main``. This task will self delete when ``app_main`` returns
|
||||
* - Idle Tasks (``IDLEx``)
|
||||
- CPU0 and CPU1
|
||||
- 0
|
||||
- Idle task created for (and pinned to) each CPU
|
||||
|
||||
Task API
|
||||
--------
|
||||
|
||||
@@ -1,66 +1,48 @@
|
||||
FreeRTOS Additions
|
||||
==================
|
||||
|
||||
This document describes the additional features added to ESP-IDF FreeRTOS. This document is split into the following parts:
|
||||
|
||||
.. contents:: Contents
|
||||
:depth: 2
|
||||
|
||||
|
||||
.. ---------------------------------------------------- Overview -------------------------------------------------------
|
||||
|
||||
Overview
|
||||
--------
|
||||
|
||||
ESP-IDF FreeRTOS is based on the Xtensa port of FreeRTOS v10.4.3 with significant modifications
|
||||
for SMP compatibility (see :doc:`ESP-IDF FreeRTOS SMP Changes<../../api-guides/freertos-smp>`).
|
||||
However, various features specific to ESP-IDF FreeRTOS have been added. The features are as follows:
|
||||
ESP-IDF FreeRTOS is modified version of based on the Xtensa port of FreeRTOS v10.4.3 with significant modifications for SMP compatibility (see :doc:`ESP-IDF FreeRTOS SMP Changes<../../api-guides/freertos-smp>`). However, various new features specific to ESP-IDF FreeRTOS have been added. The features are as follows:
|
||||
|
||||
:ref:`ring-buffers`: Ring buffers were added to provide a form of buffer that could accept
|
||||
entries of arbitrary lengths.
|
||||
|
||||
:ref:`hooks`: ESP-IDF FreeRTOS hooks provides support for registering extra Idle and
|
||||
Tick hooks at run time. Moreover, the hooks can be asymmetric amongst both CPUs.
|
||||
|
||||
:ref:`component-specific-properties`: Currently added only one component specific property `ORIG_INCLUDE_PATH`.
|
||||
- **Ring buffers**: Ring buffers provide a FIFO buffer that can accept entries of arbitrary lengths.
|
||||
- **Hooks**: ESP-IDF FreeRTOS hooks provides support for registering extra Idle and Tick hooks at run time. Moreover, the hooks can be asymmetric among both CPUs.
|
||||
- **Thread Local Storage Pointer (TLSP) Deletion Callbacks**: TLSP Deletion callbacks are run automatically when a task is deleted, thus allowing users to clean up their TLSPs automatically.
|
||||
- **Component Specific Properties**: Currently added only one component specific property ``ORIG_INCLUDE_PATH``.
|
||||
|
||||
|
||||
.. _ring-buffers:
|
||||
.. -------------------------------------------------- Ring Buffers -----------------------------------------------------
|
||||
|
||||
Ring Buffers
|
||||
------------
|
||||
|
||||
The ESP-IDF FreeRTOS ring buffer is a strictly FIFO buffer that supports arbitrarily sized items.
|
||||
Ring buffers are a more memory efficient alternative to FreeRTOS queues in situations where the
|
||||
size of items is variable. The capacity of a ring buffer is not measured by the number of items
|
||||
it can store, but rather by the amount of memory used for storing items. The ring buffer provides API
|
||||
to send an item, or to allocate space for an item in the ring buffer to be filled manually by the user.
|
||||
For efficiency reasons,
|
||||
**items are always retrieved from the ring buffer by reference**. As a result, all retrieved
|
||||
items *must also be returned* to the ring buffer by using :cpp:func:`vRingbufferReturnItem` or :cpp:func:`vRingbufferReturnItemFromISR`, in order for them to be removed from the ring buffer completely.
|
||||
The ring buffers are split into the three following types:
|
||||
The ESP-IDF FreeRTOS ring buffer is a strictly FIFO buffer that supports arbitrarily sized items. Ring buffers are a more memory efficient alternative to FreeRTOS queues in situations where the size of items is variable. The capacity of a ring buffer is not measured by the number of items it can store, but rather by the amount of memory used for storing items. The ring buffer provides API to send an item, or to allocate space for an item in the ring buffer to be filled manually by the user. For efficiency reasons, **items are always retrieved from the ring buffer by reference**. As a result, all retrieved items *must also be returned* to the ring buffer by using :cpp:func:`vRingbufferReturnItem` or :cpp:func:`vRingbufferReturnItemFromISR`, in order for them to be removed from the ring buffer completely. The ring buffers are split into the three following types:
|
||||
|
||||
**No-Split buffers** will guarantee that an item is stored in contiguous memory and will not
|
||||
attempt to split an item under any circumstances. Use No-Split buffers when items must occupy
|
||||
contiguous memory. *Only this buffer type allows you to get the data item address and write
|
||||
to the item by yourself.* Refer the documentation of the functions :cpp:func:`xRingbufferSendAcquire` and :cpp:func:`xRingbufferSendComplete` for more details.
|
||||
**No-Split buffers** will guarantee that an item is stored in contiguous memory and will not attempt to split an item under any circumstances. Use No-Split buffers when items must occupy contiguous memory. *Only this buffer type allows you to get the data item address and write to the item by yourself.* Refer the documentation of the functions :cpp:func:`xRingbufferSendAcquire` and :cpp:func:`xRingbufferSendComplete` for more details.
|
||||
|
||||
**Allow-Split buffers** will allow an item to be split in two parts when wrapping around the end of the buffer if there
|
||||
is enough space at the tail and the head of the buffer combined to store the item. Allow-Split buffers are more memory
|
||||
efficient than No-Split buffers but can return an item in two parts when retrieving.
|
||||
**Allow-Split buffers** will allow an item to be split in two parts when wrapping around the end of the buffer if there is enough space at the tail and the head of the buffer combined to store the item. Allow-Split buffers are more memory efficient than No-Split buffers but can return an item in two parts when retrieving.
|
||||
|
||||
**Byte buffers** do not store data as separate items. All data is stored as a sequence of bytes,
|
||||
and any number of bytes can be sent or retrieved each time. Use byte buffers when separate items
|
||||
do not need to be maintained (e.g. a byte stream).
|
||||
**Byte buffers** do not store data as separate items. All data is stored as a sequence of bytes, and any number of bytes can be sent or retrieved each time. Use byte buffers when separate items do not need to be maintained (e.g. a byte stream).
|
||||
|
||||
.. note::
|
||||
No-Split buffers and Allow-Split buffers will always store items at 32-bit aligned addresses. Therefore, when
|
||||
retrieving an item, the item pointer is guaranteed to be 32-bit aligned. This is useful
|
||||
especially when you need to send some data to the DMA.
|
||||
No-Split buffers and Allow-Split buffers will always store items at 32-bit aligned addresses. Therefore, when retrieving an item, the item pointer is guaranteed to be 32-bit aligned. This is useful especially when you need to send some data to the DMA.
|
||||
|
||||
.. note::
|
||||
Each item stored in No-Split or Allow-Split buffers will **require an additional 8 bytes for a header**.
|
||||
Item sizes will also be rounded up to a 32-bit aligned size (multiple of 4 bytes), however the true
|
||||
item size is recorded within the header. The sizes of No-Split and Allow-Split buffers will also
|
||||
be rounded up when created.
|
||||
Each item stored in No-Split or Allow-Split buffers will **require an additional 8 bytes for a header**. Item sizes will also be rounded up to a 32-bit aligned size (multiple of 4 bytes), however the true item size is recorded within the header. The sizes of No-Split and Allow-Split buffers will also be rounded up when created.
|
||||
|
||||
Usage
|
||||
^^^^^
|
||||
|
||||
The following example demonstrates the usage of :cpp:func:`xRingbufferCreate`
|
||||
and :cpp:func:`xRingbufferSend` to create a ring buffer and then send an item to it.
|
||||
The following example demonstrates the usage of :cpp:func:`xRingbufferCreate` and :cpp:func:`xRingbufferSend` to create a ring buffer and then send an item to it.
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
@@ -82,11 +64,7 @@ and :cpp:func:`xRingbufferSend` to create a ring buffer and then send an item to
|
||||
printf("Failed to send item\n");
|
||||
}
|
||||
|
||||
The following example demonstrates the usage of :cpp:func:`xRingbufferSendAcquire` and
|
||||
:cpp:func:`xRingbufferSendComplete` instead of :cpp:func:`xRingbufferSend` to acquire
|
||||
memory on the ring buffer (of type `RINGBUF_TYPE_NOSPLIT`) and then send an item to it. This
|
||||
adds one more step, but allows getting the address of the memory to write to, and writing to the
|
||||
memory yourself.
|
||||
The following example demonstrates the usage of :cpp:func:`xRingbufferSendAcquire` and :cpp:func:`xRingbufferSendComplete` instead of :cpp:func:`xRingbufferSend` to acquire memory on the ring buffer (of type `RINGBUF_TYPE_NOSPLIT`) and then send an item to it. This adds one more step, but allows getting the address of the memory to write to, and writing to the memory yourself.
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
@@ -123,8 +101,7 @@ memory yourself.
|
||||
printf("Failed to send item\n");
|
||||
}
|
||||
|
||||
The following example demonstrates retrieving and returning an item from a **No-Split ring buffer**
|
||||
using :cpp:func:`xRingbufferReceive` and :cpp:func:`vRingbufferReturnItem`
|
||||
The following example demonstrates retrieving and returning an item from a **No-Split ring buffer** using :cpp:func:`xRingbufferReceive` and :cpp:func:`vRingbufferReturnItem`
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
@@ -149,8 +126,7 @@ using :cpp:func:`xRingbufferReceive` and :cpp:func:`vRingbufferReturnItem`
|
||||
}
|
||||
|
||||
|
||||
The following example demonstrates retrieving and returning an item from an **Allow-Split ring buffer**
|
||||
using :cpp:func:`xRingbufferReceiveSplit` and :cpp:func:`vRingbufferReturnItem`
|
||||
The following example demonstrates retrieving and returning an item from an **Allow-Split ring buffer** using :cpp:func:`xRingbufferReceiveSplit` and :cpp:func:`vRingbufferReturnItem`
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
@@ -181,8 +157,7 @@ using :cpp:func:`xRingbufferReceiveSplit` and :cpp:func:`vRingbufferReturnItem`
|
||||
}
|
||||
|
||||
|
||||
The following example demonstrates retrieving and returning an item from a **byte buffer**
|
||||
using :cpp:func:`xRingbufferReceiveUpTo` and :cpp:func:`vRingbufferReturnItem`
|
||||
The following example demonstrates retrieving and returning an item from a **byte buffer** using :cpp:func:`xRingbufferReceiveUpTo` and :cpp:func:`vRingbufferReturnItem`
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
@@ -207,8 +182,7 @@ using :cpp:func:`xRingbufferReceiveUpTo` and :cpp:func:`vRingbufferReturnItem`
|
||||
}
|
||||
|
||||
|
||||
For ISR safe versions of the functions used above, call :cpp:func:`xRingbufferSendFromISR`, :cpp:func:`xRingbufferReceiveFromISR`,
|
||||
:cpp:func:`xRingbufferReceiveSplitFromISR`, :cpp:func:`xRingbufferReceiveUpToFromISR`, and :cpp:func:`vRingbufferReturnItemFromISR`
|
||||
For ISR safe versions of the functions used above, call :cpp:func:`xRingbufferSendFromISR`, :cpp:func:`xRingbufferReceiveFromISR`, :cpp:func:`xRingbufferReceiveSplitFromISR`, :cpp:func:`xRingbufferReceiveUpToFromISR`, and :cpp:func:`vRingbufferReturnItemFromISR`
|
||||
|
||||
.. note::
|
||||
|
||||
@@ -217,154 +191,99 @@ For ISR safe versions of the functions used above, call :cpp:func:`xRingbufferSe
|
||||
Sending to Ring Buffer
|
||||
^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
The following diagrams illustrate the differences between No-Split and Allow-Split buffers as compared to
|
||||
byte buffers with regard to sending items/data. The diagrams assume that three
|
||||
items of sizes **18, 3, and 27 bytes** are sent respectively to a **buffer of 128 bytes**.
|
||||
The following diagrams illustrate the differences between No-Split and Allow-Split buffers as compared to byte buffers with regard to sending items/data. The diagrams assume that three items of sizes **18, 3, and 27 bytes** are sent respectively to a **buffer of 128 bytes**.
|
||||
|
||||
.. packetdiag:: ../../../_static/diagrams/ring-buffer/ring_buffer_send_non_byte_buf.diag
|
||||
:caption: Sending items to No-Split or Allow-Split ring buffers
|
||||
:align: center
|
||||
|
||||
For No-Split and Allow-Split buffers, a header of 8 bytes precedes every data item. Furthermore, the space
|
||||
occupied by each item is **rounded up to the nearest 32-bit aligned size** in order to maintain overall
|
||||
32-bit alignment. However, the true size of the item is recorded inside the header which will be
|
||||
returned when the item is retrieved.
|
||||
For No-Split and Allow-Split buffers, a header of 8 bytes precedes every data item. Furthermore, the space occupied by each item is **rounded up to the nearest 32-bit aligned size** in order to maintain overall 32-bit alignment. However, the true size of the item is recorded inside the header which will be returned when the item is retrieved.
|
||||
|
||||
Referring to the diagram above, the 18, 3, and 27 byte items are **rounded up to 20, 4, and 28 bytes**
|
||||
respectively. An 8 byte header is then added in front of each item.
|
||||
Referring to the diagram above, the 18, 3, and 27 byte items are **rounded up to 20, 4, and 28 bytes** respectively. An 8 byte header is then added in front of each item.
|
||||
|
||||
.. packetdiag:: ../../../_static/diagrams/ring-buffer/ring_buffer_send_byte_buf.diag
|
||||
:caption: Sending items to byte buffers
|
||||
:align: center
|
||||
|
||||
Byte buffers treat data as a sequence of bytes and does not incur any overhead
|
||||
(no headers). As a result, all data sent to a byte buffer is merged into a single item.
|
||||
Byte buffers treat data as a sequence of bytes and does not incur any overhead (no headers). As a result, all data sent to a byte buffer is merged into a single item.
|
||||
|
||||
Referring to the diagram above, the 18, 3, and 27 byte items are sequentially written to the
|
||||
byte buffer and **merged into a single item of 48 bytes**.
|
||||
Referring to the diagram above, the 18, 3, and 27 byte items are sequentially written to the byte buffer and **merged into a single item of 48 bytes**.
|
||||
|
||||
Using SendAcquire and SendComplete
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Items in No-Split buffers are acquired (by ``SendAcquire``) in strict FIFO order and must be sent to
|
||||
the buffer by ``SendComplete`` for the data to be accessible by the consumer. Multiple items can be
|
||||
sent or acquired without calling ``SendComplete``, and the items do not necessarily need to be
|
||||
completed in the order they were acquired. However, the receiving of data items must occur in FIFO
|
||||
order, therefore not calling ``SendComplete`` for the earliest acquired item will prevent the subsequent
|
||||
items from being received.
|
||||
Items in No-Split buffers are acquired (by ``SendAcquire``) in strict FIFO order and must be sent to the buffer by ``SendComplete`` for the data to be accessible by the consumer. Multiple items can be sent or acquired without calling ``SendComplete``, and the items do not necessarily need to be completed in the order they were acquired. However, the receiving of data items must occur in FIFO order, therefore not calling ``SendComplete`` for the earliest acquired item will prevent the subsequent items from being received.
|
||||
|
||||
The following diagrams illustrate what will happen when ``SendAcquire`` and ``SendComplete`` don't happen in
|
||||
the same order. At the beginning, there is already a data item of 16 bytes sent to the ring
|
||||
buffer. Then ``SendAcquire`` is called to acquire space of 20, 8, 24 bytes on the ring buffer.
|
||||
The following diagrams illustrate what will happen when ``SendAcquire`` and ``SendComplete`` don't happen in the same order. At the beginning, there is already a data item of 16 bytes sent to the ring buffer. Then ``SendAcquire`` is called to acquire space of 20, 8, 24 bytes on the ring buffer.
|
||||
|
||||
.. packetdiag:: ../../../_static/diagrams/ring-buffer/ring_buffer_send_acquire_complete.diag
|
||||
:caption: SendAcquire/SendComplete items in No-Split ring buffers
|
||||
:align: center
|
||||
|
||||
After that, we fill (use) the buffers, and send them to the ring buffer by ``SendComplete`` in the
|
||||
order of 8, 24, 20. When 8 bytes and 24 bytes data are sent, the consumer still can only get the
|
||||
16 bytes data item. Hence, if ``SendComplete`` is not called for the 20 bytes, it will not be available, nor will
|
||||
the data items following the 20 bytes item.
|
||||
After that, we fill (use) the buffers, and send them to the ring buffer by ``SendComplete`` in the order of 8, 24, 20. When 8 bytes and 24 bytes data are sent, the consumer still can only get the 16 bytes data item. Hence, if ``SendComplete`` is not called for the 20 bytes, it will not be available, nor will the data items following the 20 bytes item.
|
||||
|
||||
When the 20 bytes item is finally completed, all the 3 data items can be received now, in the
|
||||
order of 20, 8, 24 bytes, right after the 16 bytes item existing in the buffer at the beginning.
|
||||
When the 20 bytes item is finally completed, all the 3 data items can be received now, in the order of 20, 8, 24 bytes, right after the 16 bytes item existing in the buffer at the beginning.
|
||||
|
||||
Allow-Split buffers and byte buffers do not allow using ``SendAcquire`` or ``SendComplete`` since acquired buffers are
|
||||
required to be complete (not wrapped).
|
||||
Allow-Split buffers and byte buffers do not allow using ``SendAcquire`` or ``SendComplete`` since acquired buffers are required to be complete (not wrapped).
|
||||
|
||||
|
||||
Wrap around
|
||||
^^^^^^^^^^^
|
||||
|
||||
The following diagrams illustrate the differences between No-Split, Allow-Split, and byte
|
||||
buffers when a sent item requires a wrap around. The diagrams assume a buffer of **128 bytes**
|
||||
with **56 bytes of free space that wraps around** and a sent item of **28 bytes**.
|
||||
The following diagrams illustrate the differences between No-Split, Allow-Split, and byte buffers when a sent item requires a wrap around. The diagrams assume a buffer of **128 bytes** with **56 bytes of free space that wraps around** and a sent item of **28 bytes**.
|
||||
|
||||
.. packetdiag:: ../../../_static/diagrams/ring-buffer/ring_buffer_wrap_no_split.diag
|
||||
:caption: Wrap around in No-Split buffers
|
||||
:align: center
|
||||
|
||||
No-Split buffers will **only store an item in continuous free space and will not split
|
||||
an item under any circumstances**. When the free space at the tail of the buffer is insufficient
|
||||
to completely store the item and its header, the free space at the tail will be **marked as dummy data**.
|
||||
The buffer will then wrap around and store the item in the free space at the head of the buffer.
|
||||
No-Split buffers will **only store an item in continuous free space and will not split an item under any circumstances**. When the free space at the tail of the buffer is insufficient to completely store the item and its header, the free space at the tail will be **marked as dummy data**. The buffer will then wrap around and store the item in the free space at the head of the buffer.
|
||||
|
||||
Referring to the diagram above, the 16 bytes of free space at the tail of the buffer is
|
||||
insufficient to store the 28 byte item. Therefore, the 16 bytes is marked as dummy data and
|
||||
the item is written to the free space at the head of the buffer instead.
|
||||
Referring to the diagram above, the 16 bytes of free space at the tail of the buffer is insufficient to store the 28 byte item. Therefore, the 16 bytes is marked as dummy data and the item is written to the free space at the head of the buffer instead.
|
||||
|
||||
.. packetdiag:: ../../../_static/diagrams/ring-buffer/ring_buffer_wrap_allow_split.diag
|
||||
:caption: Wrap around in Allow-Split buffers
|
||||
:align: center
|
||||
|
||||
Allow-Split buffers will attempt to **split the item into two parts** when the free space at the tail
|
||||
of the buffer is insufficient to store the item data and its header. Both parts of the
|
||||
split item will have their own headers (therefore incurring an extra 8 bytes of overhead).
|
||||
Allow-Split buffers will attempt to **split the item into two parts** when the free space at the tail of the buffer is insufficient to store the item data and its header. Both parts of the split item will have their own headers (therefore incurring an extra 8 bytes of overhead).
|
||||
|
||||
Referring to the diagram above, the 16 bytes of free space at the tail of the buffer is insufficient
|
||||
to store the 28 byte item. Therefore, the item is split into two parts (8 and 20 bytes) and written
|
||||
as two parts to the buffer.
|
||||
Referring to the diagram above, the 16 bytes of free space at the tail of the buffer is insufficient to store the 28 byte item. Therefore, the item is split into two parts (8 and 20 bytes) and written as two parts to the buffer.
|
||||
|
||||
.. note::
|
||||
Allow-Split buffers treat both parts of the split item as two separate items, therefore call
|
||||
:cpp:func:`xRingbufferReceiveSplit` instead of :cpp:func:`xRingbufferReceive` to receive both
|
||||
parts of a split item in a thread safe manner.
|
||||
Allow-Split buffers treat both parts of the split item as two separate items, therefore call :cpp:func:`xRingbufferReceiveSplit` instead of :cpp:func:`xRingbufferReceive` to receive both parts of a split item in a thread safe manner.
|
||||
|
||||
.. packetdiag:: ../../../_static/diagrams/ring-buffer/ring_buffer_wrap_byte_buf.diag
|
||||
:caption: Wrap around in byte buffers
|
||||
:align: center
|
||||
|
||||
Byte buffers will **store as much data as possible into the free space at the tail of buffer**. The remaining
|
||||
data will then be stored in the free space at the head of the buffer. No overhead is incurred when wrapping
|
||||
around in byte buffers.
|
||||
Byte buffers will **store as much data as possible into the free space at the tail of buffer**. The remaining data will then be stored in the free space at the head of the buffer. No overhead is incurred when wrapping around in byte buffers.
|
||||
|
||||
Referring to the diagram above, the 16 bytes of free space at the tail of the buffer is insufficient to
|
||||
completely store the 28 bytes of data. Therefore, the 16 bytes of free space is filled with data, and the
|
||||
remaining 12 bytes are written to the free space at the head of the buffer. The buffer now contains
|
||||
data in two separate continuous parts, and each continuous part will be treated as a separate item by the
|
||||
byte buffer.
|
||||
Referring to the diagram above, the 16 bytes of free space at the tail of the buffer is insufficient to completely store the 28 bytes of data. Therefore, the 16 bytes of free space is filled with data, and the remaining 12 bytes are written to the free space at the head of the buffer. The buffer now contains data in two separate continuous parts, and each continuous part will be treated as a separate item by the byte buffer.
|
||||
|
||||
Retrieving/Returning
|
||||
^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
The following diagrams illustrate the differences between No-Split and Allow-Split buffers as compared to
|
||||
byte buffers in retrieving and returning data.
|
||||
The following diagrams illustrate the differences between No-Split and Allow-Split buffers as compared to byte buffers in retrieving and returning data.
|
||||
|
||||
.. packetdiag:: ../../../_static/diagrams/ring-buffer/ring_buffer_read_ret_non_byte_buf.diag
|
||||
:caption: Retrieving/Returning items in No-Split and Allow-Split ring buffers
|
||||
:align: center
|
||||
|
||||
Items in No-Split buffers and Allow-Split buffers are **retrieved in strict FIFO order** and **must be returned**
|
||||
for the occupied space to be freed. Multiple items can be retrieved before returning, and the items
|
||||
do not necessarily need to be returned in the order they were retrieved. However, the freeing of space
|
||||
must occur in FIFO order, therefore not returning the earliest retrieved item will prevent the space
|
||||
of subsequent items from being freed.
|
||||
Items in No-Split buffers and Allow-Split buffers are **retrieved in strict FIFO order** and **must be returned** for the occupied space to be freed. Multiple items can be retrieved before returning, and the items do not necessarily need to be returned in the order they were retrieved. However, the freeing of space must occur in FIFO order, therefore not returning the earliest retrieved item will prevent the space of subsequent items from being freed.
|
||||
|
||||
Referring to the diagram above, the **16, 20, and 8 byte items are retrieved in FIFO order**. However, the items
|
||||
are not returned in the order they were retrieved. First, the 20 byte item is returned followed by the 8 byte and the 16
|
||||
byte items. The space is not freed until the first item, i.e., the 16 byte item is returned.
|
||||
Referring to the diagram above, the **16, 20, and 8 byte items are retrieved in FIFO order**. However, the items are not returned in the order they were retrieved. First, the 20 byte item is returned followed by the 8 byte and the 16 byte items. The space is not freed until the first item, i.e., the 16 byte item is returned.
|
||||
|
||||
.. packetdiag:: ../../../_static/diagrams/ring-buffer/ring_buffer_read_ret_byte_buf.diag
|
||||
:caption: Retrieving/Returning data in byte buffers
|
||||
:align: center
|
||||
|
||||
Byte buffers **do not allow multiple retrievals before returning** (every retrieval must be followed by a return
|
||||
before another retrieval is permitted). When using :cpp:func:`xRingbufferReceive` or
|
||||
:cpp:func:`xRingbufferReceiveFromISR`, all continuous stored data will be retrieved. :cpp:func:`xRingbufferReceiveUpTo`
|
||||
or :cpp:func:`xRingbufferReceiveUpToFromISR` can be used to restrict the maximum number of bytes retrieved. Since
|
||||
every retrieval must be followed by a return, the space will be freed as soon as the data is returned.
|
||||
Byte buffers **do not allow multiple retrievals before returning** (every retrieval must be followed by a return before another retrieval is permitted). When using :cpp:func:`xRingbufferReceive` or :cpp:func:`xRingbufferReceiveFromISR`, all continuous stored data will be retrieved. :cpp:func:`xRingbufferReceiveUpTo` or :cpp:func:`xRingbufferReceiveUpToFromISR` can be used to restrict the maximum number of bytes retrieved. Since every retrieval must be followed by a return, the space will be freed as soon as the data is returned.
|
||||
|
||||
Referring to the diagram above, the 38 bytes of continuous stored data at the tail of the buffer is retrieved,
|
||||
returned, and freed. The next call to :cpp:func:`xRingbufferReceive` or :cpp:func:`xRingbufferReceiveFromISR`
|
||||
then wraps around and does the same to the 30 bytes of continuous stored data at the head of the buffer.
|
||||
Referring to the diagram above, the 38 bytes of continuous stored data at the tail of the buffer is retrieved, returned, and freed. The next call to :cpp:func:`xRingbufferReceive` or :cpp:func:`xRingbufferReceiveFromISR` then wraps around and does the same to the 30 bytes of continuous stored data at the head of the buffer.
|
||||
|
||||
Ring Buffers with Queue Sets
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Ring buffers can be added to FreeRTOS queue sets using :cpp:func:`xRingbufferAddToQueueSetRead` such that every
|
||||
time a ring buffer receives an item or data, the queue set is notified. Once added to a queue set, every
|
||||
attempt to retrieve an item from a ring buffer should be preceded by a call to :cpp:func:`xQueueSelectFromSet`.
|
||||
To check whether the selected queue set member is the ring buffer, call :cpp:func:`xRingbufferCanRead`.
|
||||
Ring buffers can be added to FreeRTOS queue sets using :cpp:func:`xRingbufferAddToQueueSetRead` such that every time a ring buffer receives an item or data, the queue set is notified. Once added to a queue set, every attempt to retrieve an item from a ring buffer should be preceded by a call to :cpp:func:`xQueueSelectFromSet`. To check whether the selected queue set member is the ring buffer, call :cpp:func:`xRingbufferCanRead`.
|
||||
|
||||
The following example demonstrates queue set usage with ring buffers.
|
||||
|
||||
@@ -444,96 +363,78 @@ The code snippet below demonstrates a ring buffer being allocated entirely in ex
|
||||
free(buffer_struct);
|
||||
free(buffer_storage);
|
||||
|
||||
Priority Inversion
|
||||
^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Ring Buffer API Reference
|
||||
-------------------------
|
||||
Ideally, ring buffers can be used with multiple tasks in an SMP fashion where the **highest priority task will always be serviced first.** However due to the usage of binary semaphores in the ring buffer's underlying implementation, priority inversion may occur under very specific circumstances.
|
||||
|
||||
.. note::
|
||||
Ideally, ring buffers can be used with multiple tasks in an SMP fashion where the **highest
|
||||
priority task will always be serviced first.** However due to the usage of binary semaphores
|
||||
in the ring buffer's underlying implementation, priority inversion may occur under very
|
||||
specific circumstances.
|
||||
The ring buffer governs sending by a binary semaphore which is given whenever space is freed on the ring buffer. The highest priority task waiting to send will repeatedly take the semaphore until sufficient free space becomes available or until it times out. Ideally this should prevent any lower priority tasks from being serviced as the semaphore should always be given to the highest priority task.
|
||||
|
||||
The ring buffer governs sending by a binary semaphore which is given whenever space is
|
||||
freed on the ring buffer. The highest priority task waiting to send will repeatedly take
|
||||
the semaphore until sufficient free space becomes available or until it times out. Ideally
|
||||
this should prevent any lower priority tasks from being serviced as the semaphore should
|
||||
always be given to the highest priority task.
|
||||
However, in between iterations of acquiring the semaphore, there is a **gap in the critical section** which may permit another task (on the other core or with an even higher priority) to free some space on the ring buffer and as a result give the semaphore. Therefore, the semaphore will be given before the highest priority task can re-acquire the semaphore. This will result in the **semaphore being acquired by the second-highest priority task** waiting to send, hence causing priority inversion.
|
||||
|
||||
However, in between iterations of acquiring the semaphore, there is a **gap in the critical
|
||||
section** which may permit another task (on the other core or with an even higher priority) to
|
||||
free some space on the ring buffer and as a result give the semaphore. Therefore, the semaphore
|
||||
will be given before the highest priority task can re-acquire the semaphore. This will result
|
||||
in the **semaphore being acquired by the second-highest priority task** waiting to send, hence
|
||||
causing priority inversion.
|
||||
|
||||
This side effect will not affect ring buffer performance drastically given if the number
|
||||
of tasks using the ring buffer simultaneously is low, and the ring buffer is not operating
|
||||
near maximum capacity.
|
||||
|
||||
.. include-build-file:: inc/ringbuf.inc
|
||||
This side effect will not affect ring buffer performance drastically given if the number of tasks using the ring buffer simultaneously is low, and the ring buffer is not operating near maximum capacity.
|
||||
|
||||
|
||||
.. _hooks:
|
||||
.. ------------------------------------------------------ Hooks --------------------------------------------------------
|
||||
|
||||
Hooks
|
||||
-----
|
||||
|
||||
FreeRTOS consists of Idle Hooks and Tick Hooks which allow for application
|
||||
specific functionality to be added to the Idle Task and Tick Interrupt.
|
||||
ESP-IDF provides its own Idle and Tick Hook API in addition to the hooks
|
||||
provided by vanilla FreeRTOS. ESP-IDF hooks have the added benefit of
|
||||
being run time configurable and asymmetrical.
|
||||
FreeRTOS consists of Idle Hooks and Tick Hooks which allow for application specific functionality to be added to the Idle Task and Tick Interrupt. ESP-IDF provides its own Idle and Tick Hook API in addition to the hooks provided by vanilla FreeRTOS. ESP-IDF hooks have the added benefit of being run time configurable and asymmetrical.
|
||||
|
||||
Vanilla FreeRTOS Hooks
|
||||
^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Idle and Tick Hooks in vanilla FreeRTOS are implemented by the user
|
||||
defining the functions ``vApplicationIdleHook()`` and ``vApplicationTickHook()``
|
||||
respectively somewhere in the application. Vanilla FreeRTOS will run the user
|
||||
defined Idle Hook and Tick Hook on every iteration of the Idle Task and Tick
|
||||
Interrupt respectively.
|
||||
Idle and Tick Hooks in vanilla FreeRTOS are implemented by the user defining the functions ``vApplicationIdleHook()`` and ``vApplicationTickHook()`` respectively somewhere in the application. Vanilla FreeRTOS will run the user defined Idle Hook and Tick Hook on every iteration of the Idle Task and Tick Interrupt respectively.
|
||||
|
||||
Vanilla FreeRTOS hooks are referred to as **Legacy Hooks** in ESP-IDF FreeRTOS.
|
||||
To enable legacy hooks, :ref:`CONFIG_FREERTOS_LEGACY_HOOKS` should be enabled
|
||||
in :doc:`project configuration menu </api-reference/kconfig>`.
|
||||
Vanilla FreeRTOS hooks are referred to as **Legacy Hooks** in ESP-IDF FreeRTOS. To enable legacy hooks, :ref:`CONFIG_FREERTOS_LEGACY_HOOKS` should be enabled in :doc:`project configuration menu </api-reference/kconfig>`.
|
||||
|
||||
.. only:: not CONFIG_FREERTOS_UNICORE
|
||||
|
||||
Due to vanilla FreeRTOS being designed for single core, ``vApplicationIdleHook()``
|
||||
and ``vApplicationTickHook()`` can only be defined once. However, the {IDF_TARGET_NAME} is dual-core
|
||||
in nature, therefore same Idle Hook and Tick Hook are used for both cores (in other words,
|
||||
the hooks are symmetrical for both cores).
|
||||
Due to vanilla FreeRTOS being designed for single core, ``vApplicationIdleHook()`` and ``vApplicationTickHook()`` can only be defined once. However, the {IDF_TARGET_NAME} is dual-core in nature, therefore same Idle Hook and Tick Hook are used for both cores (in other words, the hooks are symmetrical for both cores).
|
||||
|
||||
ESP-IDF Idle and Tick Hooks
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
For some use-cases it may be necessary for the Idle Tasks or Tick Interrupts to execute multiple hooks
|
||||
that are configurable at run time.
|
||||
For some use-cases it may be necessary for the Idle Tasks or Tick Interrupts to execute multiple hooks that are configurable at run time.
|
||||
|
||||
.. only:: not CONFIG_FREERTOS_UNICORE
|
||||
|
||||
Furthermore, due to the dual-core nature of the {IDF_TARGET_NAME}, it may be necessary for some
|
||||
applications to have separate hooks for each core.
|
||||
Furthermore, due to the dual-core nature of the {IDF_TARGET_NAME}, it may be necessary for some applications to have separate hooks for each core.
|
||||
|
||||
Therefore, ESP-IDF provides its own hooks API in addition to the legacy hooks provided
|
||||
by vanilla FreeRTOS.
|
||||
Therefore, ESP-IDF provides its own hooks API in addition to the legacy hooks provided by vanilla FreeRTOS.
|
||||
|
||||
The ESP-IDF tick and idle hooks are registered at run time. Each tick hook and idle hook
|
||||
must be registered to a specific CPU. When the idle task runs or a tick interrupt
|
||||
occurs on a particular CPU, the CPU will run each of its registered idle hook and tick hook
|
||||
in turn.
|
||||
The ESP-IDF tick and idle hooks are registered at run time. Each tick hook and idle hook must be registered to a specific CPU. When the idle task runs or a tick interrupt occurs on a particular CPU, the CPU will run each of its registered idle hook and tick hook in turn.
|
||||
|
||||
.. note:: Tick interrupt stays active whilst cache is disabled and hence ``vApplicationTickHook()`` (legacy case) or ESP-IDF tick hooks must be placed in internal RAM. Please refer to the :ref:`SPI flash API documentation <iram-safe-interrupt-handlers>` for more details.
|
||||
.. note::
|
||||
Tick interrupt stays active whilst cache is disabled and hence ``vApplicationTickHook()`` (legacy case) or ESP-IDF tick hooks must be placed in internal RAM. Please refer to the :ref:`SPI flash API documentation <iram-safe-interrupt-handlers>` for more details.
|
||||
|
||||
|
||||
Hooks API Reference
|
||||
-------------------
|
||||
.. -------------------------------------------------- TLSP Callback ----------------------------------------------------
|
||||
|
||||
.. include-build-file:: inc/esp_freertos_hooks.inc
|
||||
TLSP Deletion Callbacks
|
||||
-----------------------
|
||||
|
||||
Vanilla FreeRTOS provides a Thread Local Storage Pointers (TLSP) feature. These are pointers stored directly in the Task Control Block (TCB) of a particular task. TLSPs allow each task to have its own unique set of pointers to data structures. Vanilla FreeRTOS expects users to...
|
||||
|
||||
- set a task's TLSPs by calling :cpp:func:`vTaskSetThreadLocalStoragePointer` after the task has been created.
|
||||
- get a task's TLSPs by calling :cpp:func:`pvTaskGetThreadLocalStoragePointer` during the task's lifetime.
|
||||
- free the memory pointed to by the TLSPs before the task is deleted.
|
||||
|
||||
However, there can be instances where users may want the freeing of TLSP memory to be automatic. Therefore, ESP-IDF FreeRTOS provides the additional feature of TLSP deletion callbacks. These user provided deletion callbacks are called automatically when a task is deleted, thus allows the TLSP memory to be cleaned up without needing to add the cleanup logic explicitly to the code of every task.
|
||||
|
||||
The TLSP deletion callbacks are set in a similar fashion to the TLSPs themselves.
|
||||
|
||||
- :cpp:func:`vTaskSetThreadLocalStoragePointerAndDelCallback` sets both a particular TLSP and its associated callback.
|
||||
- Calling the Vanilla FreeRTOS function :cpp:func:`vTaskSetThreadLocalStoragePointer` will simply set the TLSP's associated Deletion Callback to `NULL` meaning that no callback will be called for that TLSP during task deletion.
|
||||
|
||||
When implementing TLSP callbacks, users should note the following:
|
||||
|
||||
- The callback **must never attempt to block or yield** and critical sections should be kept as short as possible
|
||||
- The callback is called shortly before a deleted task's memory is freed. Thus, the callback can either be called from :cpp:func:`vTaskDelete` itself, or from the idle task.
|
||||
|
||||
|
||||
.. _component-specific-properties:
|
||||
.. ------------------------------------------ Component Specific Properties --------------------------------------------
|
||||
|
||||
Component Specific Properties
|
||||
-----------------------------
|
||||
@@ -541,3 +442,19 @@ Component Specific Properties
|
||||
Besides standard component variables that are available with basic cmake build properties, FreeRTOS component also provides arguments (only one so far) for simpler integration with other modules:
|
||||
|
||||
- `ORIG_INCLUDE_PATH` - contains an absolute path to freertos root include folder. Thus instead of `#include "freertos/FreeRTOS.h"` you can refer to headers directly: `#include "FreeRTOS.h"`.
|
||||
|
||||
|
||||
.. -------------------------------------------------- API Reference ----------------------------------------------------
|
||||
|
||||
API Reference
|
||||
-------------
|
||||
|
||||
Ring Buffer API
|
||||
^^^^^^^^^^^^^^^
|
||||
|
||||
.. include-build-file:: inc/ringbuf.inc
|
||||
|
||||
Hooks API
|
||||
^^^^^^^^^
|
||||
|
||||
.. include-build-file:: inc/esp_freertos_hooks.inc
|
||||
|
||||
Reference in New Issue
Block a user