1.. _docs-os: 2 3========== 4OS support 5========== 6 7.. toctree:: 8 :hidden: 9 10 zephyr/index 11 12Pigweed’s operating system abstraction layers are portable and configurable 13building blocks, giving users full control while maintaining high performance 14and low overhead. 15 16Although we primarily target smaller-footprint MMU-less 32-bit microcontrollers, 17the OS abstraction layers are written to work on everything from single-core 18bare metal low end microcontrollers to asymmetric multiprocessing (AMP) and 19symmetric multiprocessing (SMP) embedded systems using Real Time Operating 20Systems (RTOS). They even fully work on your developer workstation on Linux, 21Windows, or MacOS! 22 23Pigweed has ports for the following systems: 24 25.. list-table:: 26 27 * - **Environment** 28 - **Status** 29 * - STL (Mac, Window, & Linux) 30 - **✔ Supported** 31 * - `FreeRTOS <https://www.freertos.org/>`_ 32 - **✔ Supported** 33 * - :ref:`Zephyr <docs-os-zephyr>` 34 - **✔ Supported** 35 * - `Azure RTOS (formerly ThreadX) <https://azure.microsoft.com/en-us/services/rtos/>`_ 36 - **✔ Supported** 37 * - `SEGGER embOS <https://www.segger.com/products/rtos/embos/>`_ 38 - **✔ Supported** 39 * - Baremetal 40 - *In Progress* 41 42Pigweed's OS abstraction layers are divided by the **functional grouping of the 43primitives**. Many of our APIs are similar or **nearly identical to C++'s 44Standard Template Library (STL)** with the notable exception that we do not 45support exceptions. We opted to follow the STL's APIs partially because they 46are relatively well thought out and many developers are already familiar with 47them, but also because this means they are compatible with existing helpers in 48the STL; for example, ``std::lock_guard``. 49 50--------------- 51Time Primitives 52--------------- 53The :ref:`module-pw_chrono` module provides the building blocks for expressing 54durations, timestamps, and acquiring the current time. This in turn is used by 55other modules, including :ref:`module-pw_sync` and :ref:`module-pw_thread` as 56the basis for any time bound APIs (i.e. with timeouts and/or deadlines). Note 57that this module is optional and bare metal targets may opt not to use this. 58 59.. list-table:: 60 61 * - **Supported On** 62 - **SystemClock** 63 * - FreeRTOS 64 - :ref:`module-pw_chrono_freertos` 65 * - Zephyr 66 - :ref:`module-pw_chrono_zephyr` 67 * - ThreadX 68 - :ref:`module-pw_chrono_threadx` 69 * - embOS 70 - :ref:`module-pw_chrono_embos` 71 * - STL 72 - :ref:`module-pw_chrono_stl` 73 * - Baremetal 74 - Planned 75 76 77System Clock 78============ 79For RTOS and HAL interactions, we provide a ``pw::chrono::SystemClock`` facade 80which provides 64 bit timestamps and duration support along with a C API. For 81C++ there is an optional virtual wrapper, ``pw::chrono::VirtualSystemClock``, 82around the singleton clock facade to enable dependency injection. 83 84.. code-block:: cpp 85 86 #include <chrono> 87 88 #include "pw_thread/sleep.h" 89 90 using namespace std::literals::chrono_literals; 91 92 void ThisSleeps() { 93 pw::thread::sleep_for(42ms); 94 } 95 96Unlike the STL's time bound templated APIs which are not specific to a 97particular clock, Pigweed's time bound APIs are strongly typed to use the 98``pw::chrono::SystemClock``'s ``duration`` and ``time_points`` directly. 99 100.. code-block:: cpp 101 102 #include "pw_chrono/system_clock.h" 103 104 bool HasThisPointInTimePassed(const SystemClock::time_point timestamp) { 105 return SystemClock::now() > timestamp; 106 } 107 108-------------------------- 109Synchronization Primitives 110-------------------------- 111The :ref:`module-pw_sync` provides the building blocks for synchronizing between 112threads and/or interrupts through signaling primitives and critical section lock 113primitives. 114 115Critical Section Lock Primitives 116================================ 117Pigweed's locks support Clang's thread safety lock annotations and the STL's 118RAII helpers. 119 120.. list-table:: 121 122 * - **Supported On** 123 - **Mutex** 124 - **TimedMutex** 125 - **InterruptSpinLock** 126 * - FreeRTOS 127 - :ref:`module-pw_sync_freertos` 128 - :ref:`module-pw_sync_freertos` 129 - :ref:`module-pw_sync_freertos` 130 * - Zephyr 131 - :ref:`module-pw_sync_zephyr` 132 - Planned 133 - :ref:`module-pw_sync_zephyr` 134 * - ThreadX 135 - :ref:`module-pw_sync_threadx` 136 - :ref:`module-pw_sync_threadx` 137 - :ref:`module-pw_sync_threadx` 138 * - embOS 139 - :ref:`module-pw_sync_embos` 140 - :ref:`module-pw_sync_embos` 141 - :ref:`module-pw_sync_embos` 142 * - STL 143 - :ref:`module-pw_sync_stl` 144 - :ref:`module-pw_sync_stl` 145 - :ref:`module-pw_sync_stl` 146 * - Baremetal 147 - Planned, not ready for use 148 - ✗ 149 - Planned, not ready for use 150 151 152Thread Safe Mutex 153----------------- 154The ``pw::sync::Mutex`` protects shared data from being simultaneously accessed 155by multiple threads. Optionally, the ``pw::sync::TimedMutex`` can be used as an 156extension with timeout and deadline based semantics. 157 158.. code-block:: cpp 159 160 #include <mutex> 161 162 #include "pw_sync/mutex.h" 163 164 pw::sync::Mutex mutex; 165 166 void ThreadSafeCriticalSection() { 167 std::lock_guard lock(mutex); 168 NotThreadSafeCriticalSection(); 169 } 170 171Interrupt Safe InterruptSpinLock 172-------------------------------- 173The ``pw::sync::InterruptSpinLock`` protects shared data from being 174simultaneously accessed by multiple threads and/or interrupts as a targeted 175global lock, with the exception of Non-Maskable Interrupts (NMIs). Unlike global 176interrupt locks, this also works safely and efficiently on SMP systems. 177 178.. code-block:: cpp 179 180 #include <mutex> 181 182 #include "pw_sync/interrupt_spin_lock.h" 183 184 pw::sync::InterruptSpinLock interrupt_spin_lock; 185 186 void InterruptSafeCriticalSection() { 187 std::lock_guard lock(interrupt_spin_lock); 188 NotThreadSafeCriticalSection(); 189 } 190 191Signaling Primitives 192==================== 193Native signaling primitives tend to vary more compared to critical section locks 194across different platforms. For example, although common signaling primitives 195like semaphores are in most if not all RTOSes and even POSIX, it was not in the 196STL before C++20. Likewise many C++ developers are surprised that conditional 197variables tend to not be natively supported on RTOSes. Although you can usually 198build any signaling primitive based on other native signaling primitives, 199this may come with non-trivial added overhead in ROM, RAM, and execution 200efficiency. 201 202For this reason, Pigweed intends to provide some simpler signaling primitives 203which exist to solve a narrow programming need but can be implemented as 204efficiently as possible for the platform that it is used on. This simpler but 205highly portable class of signaling primitives is intended to ensure that a 206portability efficiency tradeoff does not have to be made up front. 207 208.. list-table:: 209 210 * - **Supported On** 211 - **ThreadNotification** 212 - **TimedThreadNotification** 213 - **CountingSemaphore** 214 - **BinarySemaphore** 215 * - FreeRTOS 216 - :ref:`module-pw_sync_freertos` 217 - :ref:`module-pw_sync_freertos` 218 - :ref:`module-pw_sync_freertos` 219 - :ref:`module-pw_sync_freertos` 220 * - Zephyr 221 - :ref:`module-pw_sync_stl` 222 - :ref:`module-pw_sync_stl` 223 - Planned 224 - :ref:`module-pw_sync_stl` 225 * - ThreadX 226 - :ref:`module-pw_sync_threadx` 227 - :ref:`module-pw_sync_threadx` 228 - :ref:`module-pw_sync_threadx` 229 - :ref:`module-pw_sync_threadx` 230 * - embOS 231 - :ref:`module-pw_sync_embos` 232 - :ref:`module-pw_sync_embos` 233 - :ref:`module-pw_sync_embos` 234 - :ref:`module-pw_sync_embos` 235 * - STL 236 - :ref:`module-pw_sync_stl` 237 - :ref:`module-pw_sync_stl` 238 - :ref:`module-pw_sync_stl` 239 - :ref:`module-pw_sync_stl` 240 * - Baremetal 241 - Planned 242 - ✗ 243 - TBD 244 - TBD 245 246Thread Notification 247------------------- 248Pigweed intends to provide the ``pw::sync::ThreadNotification`` and 249``pw::sync::TimedThreadNotification`` facades which permit a single consumer to 250block until an event occurs. This should be backed by the most efficient native 251primitive for a target, regardless of whether that is a semaphore, event flag 252group, condition variable, direct task notification with a critical section, or 253something else. 254 255Counting Semaphore 256------------------ 257The ``pw::sync::CountingSemaphore`` is a synchronization primitive that can be 258used for counting events and/or resource management where receiver(s) can block 259on acquire until notifier(s) signal by invoking release. 260 261.. code-block:: cpp 262 263 #include "pw_sync/counting_semaphore.h" 264 265 pw::sync::CountingSemaphore event_semaphore; 266 267 void NotifyEventOccurred() { 268 event_semaphore.release(); 269 } 270 271 void HandleEventsForever() { 272 while (true) { 273 event_semaphore.acquire(); 274 HandleEvent(); 275 } 276 } 277 278Binary Semaphore 279---------------- 280The ``pw::sync::BinarySemaphore`` is a specialization of the counting semaphore 281with an arbitrary token limit of 1, meaning it's either full or empty. 282 283.. code-block:: cpp 284 285 #include "pw_sync/binary_semaphore.h" 286 287 pw::sync::BinarySemaphore do_foo_semaphore; 288 289 void NotifyResultReady() { 290 result_ready_semaphore.release(); 291 } 292 293 void BlockUntilResultReady() { 294 result_ready_semaphore.acquire(); 295 } 296 297-------------------- 298Threading Primitives 299-------------------- 300The :ref:`module-pw_thread` module provides the building blocks for creating and 301using threads including yielding and sleeping. 302 303.. list-table:: 304 305 * - **Supported On** 306 - **Thread Creation** 307 - **Thread Id/Sleep/Yield** 308 * - FreeRTOS 309 - :ref:`module-pw_thread_freertos` 310 - :ref:`module-pw_thread_freertos` 311 * - Zephyr 312 - :ref:`module-pw_thread_zephyr` 313 - :ref:`module-pw_thread_zephyr` 314 * - ThreadX 315 - :ref:`module-pw_thread_threadx` 316 - :ref:`module-pw_thread_threadx` 317 * - embOS 318 - :ref:`module-pw_thread_embos` 319 - :ref:`module-pw_thread_embos` 320 * - STL 321 - :ref:`module-pw_thread_stl` 322 - :ref:`module-pw_thread_stl` 323 * - Baremetal 324 - ✗ 325 - ✗ 326 327Thread Creation 328=============== 329The :cpp:type:`pw::Thread`’s API is C++11 STL ``std::thread`` like. Unlike 330``std::thread``, the Pigweed's API requires ``pw::thread::Options`` as an 331argument for creating a thread. This is used to give the user full control over 332the native OS's threading options without getting in your way. 333 334.. code-block:: cpp 335 336 #include "pw_thread/detached_thread.h" 337 #include "pw_thread_freertos/context.h" 338 #include "pw_thread_freertos/options.h" 339 340 pw::thread::freertos::ContextWithStack<42> example_thread_context; 341 342 void StartDetachedExampleThread() { 343 pw::thread::DetachedThread( 344 pw::thread::freertos::Options() 345 .set_name("static_example_thread") 346 .set_priority(kFooPriority) 347 .set_static_context(example_thread_context), 348 example_thread_function); 349 } 350 351Controlling the current thread 352============================== 353Beyond thread creation, Pigweed offers support for sleeping, identifying, and 354yielding the current thread. 355 356.. code-block:: cpp 357 358 #include "pw_thread/yield.h" 359 360 void CooperativeBusyLooper() { 361 while (true) { 362 DoChunkOfWork(); 363 pw::this_thread::yield(); 364 } 365 } 366 367------------------ 368Execution Contexts 369------------------ 370Code runs in *execution contexts*. Common examples of execution contexts on 371microcontrollers are **thread context** and **interrupt context**, though there 372are others. Since OS abstractions deal with concurrency, it's important to 373understand what API primitives are safe to call in what contexts. Since the 374number of execution contexts is too large for Pigweed to cover exhaustively, 375Pigweed has the following classes of APIs: 376 377**Thread Safe APIs** - These APIs are safe to use in any execution context where 378one can use blocking or yielding APIs such as sleeping, blocking on a mutex 379waiting on a semaphore. 380 381**Interrupt (IRQ) Safe APIs** - These APIs can be used in any execution context 382which cannot use blocking and yielding APIs. These APIs must protect themselves 383from preemption from maskable interrupts, etc. This includes critical section 384thread contexts in addition to "real" interrupt contexts. Our definition 385explicitly excludes any interrupts which are not masked when holding a SpinLock, 386those are all considered non-maskable interrupts. An interrupt safe API may 387always be safely used in a context which permits thread safe APIs. 388 389**Non-Maskable Interrupt (NMI) Safe APIs** - Like the Interrupt Safe APIs, these 390can be used in any execution context which cannot use blocking or yielding APIs. 391In addition, these may be used by interrupts which are not masked when for 392example holding a SpinLock like CPU exceptions or C++/POSIX signals. These tend 393to come with significant overhead and restrictions compared to regular interrupt 394safe APIs as they **cannot rely on critical sections**, instead 395only atomic signaling can be used. An interrupt safe API may always be 396used in a context which permits interrupt safe and thread safe APIs. 397 398On naming 399========= 400Instead of having context specific APIs like FreeRTOS's ``...FromISR()``, 401Pigweed has a single API which validates the context requirements through 402``DASSERT`` and ``DCHECK`` in the backends (user configurable). We did this for 403a few reasons: 404 405#. **Too many contexts** - Since there are contexts beyond just thread, 406 interrupt, and NMI, having context-specific APIs would be a hard to 407 maintain. The proliferation of postfixed APIs (``...FromISR``, 408 ``...FromNMI``, ``...FromThreadCriticalSection``, and so on) would also be 409 confusing for users. 410 411#. **Must verify context anyway** - Backends are required to enforce context 412 requirements with ``DCHECK`` or related calls, so we chose a simple API 413 which happens to match both the C++'s STL and Google's Abseil. 414 415#. **Multi-context code** - Code running in multiple contexts would need to be 416 duplicated for each context if the APIs were postfixed, or duplicated with 417 macros. The authors chose the duplication/macro route in previous projects 418 and found it clunky and hard to maintain. 419 420----------------------------- 421Construction & Initialization 422----------------------------- 423**TL;DR: Pigweed OS primitives are initialized through C++ construction.** 424 425We have chosen to go with a model which initializes the synchronization 426primitive during C++ object construction. This means that there is a requirement 427in order for static instantiation to be safe that the user ensures that any 428necessary kernel and/or platform initialization is done before the global static 429constructors are run which would include construction of the C++ synchronization 430primitives. 431 432In addition this model for now assumes that Pigweed code will always be used to 433construct synchronization primitives used with Pigweed modules. Note that with 434this model the backend provider can decide if they want to statically 435preallocate space for the primitives or rely on dynamic allocation strategies. 436If we discover at a later point that this is not sufficiently portable than we 437can either produce an optional constructor that takes in a reference to an 438existing native synchronization type and wastes a little bit RAM or we can 439refactor the existing class into two layers where one is a StaticMutex for 440example and the other is a Mutex which only holds a handle to the native mutex 441type. This would then permit users who cannot construct their synchronization 442primitives to skip the optional static layer. 443 444Kernel / Platform Initialization Before C++ Global Static Constructors 445====================================================================== 446What is this kernel and/or platform initialization that must be done first? 447 448It's not uncommon for an RTOS to require some initialization functions to be 449invoked before more of its API can be safely used. For example for CMSIS RTOSv2 450``osKernelInitialize()`` must be invoked before anything but two basic getters 451are called. Similarly, Segger's embOS requires ``OS_Init()`` to be invoked first 452before any other embOS API. 453 454.. Note:: 455 To get around this one should invoke these initialization functions earlier 456 and/or delay the static C++ constructors to meet this ordering requirement. As 457 an example if you were using :ref:`module-pw_boot_cortex_m`, then 458 ``pw_boot_PreStaticConstructorInit()`` would be a great place to invoke kernel 459 initialization. 460 461------- 462Roadmap 463------- 464Pigweed is still actively expanding and improving its OS Abstraction Layers. 465That being said, the following concrete areas are being worked on and can be 466expected to land at some point in the future: 467 4681. We'd like to offer a system clock based timer abstraction facade which can be 469 used on either an RTOS or a hardware timer. 4702. We are evaluating a less-portable but very useful portability facade for 471 event flags / groups. This would make it even easier to ensure all firmware 472 can be fully executed on the host. 4733. Cooperative cancellation thread joining along with a ``std::jthread`` like 474 wrapper is in progress. 4754. We'd like to add support for queues, message queues, and similar channel 476 abstractions which also support interprocessor communication in a transparent 477 manner. 4785. We're interested in supporting asynchronous worker queues and worker queue 479 pools. 4806. Migrate HAL and similar APIs to use deadlines for the backend virtual 481 interfaces to permit a smaller vtable which supports both public timeout and 482 deadline semantics. 4837. Baremetal support is partially in place today, but it's not ready for use. 4848. Most of our APIs today are focused around synchronous blocking APIs, however 485 we would love to extend this to include asynchronous APIs. 486