zephyr/embassy.rs
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 a Zephyr semaphore to suspend the
52//! executor thread when there is no work to perform. This feature is incompatible with either
53//! `embassy-thread` or `embassy-interrupt` in the `embassy-executor` crate.
54//! - **`embassy-time-driver`**: This feature causes the `zephyr` crate to provide a time driver to
55//! Embassy. This driver uses a single `k_timer` in Zephyr to wake async operations that are
56//! dependent on time. This enables the `embassy-time` crate's functionality to be used freely
57//! within async tasks on Zephyr.
58//!
59//! Future versions of this support will provide async interfaces to various driver systems in
60//! Zephyr, allowing the use of Zephyr drivers freely from async code.
61//!
62//! It is perfectly permissible to use the `executor-thread` feature from embassy-executor on
63//! Zephyr, within the following guidelines:
64//!
65//! - This executor does not coordinate with the scheduler on Zephyr, but uses an
66//! architecture-specific mechanism when there is no work. On Cortex-M, this is the 'wfe'
67//! instruction, on riscv32, the 'wfi' instruction. This means that no tasks of lower priority
68//! will ever run, so this should only be started from the lowest priority task on the system.
69//! - Because the 'idle' thread in Zephyr will never run, some platforms will not enter low power
70//! mode, when the system is idle. This is very platform specific.
71//!
72//! ## Caveats
73//!
74//! [`Semaphore::take_async`]: crate::sys::sync::Semaphore::take_async
75
76#[cfg(feature = "time-driver")]
77mod time_driver;
78
79#[cfg(feature = "executor-zephyr")]
80pub use executor::Executor;
81#[cfg(feature = "executor-zephyr")]
82mod executor;