Skip to content

Initial support for using Embassy with Rust on Zephyr #63

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

Merged
merged 11 commits into from
Mar 4, 2025
Merged

Conversation

d3zd3z
Copy link
Collaborator

@d3zd3z d3zd3z commented Feb 28, 2025

These changes add the ability to use the executor from Embassy on Zephyr. For the most part, Embassy's executor and associated utilities (embassy-time, embassy-sync, embassy-futures) are fairly platform agnostic. This support adds the two things needed to make this all work on Zephyr:

  • time-driver. Enabling this feature will use a Zephyr timer to implement the embassy-timer-driver, so that all of the time related utilities from embassy-time work. There is a bit of challenge here as the embassy utilities expect the tick rate to be set via a feature, and in Zephyr this comes through Kconfig. But, once set correctly, the result should be seamless.
  • executor-zephyr. Implements an Executor that works just like embassy-executor's built in Executor type, except that it is intended to be run on a single Zephyr thread. More than one Executor can be run, each on its own thread, to provide a useful compromise between the lower-overhead scheduling of an executor, and priority-based scheduling that comes from using multiple threads.

Some simple benchmarking shows the following. All of these are measuring the round trip time between two async tasks sending a single message over a channel, and a result back.

  • 12us: Native Embassy switch time. This is with just the executor-thread executor that expects to be running on the single lowest priority thread.
  • 15us: The executor-zephyr executor, with all of the async tasks scheduled on a single executor. This includes the above switch time, along with a little overhead needed to support Zephyr threads.
  • 26us: The executor-zephyr executor, with one task of the pair running on a separate thread. This includes the zephyr context switch time in with the executor switch time.

All of these scenarios are useful in different situations.

Adds the Cargo.toml framework for initial support of using Embassy's
executor on Zephyr.  This implements a time driver for Embassy using a
`k_timer` from Zephyr.

Signed-off-by: David Brown <[email protected]>
This demo shows shows a few async tasks that are coordinated using the
Embassy embassy-thread executor.

Signed-off-by: David Brown <[email protected]>
This implements an executor for Embassy that runs a set of async tasks
on a single Zephyr thread.  The executor will suspend the thread when
there is no work to do, resuming when there is work.

It is permissible to run multiple executors, on different threads, to
support a hybrid async cooperative and priority-based scheduling.

Signed-off-by: David Brown <[email protected]>
Update the embassy demo to use the Zephyr executor.  This performs a
ping-ping test using two executors, with one end of the responder
running on another thread.

On the rp2040, the round trip time with `executor-threaded` is about
12us, with `executor-zephyr`, all on a single thread, it is about 15us,
and with pairs of task running across differrent threads, the time is
about 26us.

Signed-off-by: David Brown <[email protected]>
Update the formatting.

Signed-off-by: David Brown <[email protected]>
Run `cargo fmt`.

Signed-off-by: David Brown <[email protected]>
Copy link

@teburd teburd left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Running embassy on Zephyr wasn't on my 2025 bingo sheet, but I have to say this is really quite cool.

I wonder how this might work if you want to run the full embassy network stack in place of Zephyrs for example, where security might be a real honest to goodness concern and Rust would likely help. I kind of love the possibilities this opens up.

Or given a user mode thread could multiple embassy executors in some manner be sandboxed? Clearly accessing the hardware in a fine grained manner would need some thinking on that front.

@d3zd3z
Copy link
Collaborator Author

d3zd3z commented Feb 28, 2025

@teburd a couple of thoughts:

As far as using parts of Embassy instead of Rust. The main challenge here is hooking the device interrupts in so that the call into the hal. Since for most things in the hal, embassy requires this to be done by hand, this shouldn't be all that difficult. But, the mechanisms are very different, and Zephyr is going to need the irqs to still go through it's irq handling, as this will be needed in order for an irq to be able to cause the Zephyr scheduler to do something.

Otherwise, I would think it would then mostly just be an issue of disabling that functionality on the Zephyr side. Running a mix of these shouldn't be all that difficult.

As far as user mode goes, getting all of the permissions working would probably be a bit hard, especially since the hal is going to want access to hardware blocks. If there are enough MPU regions, this is probably doable, although it is a little unclear how user mode will help rust code, as it is largely redundant. It almost seems like more useful would be to have the userspace used to protect the rest of the system from some block of C code.

d3zd3z added 4 commits March 3, 2025 13:24
On 64-bit targets, the task Futures are larger, and the chosen pool size
is not adequate.  Increase to 2k, which still should fit in the memory
constraints on all targets, but not just fail immediately on 64-bit
targets.

Signed-off-by: David Brown <[email protected]>
Print a message after the test finish, and use this to terminate the
tests with success.

Signed-off-by: David Brown <[email protected]>
With the directives themselves removed, remove the import to avoid a
warning about unused imports.

Signed-off-by: David Brown <[email protected]>
Clippy complains about a no-argument `new()` method without an
accompanying Default.  It doesn't hurt anything to add, and could make
this potentially slightly more useful.

Signed-off-by: David Brown <[email protected]>
@d3zd3z d3zd3z merged commit bcc9ee4 into main Mar 4, 2025
4 checks passed
@d3zd3z d3zd3z mentioned this pull request Mar 14, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants