diff --git a/ci-nightly.sh b/ci-nightly.sh index 1fc9692b5..46b19c5b7 100755 --- a/ci-nightly.sh +++ b/ci-nightly.sh @@ -28,3 +28,6 @@ cargo batch \ --- build --release --manifest-path embassy-executor/Cargo.toml --target riscv32imac-unknown-none-elf --features nightly,arch-riscv32,executor-thread,integrated-timers \ --- build --release --manifest-path examples/nrf52840-rtic/Cargo.toml --target thumbv7em-none-eabi --out-dir out/examples/nrf52840-rtic \ +cargo build --release --manifest-path embassy-executor/Cargo.toml --target avr-unknown-gnu-atmega328 -Z build-std=core,alloc --features nightly,arch-avr,avr-device/atmega328p +cargo build --release --manifest-path embassy-executor/Cargo.toml --target avr-unknown-gnu-atmega328 -Z build-std=core,alloc --features nightly,arch-avr,integrated-timers,avr-device/atmega328p + diff --git a/embassy-executor-macros/src/lib.rs b/embassy-executor-macros/src/lib.rs index c9d58746a..5461fe04c 100644 --- a/embassy-executor-macros/src/lib.rs +++ b/embassy-executor-macros/src/lib.rs @@ -62,6 +62,13 @@ pub fn task(args: TokenStream, item: TokenStream) -> TokenStream { task::run(&args.meta, f).unwrap_or_else(|x| x).into() } +#[proc_macro_attribute] +pub fn main_avr(args: TokenStream, item: TokenStream) -> TokenStream { + let args = syn::parse_macro_input!(args as Args); + let f = syn::parse_macro_input!(item as syn::ItemFn); + main::run(&args.meta, f, main::avr()).unwrap_or_else(|x| x).into() +} + /// Creates a new `executor` instance and declares an application entry point for Cortex-M spawning the corresponding function body as an async task. /// /// The following restrictions apply: diff --git a/embassy-executor-macros/src/macros/main.rs b/embassy-executor-macros/src/macros/main.rs index 3c0d58567..088e64d1c 100644 --- a/embassy-executor-macros/src/macros/main.rs +++ b/embassy-executor-macros/src/macros/main.rs @@ -12,6 +12,20 @@ struct Args { entry: Option, } +pub fn avr() -> TokenStream { + quote! { + #[avr_device::entry] + fn main() -> ! { + let mut executor = ::embassy_executor::Executor::new(); + let executor = unsafe { __make_static(&mut executor) }; + + executor.run(|spawner| { + spawner.must_spawn(__embassy_main(spawner)); + }) + } + } +} + pub fn riscv(args: &[NestedMeta]) -> TokenStream { let maybe_entry = match Args::from_list(args) { Ok(args) => args.entry, diff --git a/embassy-executor/Cargo.toml b/embassy-executor/Cargo.toml index b6b156c9f..d6fe8ee8b 100644 --- a/embassy-executor/Cargo.toml +++ b/embassy-executor/Cargo.toml @@ -40,7 +40,7 @@ critical-section = "1.1" document-features = "0.2.7" -# needed for riscv +# needed for riscv and avr # remove when https://github.com/rust-lang/rust/pull/114499 is merged portable-atomic = { version = "1.5", optional = true } @@ -51,6 +51,9 @@ cortex-m = { version = "0.7.6", optional = true } wasm-bindgen = { version = "0.2.82", optional = true } js-sys = { version = "0.3", optional = true } +# arch-avr dependencies +avr-device = { version = "0.5.3", optional = true } + [dev-dependencies] critical-section = { version = "1.1", features = ["std"] } @@ -78,6 +81,8 @@ arch-cortex-m = ["_arch", "dep:cortex-m"] arch-riscv32 = ["_arch", "dep:portable-atomic"] ## WASM arch-wasm = ["_arch", "dep:wasm-bindgen", "dep:js-sys"] +## AVR +arch-avr = ["_arch", "dep:portable-atomic", "dep:avr-device"] #! ### Executor @@ -88,11 +93,11 @@ executor-interrupt = [] #! ### Task Arena Size #! Sets the [task arena](#task-arena) size. Necessary if you’re not using `nightly`. -#! +#! #!
#! Preconfigured Task Arena Sizes: #! -#! +#! # BEGIN AUTOGENERATED CONFIG FEATURES # Generated by gen_config.py. DO NOT EDIT. diff --git a/embassy-executor/src/arch/avr.rs b/embassy-executor/src/arch/avr.rs new file mode 100644 index 000000000..70085d04d --- /dev/null +++ b/embassy-executor/src/arch/avr.rs @@ -0,0 +1,72 @@ +#[cfg(feature = "executor-interrupt")] +compile_error!("`executor-interrupt` is not supported with `arch-avr`."); + +#[cfg(feature = "executor-thread")] +pub use thread::*; +#[cfg(feature = "executor-thread")] +mod thread { + use core::marker::PhantomData; + + pub use embassy_executor_macros::main_avr as main; + use portable_atomic::{AtomicBool, Ordering}; + + use crate::{raw, Spawner}; + + static SIGNAL_WORK_THREAD_MODE: AtomicBool = AtomicBool::new(false); + + #[export_name = "__pender"] + fn __pender(_context: *mut ()) { + SIGNAL_WORK_THREAD_MODE.store(true, Ordering::SeqCst); + } + + /// avr Executor + 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(core::ptr::null_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 { + avr_device::interrupt::disable(); + if !SIGNAL_WORK_THREAD_MODE.swap(false, Ordering::SeqCst) { + avr_device::interrupt::enable(); + avr_device::asm::sleep(); + } else { + avr_device::interrupt::enable(); + self.inner.poll(); + } + } + } + } + } +} diff --git a/embassy-executor/src/lib.rs b/embassy-executor/src/lib.rs index eea118ade..6a2e493a2 100644 --- a/embassy-executor/src/lib.rs +++ b/embassy-executor/src/lib.rs @@ -23,9 +23,10 @@ macro_rules! check_at_most_one { check_at_most_one!(@amo [$($f)*] [$($f)*] []); }; } -check_at_most_one!("arch-cortex-m", "arch-riscv32", "arch-std", "arch-wasm",); +check_at_most_one!("arch-avr", "arch-cortex-m", "arch-riscv32", "arch-std", "arch-wasm",); #[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-riscv32", path = "arch/riscv32.rs")] #[cfg_attr(feature = "arch-std", path = "arch/std.rs")]