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