xref: /aosp_15_r20/external/pigweed/pw_thread_freertos/docs.rst (revision 61c4878ac05f98d0ceed94b57d316916de578985)
1.. _module-pw_thread_freertos:
2
3==================
4pw_thread_freertos
5==================
6This is a set of backends for pw_thread based on FreeRTOS.
7
8.. Warning::
9  This module is still under construction, the API is not yet stable.
10
11-----------------------
12Thread Creation Backend
13-----------------------
14A backend for ``pw::Thread`` is offered using ``xTaskCreateStatic()``.
15Optional dynamic allocation for threads is supported using ``xTaskCreate()``.
16Optional joining support is enabled via an ``StaticEventGroup_t`` in each
17thread's context.
18
19.. Note::
20  Scheduler State API support is required in your FreeRTOS Configuration, i.e.
21  ``INCLUDE_xTaskGetSchedulerState == 1``.
22
23This backend always permits users to start threads where static contexts are
24passed in as an option. As a quick example, a detached thread can be created as
25follows:
26
27.. code-block:: cpp
28
29   #include "FreeRTOS.h"
30   #include "pw_thread/detached_thread.h"
31   #include "pw_thread_freertos/config.h"
32   #include "pw_thread_freertos/context.h"
33   #include "pw_thread_freertos/options.h"
34
35   constexpr UBaseType_t kFooPriority =
36       pw::thread::freertos::config::kDefaultPriority;
37   constexpr size_t kFooStackSizeWords =
38       pw::thread::freertos::config::kDefaultStackSizeWords;
39
40   pw::thread::freertos::StaticContextWithStack<kFooStackSizeWords>
41       example_thread_context;
42   void StartExampleThread() {
43     pw::thread::DetachedThread(
44         pw::thread::freertos::Options()
45             .set_name("static_example_thread")
46             .set_priority(kFooPriority)
47             .set_static_context(example_thread_context),
48         example_thread_function);
49   }
50
51Alternatively when ``PW_THREAD_FREERTOS_CONFIG_DYNAMIC_ALLOCATION_ENABLED`` is
52enabled, dynamic thread allocation can be used. The above example could instead
53be done as follows:
54
55.. code-block:: cpp
56
57   #include "FreeRTOS.h"
58   #include "pw_thread/detached_thread.h"
59   #include "pw_thread_freertos/config.h"
60   #include "pw_thread_freertos/context.h"
61   #include "pw_thread_freertos/options.h"
62
63   constexpr UBaseType_t kFooPriority =
64       pw::thread::freertos::config::kDefaultPriority;
65   constexpr size_t kFooStackSizeWords =
66       pw::thread::freertos::config::kDefaultStackSizeWords;
67
68   void StartExampleThread() {
69     pw::thread::DetachedThread(
70         pw::thread::freertos::Options()
71             .set_name("dyanmic_example_thread")
72             .set_priority(kFooPriority)
73             .set_stack_size(kFooStackSizeWords),
74         example_thread_function)
75   }
76
77
78Module Configuration Options
79============================
80The following configurations can be adjusted via compile-time configuration of
81this module, see the
82:ref:`module documentation <module-structure-compile-time-configuration>` for
83more details.
84
85.. c:macro:: PW_THREAD_FREERTOS_CONFIG_JOINING_ENABLED
86
87  Whether thread joining is enabled. By default this is disabled.
88
89  We suggest only enabling this when thread joining is required to minimize
90  the RAM and ROM cost of threads.
91
92  Enabling this grows the RAM footprint of every ``pw::Thread`` as it adds a
93  ``StaticEventGroup_t`` to every thread's ``pw::thread::freertos::Context``. In
94  addition, there is a minute ROM cost to construct and destroy this added
95  object.
96
97  ``PW_THREAD_JOINING_ENABLED`` gets set to this value.
98
99.. c:macro:: PW_THREAD_FREERTOS_CONFIG_DYNAMIC_ALLOCATION_ENABLED
100
101  Whether dynamic allocation for threads (stacks and contexts) is enabled. By
102  default this matches the FreeRTOS configuration on whether dynamic
103  allocations are enabled. Note that static contexts **must** be provided if
104  dynamic allocations are disabled.
105
106.. c:macro:: PW_THREAD_FREERTOS_CONFIG_DEFAULT_STACK_SIZE_WORDS
107
108   The default stack size in words. By default this uses the minimal FreeRTOS
109   stack size based on ``configMINIMAL_STACK_SIZE``.
110
111.. c:macro:: PW_THREAD_FREERTOS_CONFIG_DEFAULT_PRIORITY
112
113   The default thread priority. By default this uses the minimal FreeRTOS
114   priority level above the idle priority (``tskIDLE_PRIORITY + 1``).
115
116.. c:macro:: PW_THREAD_FREERTOS_CONFIG_MAXIMUM_PRIORITY
117
118  The maximum thread priority. By default this uses the value below the
119  number of priorities defined by the FreeRTOS configuration
120  (``configMAX_PRIORITIES - 1``).
121
122.. c:macro:: PW_THREAD_FREERTOS_CONFIG_LOG_LEVEL
123
124  The log level to use for this module. Logs below this level are omitted.
125
126FreeRTOS Thread Options
127=======================
128.. cpp:class:: pw::thread::freertos::Options
129
130  .. cpp:function:: set_name(const char* name)
131
132    Sets the name for the FreeRTOS task, note that this will be truncated
133    based on ``configMAX_TASK_NAME_LEN``. This is deep copied by FreeRTOS into
134    the task's task control block (TCB).
135
136  .. cpp:function:: set_priority(UBaseType_t priority)
137
138    Sets the priority for the FreeRTOS task. This must be a value between
139    ``0`` to ``PW_THREAD_FREERTOS_CONFIG_MAXIMUM_PRIORITY``. Higher priority
140    values have a higher priority.
141
142    Note that the idle task priority, ``tskIDLE_PRIORITY``, is fixed to ``0``.
143    See the `FreeRTOS documentation on the idle task
144    <https://www.freertos.org/RTOS-idle-task.html>`_ for more details.
145
146    Precondition: This must be <= PW_THREAD_FREERTOS_CONFIG_MAXIMUM_PRIORITY.
147
148  .. cpp:function:: set_stack_size(size_t size_words)
149
150    Set the stack size in words for a dynamically thread.
151
152    This is only available if
153    ``PW_THREAD_FREERTOS_CONFIG_DYNAMIC_ALLOCATION_ENABLED`` is enabled.
154
155    Precondition: size_words must be >= ``configMINIMAL_STACK_SIZE``
156
157  .. cpp:function:: set_static_context(pw::thread::freertos::Context& context)
158
159    Set the pre-allocated context (all memory needed to run a thread). The
160    ``StaticContext`` can either be constructed with an externally provided
161    ``pw::span<StackType_t>`` stack or the templated form of
162    ``StaticContextWithStack<kStackSizeWords>`` can be used.
163
164
165-----------------------------
166Thread Identification Backend
167-----------------------------
168A backend for ``pw::Thread::id`` and ``pw::thread::get_id()`` is offered using
169``xTaskGetCurrentTaskHandle()``. It uses ``DASSERT`` to ensure that it is not
170invoked from interrupt context and if possible that the scheduler has started
171via ``xTaskGetSchedulerState()``.
172
173------------------------
174Thread Iteration Backend
175------------------------
176``pw_thread_freertos_TSKTCB_BACKEND`` to be configured
177properly and ``pw_third_party_freertos_DISABLE_TASKS_STATICS`` to be enabled.
178To allow for peak stack usage measurement, the FreeRTOS config
179``INCLUDE_uxTaskGetStackHighWaterMark`` should also be enabled.
180
181--------------------
182Thread Sleep Backend
183--------------------
184A backend for ``pw::thread::sleep_for()`` and ``pw::thread::sleep_until()`` is
185offerred using ``vTaskDelay()`` if the duration is at least one tick, else
186``taskYIELD()`` is used. It uses ``pw::this_thread::get_id() != Thread::id()``
187to ensure it invoked only from a thread.
188
189--------------------
190Thread Yield Backend
191--------------------
192A backend for ``pw::thread::yield()`` is offered using via ``taskYIELD()``.
193It uses ``pw::this_thread::get_id() != Thread::id()`` to ensure it invoked only
194from a thread.
195
196---------------------------
197Test Thread Context Backend
198---------------------------
199A backend for ``pw::thread::TestThreadContext()`` is offered using default
200options and a static stack size of 8192 words.
201
202---------
203Utilities
204---------
205``ForEachThread()``
206===================
207In cases where an operation must be performed for every thread,
208``ForEachThread()`` can be used to iterate over all the created thread TCBs.
209Note that it's only safe to use this while the scheduler and interrupts are
210disabled.
211
212Calling this before the scheduler has started, via ``vTaskStartScheduler()``, is
213non-fatal but will result in no action and a ``FailedPrecondition`` error code.
214
215An ``Aborted`` error status is returned if the provided callback returns
216``false`` to request an early termination of thread iteration.
217
218*Return values*
219
220* ``FailedPrecondition``: Returned when ``ForEachThread()`` is run before the OS
221  has been initialized.
222* ``Aborted``: The callback requested an early-termination of thread iteration.
223* ``OkStatus``: The callback has been successfully run with every thread.
224
225.. Note:: This uses an unsupported method to iterate the threads in a more
226   efficient manner while also supporting interrupt contexts. This requires
227   linking against internal statics from the FreeRTOS kernel,
228   :ref:`pw_third_party_freertos_DISABLE_TASKS_STATICS <third_party-freertos_disable_task_statics>`
229   must be used.
230
231--------------------
232Snapshot integration
233--------------------
234This ``pw_thread`` backend provides helper functions that capture FreeRTOS
235thread state to a ``pw::Thread`` proto.
236
237FreeRTOS tskTCB facade
238======================
239Unfortunately FreeRTOS entirely hides the contents of the TCB inside of
240``Source/tasks.c``, but it's necessary for snapshot processing in order to
241access the stack limits from interrupt contexts. For this reason, FreeRTOS
242snapshot integration relies on the ``pw_thread_freertos:freertos_tsktcb`` facade
243to provide the ``tskTCB`` definition. By default, a header will automatically be
244generated from FreeRTOS's ``tasks.c`` file to work around this limitation.
245
246In the event that the automatic header generation is incompatible with your
247version of FreeRTOS, ``pw_thread_freertos_FREERTOS_TSKTCB_BACKEND`` must be
248configured to point to a source set that provides the ``struct tskTCB``
249definition through ``pw_thread_freertos_backend/freertos_tsktcb.h``. The facade
250asserts that this definition matches the size of FreeRTOS's ``StaticTask_T``
251which is the public opaque TCB type.
252
253``SnapshotThreads()``
254=====================
255``SnapshotThreads()`` captures the thread name, state, and stack information for
256the provided TCB to a ``pw::Thread`` protobuf encoder. To ensure the most
257up-to-date information is captured, the stack pointer for the currently running
258thread must be provided for cases where the running thread is being captured.
259For ARM Cortex-M CPUs, you can do something like this:
260
261.. code-block:: cpp
262
263   // Capture PSP.
264   void* stack_ptr = 0;
265   asm volatile("mrs %0, psp\n" : "=r"(stack_ptr));
266   pw::thread::ProcessThreadStackCallback cb =
267       [](pw::thread::proto::Thread::StreamEncoder& encoder,
268          pw::ConstByteSpan stack) -> pw::Status {
269     return encoder.WriteRawStack(stack);
270   };
271   pw::thread::threadx::SnapshotThread(my_thread, thread_state, stack_ptr,
272                                       snapshot_encoder, cb);
273
274Some FreeRTOS ports (e.g. RISC-V) automatically store the stack pointer back
275into the running thread's TCB upon exception entry, so there's no need to inject
276an updated stack pointer. If you're unsure of your platform's behavior, inject
277an updated stack pointer captured upon exception entry to be safe.
278
279``SnapshotThreads()`` wraps the singular thread capture to instead captures
280all created threads to a ``pw::thread::proto::SnapshotThreadInfo`` message
281which also captures the thread state for you. This proto
282message overlays a snapshot, so it is safe to static cast a
283``pw::snapshot::Snapshot::StreamEncoder`` to a
284``pw::thread::proto::SnapshotThreadInfo::StreamEncoder`` when calling this
285function.
286
287.. Note:: ``SnapshotThreads()`` is only safe to use this while the scheduler and
288   interrupts are disabled as it relies on ``ForEachThread()``.
289
290Thread Stack Capture
291--------------------
292Snapshot attempts to capture as much of the thread stack state as possible,
293however it can be limited by the FreeRTOS configuration.
294
295The ``stack_start_ptr`` can only be provided if the ``portSTACK_GROWTH`` is < 0,
296i.e. the stack grows down, when ``configRECORD_STACK_HIGH_ADDRESS`` is enabled.
297
298The ``stack_pointer_est_peak`` can only be provided when
299``config_USE_TRACE_FACILITY`` and/or ``INCLUDE_uxTaskGetStackHighWaterMark`` are
300enabled and ``stack_start_ptr``'s requirements above are met.
301