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