diff --git a/ci-nightly.sh b/ci-nightly.sh index c0a2482e7..d486d442c 100755 --- a/ci-nightly.sh +++ b/ci-nightly.sh @@ -21,5 +21,6 @@ cargo batch \ --- build --release --manifest-path embassy-executor/Cargo.toml --target riscv32imac-unknown-none-elf --features nightly,arch-riscv32 \ --- build --release --manifest-path embassy-executor/Cargo.toml --target riscv32imac-unknown-none-elf --features nightly,arch-riscv32,executor-thread \ --- build --release --manifest-path examples/nrf52840-rtic/Cargo.toml --target thumbv7em-none-eabi --artifact-dir out/examples/nrf52840-rtic \ + --- build --release --manifest-path embassy-executor/Cargo.toml --target armv7a-none-eabi --features nightly,arch-cortex-ar,executor-thread \ RUSTFLAGS="$RUSTFLAGS -C target-cpu=atmega328p" cargo build --release --manifest-path embassy-executor/Cargo.toml --target avr-none -Z build-std=core,alloc --features nightly,arch-avr,avr-device/atmega328p diff --git a/ci.sh b/ci.sh index d21c1c97b..e1fdf998a 100755 --- a/ci.sh +++ b/ci.sh @@ -35,6 +35,9 @@ cargo batch \ --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features arch-cortex-m,executor-thread \ --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features arch-cortex-m,executor-interrupt \ --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features arch-cortex-m,executor-thread,executor-interrupt \ + --- build --release --manifest-path embassy-executor/Cargo.toml --target armv7a-none-eabi --features arch-cortex-ar,executor-thread \ + --- build --release --manifest-path embassy-executor/Cargo.toml --target armv7r-none-eabi --features arch-cortex-ar,executor-thread \ + --- build --release --manifest-path embassy-executor/Cargo.toml --target armv7r-none-eabihf --features arch-cortex-ar,executor-thread \ --- build --release --manifest-path embassy-executor/Cargo.toml --target riscv32imac-unknown-none-elf --features arch-riscv32 \ --- build --release --manifest-path embassy-executor/Cargo.toml --target riscv32imac-unknown-none-elf --features arch-riscv32,executor-thread \ --- build --release --manifest-path embassy-sync/Cargo.toml --target thumbv6m-none-eabi --features defmt \ diff --git a/embassy-executor-macros/src/lib.rs b/embassy-executor-macros/src/lib.rs index 8e737db6a..962ce2e75 100644 --- a/embassy-executor-macros/src/lib.rs +++ b/embassy-executor-macros/src/lib.rs @@ -70,6 +70,31 @@ pub fn main_cortex_m(args: TokenStream, item: TokenStream) -> TokenStream { main::run(args.into(), item.into(), &main::ARCH_CORTEX_M).into() } +/// Creates a new `executor` instance and declares an application entry point for Cortex-A/R +/// spawning the corresponding function body as an async task. +/// +/// The following restrictions apply: +/// +/// * The function must accept exactly 1 parameter, an `embassy_executor::Spawner` handle that it +/// can use to spawn additional tasks. +/// * The function must be declared `async`. +/// * The function must not use generics. +/// * Only a single `main` task may be declared. +/// +/// ## Examples +/// Spawning a task: +/// +/// ``` rust +/// #[embassy_executor::main] +/// async fn main(_s: embassy_executor::Spawner) { +/// // Function body +/// } +/// ``` +#[proc_macro_attribute] +pub fn main_cortex_ar(args: TokenStream, item: TokenStream) -> TokenStream { + main::run(args.into(), item.into(), &main::ARCH_CORTEX_AR).into() +} + /// Creates a new `executor` instance and declares an architecture agnostic application entry point spawning /// the corresponding function body as an async task. /// diff --git a/embassy-executor-macros/src/macros/main.rs b/embassy-executor-macros/src/macros/main.rs index 95722b19b..a0e7b3401 100644 --- a/embassy-executor-macros/src/macros/main.rs +++ b/embassy-executor-macros/src/macros/main.rs @@ -37,6 +37,12 @@ pub static ARCH_CORTEX_M: Arch = Arch { executor_required: false, }; +pub static ARCH_CORTEX_AR: Arch = Arch { + default_entry: None, + flavor: Flavor::Standard, + executor_required: false, +}; + pub static ARCH_SPIN: Arch = Arch { default_entry: None, flavor: Flavor::Standard, diff --git a/embassy-executor/CHANGELOG.md b/embassy-executor/CHANGELOG.md index 5d2468c58..608c67724 100644 --- a/embassy-executor/CHANGELOG.md +++ b/embassy-executor/CHANGELOG.md @@ -5,6 +5,10 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## unreleased + +- Added support for Cortex-A and Cortex-R + ## 0.7.0 - 2025-01-02 - Performance optimizations. diff --git a/embassy-executor/Cargo.toml b/embassy-executor/Cargo.toml index 9f9eaa600..f014ccf30 100644 --- a/embassy-executor/Cargo.toml +++ b/embassy-executor/Cargo.toml @@ -45,6 +45,9 @@ portable-atomic = { version = "1.5", optional = true } # arch-cortex-m dependencies cortex-m = { version = "0.7.6", optional = true } +# arch-cortex-ar dependencies +cortex-ar = { version = "0.2", optional = true } + # arch-wasm dependencies wasm-bindgen = { version = "0.2.82", optional = true } js-sys = { version = "0.3", optional = true } @@ -73,6 +76,8 @@ _arch = [] # some arch was picked arch-std = ["_arch"] ## Cortex-M arch-cortex-m = ["_arch", "dep:cortex-m"] +## Cortex-A/R +arch-cortex-ar = ["_arch", "dep:cortex-ar"] ## RISC-V 32 arch-riscv32 = ["_arch"] ## WASM diff --git a/embassy-executor/src/arch/cortex_ar.rs b/embassy-executor/src/arch/cortex_ar.rs new file mode 100644 index 000000000..f9e2f3f7c --- /dev/null +++ b/embassy-executor/src/arch/cortex_ar.rs @@ -0,0 +1,84 @@ +#[cfg(feature = "executor-interrupt")] +compile_error!("`executor-interrupt` is not supported with `arch-cortex-ar`."); + +#[export_name = "__pender"] +#[cfg(any(feature = "executor-thread", feature = "executor-interrupt"))] +fn __pender(context: *mut ()) { + // `context` is always `usize::MAX` created by `Executor::run`. + let context = context as usize; + + #[cfg(feature = "executor-thread")] + // Try to make Rust optimize the branching away if we only use thread mode. + if !cfg!(feature = "executor-interrupt") || context == THREAD_PENDER { + cortex_ar::asm::sev(); + return; + } +} + +#[cfg(feature = "executor-thread")] +pub use thread::*; +#[cfg(feature = "executor-thread")] +mod thread { + pub(super) const THREAD_PENDER: usize = usize::MAX; + + use core::marker::PhantomData; + + use cortex_ar::asm::wfe; + pub use embassy_executor_macros::main_cortex_ar as main; + + use crate::{raw, Spawner}; + + /// Thread mode executor, using WFE/SEV. + /// + /// This is the simplest and most common kind of executor. It runs on + /// thread mode (at the lowest priority level), and uses the `WFE` ARM instruction + /// to sleep when it has no more work to do. When a task is woken, a `SEV` instruction + /// is executed, to make the `WFE` exit from sleep and poll the task. + /// + /// This executor allows for ultra low power consumption for chips where `WFE` + /// triggers low-power sleep without extra steps. If your chip requires extra steps, + /// you may use [`raw::Executor`] directly to program custom behavior. + pub struct Executor { + inner: raw::Executor, + not_send: PhantomData<*mut ()>, + } + + impl Executor { + /// Create a new Executor. + pub fn new() -> Self { + Self { + inner: raw::Executor::new(THREAD_PENDER as *mut ()), + not_send: PhantomData, + } + } + + /// Run the executor. + /// + /// The `init` closure is called with a [`Spawner`] that spawns tasks on + /// this executor. Use it to spawn the initial task(s). After `init` returns, + /// the executor starts running the tasks. + /// + /// To spawn more tasks later, you may keep copies of the [`Spawner`] (it is `Copy`), + /// for example by passing it as an argument to the initial tasks. + /// + /// This function requires `&'static mut self`. This means you have to store the + /// Executor instance in a place where it'll live forever and grants you mutable + /// access. There's a few ways to do this: + /// + /// - a [StaticCell](https://docs.rs/static_cell/latest/static_cell/) (safe) + /// - a `static mut` (unsafe) + /// - a local variable in a function you know never returns (like `fn main() -> !`), upgrading its lifetime with `transmute`. (unsafe) + /// + /// This function never returns. + pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! { + init(self.inner.spawner()); + + loop { + unsafe { + self.inner.poll(); + } + wfe(); + } + } + } +} diff --git a/embassy-executor/src/lib.rs b/embassy-executor/src/lib.rs index d6fd3d651..dfe420bab 100644 --- a/embassy-executor/src/lib.rs +++ b/embassy-executor/src/lib.rs @@ -26,6 +26,7 @@ macro_rules! check_at_most_one { check_at_most_one!( "arch-avr", "arch-cortex-m", + "arch-cortex-ar", "arch-riscv32", "arch-std", "arch-wasm", @@ -35,6 +36,7 @@ check_at_most_one!( #[cfg(feature = "_arch")] #[cfg_attr(feature = "arch-avr", path = "arch/avr.rs")] #[cfg_attr(feature = "arch-cortex-m", path = "arch/cortex_m.rs")] +#[cfg_attr(feature = "arch-cortex-ar", path = "arch/cortex_ar.rs")] #[cfg_attr(feature = "arch-riscv32", path = "arch/riscv32.rs")] #[cfg_attr(feature = "arch-std", path = "arch/std.rs")] #[cfg_attr(feature = "arch-wasm", path = "arch/wasm.rs")] diff --git a/rust-toolchain-nightly.toml b/rust-toolchain-nightly.toml index d803b29a0..e75ea40cc 100644 --- a/rust-toolchain-nightly.toml +++ b/rust-toolchain-nightly.toml @@ -9,4 +9,5 @@ targets = [ "thumbv8m.main-none-eabihf", "riscv32imac-unknown-none-elf", "wasm32-unknown-unknown", + "armv7a-none-eabi", ] diff --git a/rust-toolchain.toml b/rust-toolchain.toml index ca5816121..870904c3a 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -9,4 +9,7 @@ targets = [ "thumbv8m.main-none-eabihf", "riscv32imac-unknown-none-elf", "wasm32-unknown-unknown", + "armv7a-none-eabi", + "armv7r-none-eabi", + "armv7r-none-eabihf", ]