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