Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add embassy support. #61

Open
d3zd3z opened this issue Feb 26, 2025 · 0 comments
Open

Add embassy support. #61

d3zd3z opened this issue Feb 26, 2025 · 0 comments
Labels
enhancement New feature or request

Comments

@d3zd3z
Copy link
Collaborator

d3zd3z commented Feb 26, 2025

Embassy "The next-generation framework for embedded applications. Write safe, correct and energy-efficient embedded code faster, using the Rust programming language, its async facilities, and the Embassy libraries."

This framework primarily provides extensive async support for various platforms. It can be used natively (bare metal), with an executor that schedules async execution on a single thread, or other executors that run in IRQ mode. There is also support for the RTIC framework.

Adding support so that this framework can be used within Zephyr is not particularly difficult. In fact, the single thread executor just works, as is, on top of Zephyr. If run from the lowest-priority thread, it is perfectly usable, although it might inhibit sleep in some cases. To be truly useful, there needs to be an embassy-time-driver implementation for Zephyr so that delaying and periodic intervals will work.

It also would not be that difficult to implement a specific variant of the embassy executor that would use Zephyr's thread primitives and allow an executor to be run per thread in Zephyr. This would allow multiple async tasks to run within a single Zephyr thread.

The main issue is that a traditional rust async executor will be incompatible with the scheduling primitives within Zephyr. However, embassy provides async versions of these primitives that will work fine within Zephyr. Driver callbacks can use the non-blocking "wake" versions of these primitives to allow coordination between Zephyr drivers and frameworks and the Embassy primitives.

To implement this, this proposes a few features:

  • embassy-time-driver: Enabling this within the zephyr crate will provide a time driver to Embassy so that the facilities in embassy-time can be used freely within Zephyr
  • embassy-executor: Enabling this will provide an executor, using Embassy-executor, that will run tasks on a single Zephyr thread (as a function that never returns). It is possible to run multiple of these, specifically on threads of different priorities).

Conflicts

The embassy-executor feature will be mutually exclusive with the executor-thread and executor-interrupt features on Embassy-executor. In addition, the _async operations on Zephyr primitives are incompatible with this executor. It might be reasonable to conditionalize these such that they are not available if the embassy-executor is chosen, but it is still possible for an app to bring in the embassy-thread executor.

Enabling executor-thread on embassy requires the architecture to be specified. This can be done in the Cargo.toml, but will make for a package that will only work on a single architecture. If useful, the CMake file could be extended to set these features as needed to allow multiple targets to be used.

Benefits

Within zephyr::kio and zephyr::work, the Zephyr crate provides an async executor that uses Rust work queues. This provides a few benefits:

  • This async support works with Zephyr's sync primitives (at least Semaphore, and Queue).
  • This code otherwise works more tightly with Zephyr operations.
    and some disadvantages:
  • There is effectively double scheduling overhead, the operations generally invoke both the work queue scheduler and the Zephyr thread scheduler together.
    The embassy-executor (embassy-thread) provides very low scheduling overhead for async tasks, but only supports running in a single Zephyr thread. The executor that this issue will provide will run on Zephyr threads. The additional overhead will be low, but non-zero. Waking async tasks will require a call into k_thread_resume, which if the thread is running, is a quick check, but does have a spin lock, and if it has to wake the thread does require a reschedule within Zephyr. However, this now allows these async managing threads to coexist with other threads within Zephyr, rather than just requiring it to be run on the lowest priority thread.
@d3zd3z d3zd3z added the enhancement New feature or request label Feb 26, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
Development

No branches or pull requests

1 participant