|
| 1 | +//! Support for Embassy on Rust+Zephyr |
| 2 | +//! |
| 3 | +//! [Embassy](https://embassy.dev/) is: "The next-generation framework for embedded applications". |
| 4 | +//! From a typical RTOS perspective it is perhaps a little difficult to explain what exactly it is, |
| 5 | +//! and why it makes sense to discuss it in the context of supporting Rust on Zephyr. |
| 6 | +//! |
| 7 | +//! At a core level, Embassy is a set of crates that implement various functionality that is used |
| 8 | +//! when writing bare metal applications in Rust. Combined, these provide most of the functionality |
| 9 | +//! that is needed for an embedded application. However, the crates are largely independent, and as |
| 10 | +//! such find use when combined with Zephyr. |
| 11 | +//! |
| 12 | +//! ## Executor |
| 13 | +//! |
| 14 | +//! A significant aspect of Embassy's functionality revolves around providing one or more executors |
| 15 | +//! for coordinating async code in Rust. The Rust language transforms code annotated with |
| 16 | +//! async/await into state machines that allow these operations to be run cooperatively. A bare |
| 17 | +//! metal system with one or more of these executors managing async tasks can indeed solve many of |
| 18 | +//! the types of scheduling solutions needed for embedded systems. |
| 19 | +//! |
| 20 | +//! Although Zephyr does have a thread scheduler, there are still some advantages to running an |
| 21 | +//! executor on one or more Zephyr threads: |
| 22 | +//! |
| 23 | +//! - Because the async code is transformed into a state machine, this code only uses stack while |
| 24 | +//! evaluating to the next stopping point. This allows a large number of async operations to |
| 25 | +//! happen on a single thread, without requiring additional stack. The state machines themselves |
| 26 | +//! do take memory, but this usage is known at compile time (with the stable Rust compiler, it is |
| 27 | +//! allocated from a pool, and with the nightly compiler, can be completely compile time |
| 28 | +//! determined). |
| 29 | +//! - Context switches between async threads can be very fast. When running a single executor |
| 30 | +//! thread, there is no need for locking for data that is entirely kept within that thread, and |
| 31 | +//! these context switches have similar cost to a function call. Even with multiple threads |
| 32 | +//! involved, many switches will happen on the same underlying Zephyr thread, reducing the need to |
| 33 | +//! reschedule. |
| 34 | +//! - Embassy provides a lot of mechanisms for coordinating between these tasks, all that work in |
| 35 | +//! the context of async/await. Some may be thought of as redundant with Zephyr primitives, but |
| 36 | +//! they serve a different purpose, and provide more streamlined coordination for things entirely |
| 37 | +//! within the Rust world. |
| 38 | +//! |
| 39 | +//! ## Use |
| 40 | +//! |
| 41 | +//! To best use this module, it is best to look at the various examples under `samples/embassy*` in |
| 42 | +//! this repo. Some of the embassy crates, especially embassy-executor have numerous features that |
| 43 | +//! must be configured correctly for proper operation. To use the 'executor-thread' feature, it is |
| 44 | +//! also necessary to configure embassy for the proper platform. Future versions of the Cmake files |
| 45 | +//! for Rust on Zephyr may provide assistance with this, but for now, this does limit a given |
| 46 | +//! application to running on a specific architecture. For using the `executor-zephyr` feature |
| 47 | +//! provided by this module, it easier to allow the code to run on multiple platforms. |
| 48 | +//! |
| 49 | +//! The following features in the `zephyr` crate configure what is supported: |
| 50 | +//! |
| 51 | +//! - **`executor-zephyr`**: This implements an executor that uses Zephyr's thread primitives |
| 52 | +//! (`k_thread_suspend` and `k_thread_resume`) to suspend the executor thread when there is no work |
| 53 | +//! to perform. This feature is incompatible with either `embassy-thread` or `embassy-interrupt` |
| 54 | +//! in the `embassy-executor` crate. |
| 55 | +//! - **`embassy-time-driver`**: This feature causes the `zephyr` crate to provide a time driver to |
| 56 | +//! Embassy. This driver uses a single `k_timer` in Zephyr to wake async operations that are |
| 57 | +//! dependent on time. This enables the `embassy-time` crate's functionality to be used freely |
| 58 | +//! within async tasks on Zephyr. |
| 59 | +//! |
| 60 | +//! Future versions of this support will provide async interfaces to various driver systems in |
| 61 | +//! Zephyr, allowing the use of Zephyr drivers freely from async code. |
| 62 | +//! |
| 63 | +//! It is perfectly permissible to use the `executor-thread` feature from embassy-executor on |
| 64 | +//! Zephyr, within the following guidelines: |
| 65 | +//! |
| 66 | +//! - The executor is incompatible with the async executor provided within [`crate::kio`], and |
| 67 | +//! because there are no features to enable this, this functions will still be accessible. Be |
| 68 | +//! careful. You should enable `no-kio` in the zephyr crate to hide these functions. |
| 69 | +//! - This executor does not coordinate with the scheduler on Zephyr, but uses an |
| 70 | +//! architecture-specific mechanmism when there is no work. On Cortex-M, this is the 'wfe' |
| 71 | +//! instruction, on riscv32, the 'wfi' instruction. This means that no tasks of lower priority |
| 72 | +//! will ever run, so this should only be started from the lowest priority task on the system. |
| 73 | +//! - Because the 'idle' thread in Zephyr will never run, some platforms will not enter low power |
| 74 | +//! mode, when the system is idle. This is very platform specific. |
| 75 | +//! |
| 76 | +//! ## Caveats |
| 77 | +//! |
| 78 | +//! The executor provided by Embassy is fundamentally incompatible with the executor provided by |
| 79 | +//! this crate's [`crate::kio`] and [`crate::work::futures`]. Trying to use the functionality |
| 80 | +//! provided by operations, such as [`Semaphore::take_async`], will generally result in a panic. |
| 81 | +//! These routines are conditionally compiled out when `executor-zephyr` is enabled, but there is no |
| 82 | +//! way for this crate to detect the use of embassy's `executor-threaded`. Combining these will |
| 83 | +//! result in undefined behavior, likely difficult to debug crashes. |
| 84 | +//! |
| 85 | +//! [`Semaphore::take_async`]: crate::sys::sync::Semaphore::take_async |
| 86 | +
|
| 87 | +#[cfg(feature = "time-driver")] |
| 88 | +mod time_driver; |
0 commit comments