xref: /aosp_15_r20/external/pigweed/pw_thread_embos/docs.rst (revision 61c4878ac05f98d0ceed94b57d316916de578985)
1.. _module-pw_thread_embos:
2
3===============
4pw_thread_embos
5===============
6This is a set of backends for pw_thread based on embOS v4.
7
8.. Warning::
9  This module is still under construction, the API is not yet stable.
10
11-----------------------
12Thread Creation Backend
13-----------------------
14A backend or ``pw::Thread`` is offered using ``OS_CreateTaskEx()``. Optional
15joining support is enabled via an ``OS_EVENT`` in each thread's context.
16
17This backend permits users to start threads where contexts must be explicitly
18allocated and passed in as an option. As a quick example, a detached thread
19can be created as follows:
20
21.. code-block:: cpp
22
23   #include "pw_thread/detached_thread.h"
24   #include "pw_thread_embos/config.h"
25   #include "pw_thread_embos/context.h"
26   #include "pw_thread_embos/options.h"
27   #include "RTOS.h"  // For the embOS types.
28
29   constexpr OS_PRIO kFooPriority =
30       pw::thread::embos::config::kDefaultPriority;
31   constexpr OS_UINT kFooTimeSliceInterval =
32       pw::thread::embos::config::kDefaultTimeSliceInterval;
33   constexpr size_t kFooStackSizeWords =
34       pw::thread::embos::config::kDefaultStackSizeWords;
35
36   pw::thread::embos::ContextWithStack<kFooStackSizeWords>
37       example_thread_context;
38   void StartExampleThread() {
39     pw::thread::DetachedThread(
40         pw::thread::embos::Options()
41             .set_name("example_thread")
42             .set_priority(kFooPriority)
43             .set_time_slice_interval(kFooTimeSliceInterval)
44             .set_context(example_thread_context),
45         example_thread_function);
46   }
47
48
49Module Configuration Options
50============================
51The following configurations can be adjusted via compile-time configuration of
52this module, see the
53:ref:`module documentation <module-structure-compile-time-configuration>` for
54more details.
55
56.. c:macro:: PW_THREAD_EMBOS_CONFIG_JOINING_ENABLED
57
58  Whether thread joining is enabled. By default this is disabled.
59
60  We suggest only enabling this when thread joining is required to minimize
61  the RAM and ROM cost of threads.
62
63  Enabling this grows the RAM footprint of every pw::Thread as it adds an
64  OS_EVENT to every thread's pw::thread::embos::Context. In addition, there is a
65  minute ROM cost to construct and destroy this added object.
66
67  PW_THREAD_JOINING_ENABLED gets set to this value.
68
69.. c:macro:: PW_THREAD_EMBOS_CONFIG_MINIMUM_STACK_SIZE_WORDS
70
71  The minimum stack size in words. By default this uses Segger's recommendation
72  of 68 bytes.
73
74.. c:macro:: PW_THREAD_EMBOS_CONFIG_DEFAULT_STACK_SIZE_WORDS
75
76  The default stack size in words. By default this uses Segger's recommendation
77  of 256 bytes to start.
78
79.. c:macro:: PW_THREAD_EMBOS_CONFIG_MAX_THREAD_NAME_LEN
80
81  The maximum length of a thread's name, not including null termination. By
82  default this is arbitrarily set to 15. This results in an array of characters
83  which is this length + 1 bytes in every ``pw::Thread``'s context.
84
85.. c:macro:: PW_THREAD_EMBOS_CONFIG_MIN_PRIORITY
86
87  The minimum priority level, this is normally 1, since 0 is not a valid
88  priority level.
89
90.. c:macro:: PW_THREAD_EMBOS_CONFIG_DEFAULT_PRIORITY
91
92  The default priority level. By default this uses the minimal embOS priority.
93
94.. c:macro:: PW_THREAD_EMBOS_CONFIG_DEFAULT_TIME_SLICE_INTERVAL
95
96  The round robin time slice tick interval for threads at the same priority.
97  By default this is set to 2 ticks based on the embOS default.
98
99.. c:macro:: PW_THREAD_EMBOS_CONFIG_LOG_LEVEL
100
101  The log level to use for this module. Logs below this level are omitted.
102
103embOS Thread Options
104====================
105.. cpp:class:: pw::thread::embos::Options
106
107  .. cpp:function:: set_name(const char* name)
108
109    Sets the name for the embOS task, this is optional.
110    Note that this will be deep copied into the context and may be truncated
111    based on ``PW_THREAD_EMBOS_CONFIG_MAX_THREAD_NAME_LEN``.
112
113  .. cpp:function:: set_priority(OS_PRIO priority)
114
115    Sets the priority for the embOS task. Higher values are higher priority,
116    see embOS OS_CreateTaskEx for more detail.
117    Precondition: This must be >= ``PW_THREAD_EMBOS_CONFIG_MIN_PRIORITY``.
118
119  .. cpp:function:: set_time_slice_interval(OS_UINT time_slice_interval)
120
121    Sets the number of ticks this thread is allowed to run before other ready
122    threads of the same priority are given a chance to run.
123
124    A value of 0 disables time-slicing of this thread.
125
126    Precondition: This must be <= 255 ticks.
127
128  .. cpp:function:: set_context(pw::thread::embos::Context& context)
129
130    Set the pre-allocated context (all memory needed to run a thread). Note that
131    this is required for this thread creation backend! The ``Context`` can
132    either be constructed with an externally provided ``pw::span<OS_UINT>``
133    stack or the templated form of ``ContextWithStack<kStackSizeWords>`` can
134    be used.
135
136
137-----------------------------
138Thread Identification Backend
139-----------------------------
140A backend for ``pw::Thread::id`` and ``pw::thread::get_id()`` is offerred using
141``OS_GetTaskID()``. It uses ``DASSERT`` to ensure that the scheduler has started
142via ``OS_IsRunning()``.
143
144--------------------
145Thread Sleep Backend
146--------------------
147A backend for ``pw::thread::sleep_for()`` and ``pw::thread::sleep_until()`` is
148offerred using ``OS_Delay()`` if the duration is at least one tick, else
149``OS_Yield()`` is used. It uses ``pw::this_thread::get_id() != Thread::id()`` to
150ensure it invoked only from a thread.
151
152--------------------
153Thread Yield Backend
154--------------------
155A backend for ``pw::thread::yield()`` is offered using via ``OS_Yield()``.
156It uses ``pw::this_thread::get_id() != Thread::id()`` to ensure it invoked only
157from a thread.
158
159---------
160Utilities
161---------
162``ForEachThread()``
163===================
164In cases where an operation must be performed for every thread,
165``ForEachThread()`` can be used to iterate over all the created thread TCBs.
166Note that it's only safe to use this while the scheduler is suspended, and this
167should only be used after ``OS_Start()`` has been called. Calling this before
168the scheduler has started is non-fatal, but will result in no action and a
169``FailedPrecondition`` error code.
170
171An ``Aborted`` error status is returned if the provided callback returns
172``false`` to request an early termination of thread iteration.
173
174*Return values*
175
176* ``FailedPrecondition``: Returned when ``ForEachThread()`` is run before the OS
177  has been initialized.
178* ``Aborted``: The callback requested an early-termination of thread iteration.
179* ``OkStatus``: The callback has been successfully run with every thread.
180
181--------------------
182Snapshot Integration
183--------------------
184This ``pw_thread`` backend provides helper functions that capture embOS thread
185info to a ``pw::Thread`` proto.
186
187``SnapshotThreads()``
188=====================
189``SnapshotThreads()`` captures the thread name, state, and stack information for
190the provided embOS TCB to a ``pw::Thread`` protobuf encoder. To ensure the most
191up-to-date information is captured, the stack pointer for the currently running
192thread must be provided for cases where the running thread is being captured.
193For ARM Cortex-M CPUs, you can do something like this:
194
195.. code-block:: cpp
196
197   // Capture PSP.
198   void* stack_ptr = 0;
199   asm volatile("mrs %0, psp\n" : "=r"(stack_ptr));
200   pw::thread::ProcessThreadStackCallback cb =
201       [](pw::thread::proto::Thread::StreamEncoder& encoder,
202          pw::ConstByteSpan stack) -> pw::Status {
203     return encoder.WriteRawStack(stack);
204   };
205   pw::thread::embos::SnapshotThread(my_thread, stack_ptr,
206                                     snapshot_encoder, cb);
207
208``SnapshotThreads()`` wraps the singular thread capture to instead captures
209all created threads to a ``pw::thread::proto::SnapshotThreadInfo`` message.
210This proto message overlays a snapshot, so it is safe to static cast a
211``pw::snapshot::Snapshot::StreamEncoder`` to a
212``pw::thread::proto::SnapshotThreadInfo::StreamEncoder`` when calling this
213function.
214
215Thread Name Capture
216-------------------
217In order to capture thread names when snapshotting a thread, embOS must have
218``OS_TRACKNAME`` enabled. If ``OS_TRACKNAME`` is disabled, no thread name
219is captured. Enabling this is strongly recommended for debugability.
220
221Thread State Capture
222--------------------
223embOS thread state is not part of embOS's public API. Despite this, the
224snapshot integration captures thread state based on information on how the
225thread state is represented from
226`Segger's public forum <https://forum.segger.com/index.php/Thread/6548-ABANDONED-Task-state-values/?postID=23963#post23963>`_.
227This has been tested on embOS 4.22, and was initially
228reported for embOS 5.06. The logic Pigweed uses to interpret thread state may
229be incorrect for other versions of embOS.
230
231Thread Stack Capture
232--------------------
233Full thread stack information capture is dependent on embOS tracking the stack
234bounds for each task. When either ``OS_SUPPORT_MPU`` or ``OS_CHECKSTACK`` are
235enabled, stack bounds are tracked and the callback for thread stack dumping
236will be called. If both of these options are disabled, ``stack_start_pointer``
237and ``stack_end_pointer`` will not be captured, and the
238``ProcessThreadStackCallback`` will not be called.
239