blob: b74ef66e01deec831d5c73b7398c6690bd1dac54 [file] [log] [blame]
Ewout van Bekkum749342b2021-01-19 14:53:19 -08001.. _module-pw_thread_freertos:
2
Ewout van Bekkumb9b1d362021-05-14 17:16:07 -07003==================
Ewout van Bekkum749342b2021-01-19 14:53:19 -08004pw_thread_freertos
Ewout van Bekkumb9b1d362021-05-14 17:16:07 -07005==================
6This is a set of backends for pw_thread based on FreeRTOS.
Ewout van Bekkum749342b2021-01-19 14:53:19 -08007
Ewout van Bekkumb9b1d362021-05-14 17:16:07 -07008.. contents::
9 :local:
10 :depth: 1
11
12.. Warning::
13 This module is still under construction, the API is not yet stable.
14
15-----------------------
16Thread Creation Backend
17-----------------------
18A backend for ``pw::thread::Thread`` is offered using ``xTaskCreateStatic()``.
19Optional dynamic allocation for threads is supported using ``xTaskCreate()``.
20Optional joining support is enabled via an ``StaticEventGroup_t`` in each
21thread's context.
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"
Ewout van Bekkumc2931da2021-07-27 16:39:48 -070031 #include "pw_thread_freertos/config.h"
Ewout van Bekkumb9b1d362021-05-14 17:16:07 -070032 #include "pw_thread_freertos/context.h"
33 #include "pw_thread_freertos/options.h"
34
Ewout van Bekkumc2931da2021-07-27 16:39:48 -070035 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;
Ewout van Bekkumb9b1d362021-05-14 17:16:07 -070042 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"
Ewout van Bekkumc2931da2021-07-27 16:39:48 -070059 #include "pw_thread_freertos/config.h"
Ewout van Bekkumb9b1d362021-05-14 17:16:07 -070060 #include "pw_thread_freertos/context.h"
61 #include "pw_thread_freertos/options.h"
62
Ewout van Bekkumc2931da2021-07-27 16:39:48 -070063 constexpr UBaseType_t kFooPriority =
64 pw::thread::freertos::config::kDefaultPriority;
65 constexpr size_t kFooStackSizeWords =
66 pw::thread::freertos::config::kDefaultStackSizeWords;
67
Ewout van Bekkumb9b1d362021-05-14 17:16:07 -070068 void StartExampleThread() {
69 pw::thread::DetachedThread(
70 pw::thread::freertos::Options()
71 .set_name("dyanmic_example_thread")
72 .set_priority(kFooPriority)
Ewout van Bekkumc2931da2021-07-27 16:39:48 -070073 .set_stack_size(kFooStackSizeWords),
Ewout van Bekkumb9b1d362021-05-14 17:16:07 -070074 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::Thread`` as it
93 adds a ``StaticEventGroup_t`` to every thread's
94 ``pw::thread::freertos::Context``. In addition, there is a minute ROM cost to
95 construct and destroy this added 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 stack size in words. By default this uses the minimal FreeRTOS
114 priority level above the idle priority (``tskIDLE_PRIORITY + 1``).
115
116FreeRTOS Thread Options
117=======================
118.. cpp:class:: pw::thread::freertos::Options
119
120 .. cpp:function:: set_name(const char* name)
121
122 Sets the name for the FreeRTOS task, note that this will be truncated
123 based on ``configMAX_TASK_NAME_LEN``. This is deep copied by FreeRTOS into
124 the task's task control block (TCB).
125
126 .. cpp:function:: set_priority(UBaseType_t priority)
127
128 Sets the priority for the FreeRTOS task. This must be a value between
129 ``tskIDLE_PRIORITY`` or ``0`` to ``configMAX_PRIORITIES - 1``. Higher
130 priority values have a higher priority.
131
132 .. cpp:function:: set_stack_size(size_t size_words)
133
134 Set the stack size in words for a dynamically thread.
135
136 This is only available if
137 ``PW_THREAD_FREERTOS_CONFIG_DYNAMIC_ALLOCATION_ENABLED`` is enabled.
138
139 Precondition: size_words must be >= ``configMINIMAL_STACK_SIZE``
140
141 .. cpp:function:: set_static_context(pw::thread::freertos::Context& context)
142
143 Set the pre-allocated context (all memory needed to run a thread). The
144 ``StaticContext`` can either be constructed with an externally provided
145 ``std::span<StackType_t>`` stack or the templated form of
146 ``StaticContextWithStack<kStackSizeWords>`` can be used.
147
148
149-----------------------------
150Thread Identification Backend
151-----------------------------
152A backend for ``pw::thread::Id`` and ``pw::thread::get_id()`` is offerred using
153``xTaskGetCurrentTaskHandle()``. It uses ``DASSERT`` to ensure that it is not
154invoked from interrupt context and if possible that the scheduler has started
155via ``xTaskGetSchedulerState()``.
156
157--------------------
158Thread Sleep Backend
159--------------------
160A backend for ``pw::thread::sleep_for()`` and ``pw::thread::sleep_until()`` is
161offerred using ``vTaskDelay()`` if the duration is at least one tick, else
162``taskYIELD()`` is used. It uses ``pw::this_thread::get_id() != thread::Id()``
163to ensure it invoked only from a thread.
164
165--------------------
166Thread Yield Backend
167--------------------
168A backend for ``pw::thread::yield()`` is offered using via ``taskYIELD()``.
169It uses ``pw::this_thread::get_id() != thread::Id()`` to ensure it invoked only
170from a thread.
Ewout van Bekkumdb698f32021-07-14 20:58:39 -0700171
172---------
173utilities
174---------
175In cases where an operation must be performed for every thread,
176``ForEachThread()`` can be used to iterate over all the created thread TCBs.
177Note that it's only safe to use this while the scheduler and interrupts are
178disabled.
179
Ewout van Bekkumb40a6b62021-07-23 21:32:49 -0700180Calling this before the scheduler has started, via ``vTaskStartScheduler()``, is
181non-fatal but will result in no action and a ``FailedPrecondition`` error code.
182
183An ``Aborted`` error status is returned if the provided callback returns
184``false`` to request an early termination of thread iteration.
185
186Return values
187=============
188
189* ``FailedPrecondition``: Returned when ``ForEachThread()`` is run before the OS
190 has been initialized.
191* ``Aborted``: The callback requested an early-termination of thread iteration.
192* ``OkStatus``: The callback has been successfully run with every thread.
193
Ewout van Bekkumdb698f32021-07-14 20:58:39 -0700194.. Note:: This uses an unsupported method to iterate the threads in a more
195 efficient manner while also supporting interrupt contexts. This requires
196 linking against internal statics from the FreeRTOS kernel,
197 :ref:`pw_third_party_freertos_DISABLE_TASKS_STATICS <third_party-freertos_disable_task_statics>`
198 must be used.
199
200--------------------
201Snapshot integration
202--------------------
203This ``pw_thread`` backend provides helper functions that capture FreeRTOS
204thread state to a ``pw::thread::Thread`` proto.
205
206FreeRTOS tskTCB facade
207======================
208Unfortunately FreeRTOS entirely hides the contents of the TCB inside of
209``Source/tasks.c``, but it's necessary for snapshot processing in order to
210access the stack limits from interrupt contexts. For this reason, FreeRTOS
211snapshot integration relies on the ``pw_thread_freertos:freertos_tsktcb`` facade
212to provide the ``tskTCB`` definition.
213
214The selected backend is expected to provide the ``struct tskTCB`` definition
215through ``pw_thread_freertos_backend/freertos_tsktcb.h``. The facade asserts
216that this definition matches the size of FreeRTOS's ``StaticTask_T`` which is
217the public opaque TCB type.
218
219SnapshotThread()/SnapshotThreads()
220==================================
221``SnapshotThread()`` captures the thread name, state, and stack information for
222the provided TCB to a ``pw::thread::Thread`` protobuf encoder. To ensure
223the most up-to-date information is captured, the stack pointer for the currently
224running thread must be provided for cases where the running thread is being
225captured. For ARM Cortex-M CPUs, you can do something like this:
226
227.. Code:: cpp
228
229 // Capture PSP.
230 void* stack_ptr = 0;
231 asm volatile("mrs %0, psp\n" : "=r"(stack_ptr));
232 pw::thread::ProcessThreadStackCallback cb =
233 [](pw::thread::Thread::StreamEncoder& encoder,
234 pw::ConstByteSpan stack) -> pw::Status {
235 return encoder.WriteRawStack(stack);
236 };
237 pw::thread::threadx::SnapshotThread(my_thread, thread_state, stack_ptr,
238 snapshot_encoder, cb);
239
240``SnapshotThreads()`` wraps the singular thread capture to instead captures
241all created threads to a ``pw::thread::SnapshotThreadInfo`` message which also
242captures the thread state for you. This proto
243message overlays a snapshot, so it is safe to static cast a
244``pw::snapshot::Snapshot::StreamEncoder`` to a
245``pw::thread::SnapshotThreadInfo::StreamEncoder`` when calling this function.
246
247.. Note:: ``SnapshotThreads()`` is only safe to use this while the scheduler and
248 interrupts are disabled as it relies on ``ForEachThread()``.