aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDummyc0m <[email protected]>2024-10-06 23:23:33 -0700
committerDummyc0m <[email protected]>2024-10-06 23:33:34 -0700
commit9e6e09a8d747ec90aae215df8471dfe349993487 (patch)
tree0cd116be26fea69f9b770b3f36c87fc1273ae20b
parent8f273497453d3ca3f297465b67820d4d36705d11 (diff)
executor/spin: introduce an architecture agnostic executor
Spin polls the raw executor and never sleeps. It is useful for disabling any power features associated with wfi/wfe-like instructions. When implementing support for the CH32V30x MCU, the wfi instruction had issues interacting with the USB OTG peripheral and appeared to be non-spec-compliant. 1. When sending a USB Data-in packet, the USB peripheral appears to be unable to read the system main memory while in WFI. This manifests in the USB peripheral sending all or partially zeroed DATA packets. Disabling WFI works around this issue. 2. The WFI instruction does not wake up the processor when MIE is disabled. The MCU provides a WFITOWFE bit to emulate the WFE instruction on arm, which, when enabled, ignores the MIE and allows the processor to wake up. This works around the non-compliant WFI implementation. Co-authored-by: Codetector <[email protected]> Co-authored-by: Dummyc0m <[email protected]>
-rw-r--r--embassy-executor-macros/src/lib.rs29
-rw-r--r--embassy-executor-macros/src/macros/main.rs29
-rw-r--r--embassy-executor/Cargo.toml2
-rw-r--r--embassy-executor/src/arch/spin.rs58
-rw-r--r--embassy-executor/src/lib.rs10
5 files changed, 126 insertions, 2 deletions
diff --git a/embassy-executor-macros/src/lib.rs b/embassy-executor-macros/src/lib.rs
index 5461fe04c..61d388b9e 100644
--- a/embassy-executor-macros/src/lib.rs
+++ b/embassy-executor-macros/src/lib.rs
@@ -94,6 +94,35 @@ pub fn main_cortex_m(args: TokenStream, item: TokenStream) -> TokenStream {
94 main::run(&args.meta, f, main::cortex_m()).unwrap_or_else(|x| x).into() 94 main::run(&args.meta, f, main::cortex_m()).unwrap_or_else(|x| x).into()
95} 95}
96 96
97/// Creates a new `executor` instance and declares an architecture agnostic application entry point spawning
98/// the corresponding function body as an async task.
99///
100/// The following restrictions apply:
101///
102/// * The function must accept exactly 1 parameter, an `embassy_executor::Spawner` handle that it can use to spawn additional tasks.
103/// * The function must be declared `async`.
104/// * The function must not use generics.
105/// * Only a single `main` task may be declared.
106///
107/// A user-defined entry macro must provided via the `entry` argument
108///
109/// ## Examples
110/// Spawning a task:
111/// ``` rust
112/// #[embassy_executor::main(entry = "qingke_rt::entry")]
113/// async fn main(_s: embassy_executor::Spawner) {
114/// // Function body
115/// }
116/// ```
117#[proc_macro_attribute]
118pub fn main_spin(args: TokenStream, item: TokenStream) -> TokenStream {
119 let args = syn::parse_macro_input!(args as Args);
120 let f = syn::parse_macro_input!(item as syn::ItemFn);
121 main::run(&args.meta, f, main::spin(&args.meta))
122 .unwrap_or_else(|x| x)
123 .into()
124}
125
97/// Creates a new `executor` instance and declares an application entry point for RISC-V spawning the corresponding function body as an async task. 126/// Creates a new `executor` instance and declares an application entry point for RISC-V spawning the corresponding function body as an async task.
98/// 127///
99/// The following restrictions apply: 128/// The following restrictions apply:
diff --git a/embassy-executor-macros/src/macros/main.rs b/embassy-executor-macros/src/macros/main.rs
index 26dfa2397..66a3965d0 100644
--- a/embassy-executor-macros/src/macros/main.rs
+++ b/embassy-executor-macros/src/macros/main.rs
@@ -1,5 +1,5 @@
1use darling::export::NestedMeta; 1use darling::export::NestedMeta;
2use darling::FromMeta; 2use darling::{Error, FromMeta};
3use proc_macro2::TokenStream; 3use proc_macro2::TokenStream;
4use quote::quote; 4use quote::quote;
5use syn::{Expr, ReturnType, Type}; 5use syn::{Expr, ReturnType, Type};
@@ -50,6 +50,33 @@ pub fn riscv(args: &[NestedMeta]) -> TokenStream {
50 } 50 }
51} 51}
52 52
53pub fn spin(args: &[NestedMeta]) -> TokenStream {
54 let maybe_entry = match Args::from_list(args) {
55 Ok(args) => args.entry,
56 Err(e) => return e.write_errors(),
57 };
58
59 let entry = match maybe_entry {
60 Some(str) => str,
61 None => return Error::missing_field("entry").write_errors(),
62 };
63 let entry = match Expr::from_string(&entry) {
64 Ok(expr) => expr,
65 Err(e) => return e.write_errors(),
66 };
67
68 quote! {
69 #[#entry]
70 fn main() -> ! {
71 let mut executor = ::embassy_executor::Executor::new();
72 let executor = unsafe { __make_static(&mut executor) };
73 executor.run(|spawner| {
74 spawner.must_spawn(__embassy_main(spawner));
75 })
76 }
77 }
78}
79
53pub fn cortex_m() -> TokenStream { 80pub fn cortex_m() -> TokenStream {
54 quote! { 81 quote! {
55 #[cortex_m_rt::entry] 82 #[cortex_m_rt::entry]
diff --git a/embassy-executor/Cargo.toml b/embassy-executor/Cargo.toml
index 01fa28b88..e2fedce3c 100644
--- a/embassy-executor/Cargo.toml
+++ b/embassy-executor/Cargo.toml
@@ -82,6 +82,8 @@ arch-riscv32 = ["_arch"]
82arch-wasm = ["_arch", "dep:wasm-bindgen", "dep:js-sys", "critical-section/std"] 82arch-wasm = ["_arch", "dep:wasm-bindgen", "dep:js-sys", "critical-section/std"]
83## AVR 83## AVR
84arch-avr = ["_arch", "dep:portable-atomic", "dep:avr-device"] 84arch-avr = ["_arch", "dep:portable-atomic", "dep:avr-device"]
85## spin (architecture agnostic; never sleeps)
86arch-spin = ["_arch"]
85 87
86#! ### Executor 88#! ### Executor
87 89
diff --git a/embassy-executor/src/arch/spin.rs b/embassy-executor/src/arch/spin.rs
new file mode 100644
index 000000000..340023620
--- /dev/null
+++ b/embassy-executor/src/arch/spin.rs
@@ -0,0 +1,58 @@
1#[cfg(feature = "executor-interrupt")]
2compile_error!("`executor-interrupt` is not supported with `arch-spin`.");
3
4#[cfg(feature = "executor-thread")]
5pub use thread::*;
6#[cfg(feature = "executor-thread")]
7mod thread {
8 use core::marker::PhantomData;
9
10 pub use embassy_executor_macros::main_spin as main;
11
12 use crate::{raw, Spawner};
13
14 #[export_name = "__pender"]
15 fn __pender(_context: *mut ()) {}
16
17 /// Spin Executor
18 pub struct Executor {
19 inner: raw::Executor,
20 not_send: PhantomData<*mut ()>,
21 }
22
23 impl Executor {
24 /// Create a new Executor.
25 pub fn new() -> Self {
26 Self {
27 inner: raw::Executor::new(core::ptr::null_mut()),
28 not_send: PhantomData,
29 }
30 }
31
32 /// Run the executor.
33 ///
34 /// The `init` closure is called with a [`Spawner`] that spawns tasks on
35 /// this executor. Use it to spawn the initial task(s). After `init` returns,
36 /// the executor starts running the tasks.
37 ///
38 /// To spawn more tasks later, you may keep copies of the [`Spawner`] (it is `Copy`),
39 /// for example by passing it as an argument to the initial tasks.
40 ///
41 /// This function requires `&'static mut self`. This means you have to store the
42 /// Executor instance in a place where it'll live forever and grants you mutable
43 /// access. There's a few ways to do this:
44 ///
45 /// - a [StaticCell](https://docs.rs/static_cell/latest/static_cell/) (safe)
46 /// - a `static mut` (unsafe)
47 /// - a local variable in a function you know never returns (like `fn main() -> !`), upgrading its lifetime with `transmute`. (unsafe)
48 ///
49 /// This function never returns.
50 pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! {
51 init(self.inner.spawner());
52
53 loop {
54 unsafe { self.inner.poll() };
55 }
56 }
57 }
58}
diff --git a/embassy-executor/src/lib.rs b/embassy-executor/src/lib.rs
index 6a2e493a2..d816539ac 100644
--- a/embassy-executor/src/lib.rs
+++ b/embassy-executor/src/lib.rs
@@ -23,7 +23,14 @@ macro_rules! check_at_most_one {
23 check_at_most_one!(@amo [$($f)*] [$($f)*] []); 23 check_at_most_one!(@amo [$($f)*] [$($f)*] []);
24 }; 24 };
25} 25}
26check_at_most_one!("arch-avr", "arch-cortex-m", "arch-riscv32", "arch-std", "arch-wasm",); 26check_at_most_one!(
27 "arch-avr",
28 "arch-cortex-m",
29 "arch-riscv32",
30 "arch-std",
31 "arch-wasm",
32 "arch-spin",
33);
27 34
28#[cfg(feature = "_arch")] 35#[cfg(feature = "_arch")]
29#[cfg_attr(feature = "arch-avr", path = "arch/avr.rs")] 36#[cfg_attr(feature = "arch-avr", path = "arch/avr.rs")]
@@ -31,6 +38,7 @@ check_at_most_one!("arch-avr", "arch-cortex-m", "arch-riscv32", "arch-std", "arc
31#[cfg_attr(feature = "arch-riscv32", path = "arch/riscv32.rs")] 38#[cfg_attr(feature = "arch-riscv32", path = "arch/riscv32.rs")]
32#[cfg_attr(feature = "arch-std", path = "arch/std.rs")] 39#[cfg_attr(feature = "arch-std", path = "arch/std.rs")]
33#[cfg_attr(feature = "arch-wasm", path = "arch/wasm.rs")] 40#[cfg_attr(feature = "arch-wasm", path = "arch/wasm.rs")]
41#[cfg_attr(feature = "arch-spin", path = "arch/spin.rs")]
34mod arch; 42mod arch;
35 43
36#[cfg(feature = "_arch")] 44#[cfg(feature = "_arch")]