diff options
| -rw-r--r-- | Cargo.toml | 2 | ||||
| -rw-r--r-- | embassy-macros/Cargo.toml | 13 | ||||
| -rw-r--r-- | embassy-macros/src/lib.rs | 114 | ||||
| -rw-r--r-- | embassy/Cargo.toml | 2 | ||||
| -rw-r--r-- | embassy/src/executor.rs | 48 | ||||
| -rw-r--r-- | embassy/src/executor/executor.rs | 305 | ||||
| -rw-r--r-- | embassy/src/executor/mod.rs | 9 | ||||
| -rw-r--r-- | embassy/src/executor/timer_executor.rs | 77 | ||||
| -rw-r--r-- | embassy/src/time.rs | 331 | ||||
| -rw-r--r-- | embassy/src/time/duration.rs | 118 | ||||
| -rw-r--r-- | embassy/src/time/instant.rs | 102 | ||||
| -rw-r--r-- | embassy/src/time/mod.rs | 24 | ||||
| -rw-r--r-- | embassy/src/time/timer.rs | 30 | ||||
| -rw-r--r-- | embassy/src/time/traits.rs | 44 | ||||
| -rw-r--r-- | examples/Cargo.toml | 4 | ||||
| -rw-r--r-- | examples/src/bin/gpiote.rs | 2 | ||||
| -rw-r--r-- | examples/src/bin/qspi.rs | 2 | ||||
| -rw-r--r-- | examples/src/bin/uart.rs | 2 |
18 files changed, 842 insertions, 387 deletions
diff --git a/Cargo.toml b/Cargo.toml index abce83ec6..17fbf0a8d 100644 --- a/Cargo.toml +++ b/Cargo.toml | |||
| @@ -14,8 +14,6 @@ exclude = [ | |||
| 14 | panic-probe = { git = "https://github.com/knurling-rs/probe-run", branch="main" } | 14 | panic-probe = { git = "https://github.com/knurling-rs/probe-run", branch="main" } |
| 15 | defmt-rtt = { git = "https://github.com/knurling-rs/defmt", branch="cursed-symbol-names-linkers-must-repent-for-their-sins" } | 15 | defmt-rtt = { git = "https://github.com/knurling-rs/defmt", branch="cursed-symbol-names-linkers-must-repent-for-their-sins" } |
| 16 | defmt = { git = "https://github.com/knurling-rs/defmt", branch="cursed-symbol-names-linkers-must-repent-for-their-sins" } | 16 | defmt = { git = "https://github.com/knurling-rs/defmt", branch="cursed-symbol-names-linkers-must-repent-for-their-sins" } |
| 17 | static-executor = { git = "https://github.com/Dirbaio/static-executor", branch="multi"} | ||
| 18 | futures-intrusive = { git = "https://github.com/Dirbaio/futures-intrusive", branch="master"} | ||
| 19 | 17 | ||
| 20 | [profile.dev] | 18 | [profile.dev] |
| 21 | codegen-units = 1 | 19 | codegen-units = 1 |
diff --git a/embassy-macros/Cargo.toml b/embassy-macros/Cargo.toml new file mode 100644 index 000000000..1f3e20aa8 --- /dev/null +++ b/embassy-macros/Cargo.toml | |||
| @@ -0,0 +1,13 @@ | |||
| 1 | [package] | ||
| 2 | name = "embassy-macros" | ||
| 3 | version = "0.1.0" | ||
| 4 | authors = ["Dario Nieuwenhuis <[email protected]>"] | ||
| 5 | edition = "2018" | ||
| 6 | |||
| 7 | [dependencies] | ||
| 8 | syn = { version = "1.0.39", features = ["full", "extra-traits"] } | ||
| 9 | quote = "1.0.7" | ||
| 10 | darling = "0.10.2" | ||
| 11 | |||
| 12 | [lib] | ||
| 13 | proc-macro = true | ||
diff --git a/embassy-macros/src/lib.rs b/embassy-macros/src/lib.rs new file mode 100644 index 000000000..1745311ba --- /dev/null +++ b/embassy-macros/src/lib.rs | |||
| @@ -0,0 +1,114 @@ | |||
| 1 | #![feature(proc_macro_diagnostic)] | ||
| 2 | |||
| 3 | extern crate proc_macro; | ||
| 4 | |||
| 5 | use darling::FromMeta; | ||
| 6 | use proc_macro::{Diagnostic, Level, Span, TokenStream}; | ||
| 7 | use quote::{format_ident, quote}; | ||
| 8 | use syn::spanned::Spanned; | ||
| 9 | |||
| 10 | #[derive(Debug, FromMeta)] | ||
| 11 | struct MacroArgs { | ||
| 12 | #[darling(default)] | ||
| 13 | pool_size: Option<usize>, | ||
| 14 | } | ||
| 15 | |||
| 16 | #[proc_macro_attribute] | ||
| 17 | pub fn task(args: TokenStream, item: TokenStream) -> TokenStream { | ||
| 18 | let args = syn::parse_macro_input!(args as syn::AttributeArgs); | ||
| 19 | let mut task_fn = syn::parse_macro_input!(item as syn::ItemFn); | ||
| 20 | |||
| 21 | let args = match MacroArgs::from_list(&args) { | ||
| 22 | Ok(v) => v, | ||
| 23 | Err(e) => { | ||
| 24 | return TokenStream::from(e.write_errors()); | ||
| 25 | } | ||
| 26 | }; | ||
| 27 | |||
| 28 | let pool_size: usize = args.pool_size.unwrap_or(1); | ||
| 29 | |||
| 30 | let mut fail = false; | ||
| 31 | if task_fn.sig.asyncness.is_none() { | ||
| 32 | task_fn | ||
| 33 | .sig | ||
| 34 | .span() | ||
| 35 | .unwrap() | ||
| 36 | .error("task functions must be async") | ||
| 37 | .emit(); | ||
| 38 | fail = true; | ||
| 39 | } | ||
| 40 | if task_fn.sig.generics.params.len() != 0 { | ||
| 41 | task_fn | ||
| 42 | .sig | ||
| 43 | .span() | ||
| 44 | .unwrap() | ||
| 45 | .error("task functions must not be generic") | ||
| 46 | .emit(); | ||
| 47 | fail = true; | ||
| 48 | } | ||
| 49 | if pool_size < 1 { | ||
| 50 | Span::call_site() | ||
| 51 | .error("pool_size must be 1 or greater") | ||
| 52 | .emit(); | ||
| 53 | fail = true | ||
| 54 | } | ||
| 55 | |||
| 56 | let mut arg_names: syn::punctuated::Punctuated<syn::Ident, syn::Token![,]> = | ||
| 57 | syn::punctuated::Punctuated::new(); | ||
| 58 | let args = &task_fn.sig.inputs; | ||
| 59 | |||
| 60 | for arg in args.iter() { | ||
| 61 | match arg { | ||
| 62 | syn::FnArg::Receiver(_) => { | ||
| 63 | arg.span() | ||
| 64 | .unwrap() | ||
| 65 | .error("task functions must not have receiver arguments") | ||
| 66 | .emit(); | ||
| 67 | fail = true; | ||
| 68 | } | ||
| 69 | syn::FnArg::Typed(t) => match t.pat.as_ref() { | ||
| 70 | syn::Pat::Ident(i) => arg_names.push(i.ident.clone()), | ||
| 71 | _ => { | ||
| 72 | arg.span() | ||
| 73 | .unwrap() | ||
| 74 | .error("pattern matching in task arguments is not yet supporteds") | ||
| 75 | .emit(); | ||
| 76 | fail = true; | ||
| 77 | } | ||
| 78 | }, | ||
| 79 | } | ||
| 80 | } | ||
| 81 | |||
| 82 | if fail { | ||
| 83 | return TokenStream::new(); | ||
| 84 | } | ||
| 85 | |||
| 86 | let name = task_fn.sig.ident.clone(); | ||
| 87 | |||
| 88 | let type_name = format_ident!("__embassy_executor_type_{}", name); | ||
| 89 | let pool_name = format_ident!("__embassy_executor_pool_{}", name); | ||
| 90 | let task_fn_name = format_ident!("__embassy_executor_task_{}", name); | ||
| 91 | let create_fn_name = format_ident!("__embassy_executor_create_{}", name); | ||
| 92 | |||
| 93 | let visibility = &task_fn.vis; | ||
| 94 | |||
| 95 | task_fn.sig.ident = task_fn_name.clone(); | ||
| 96 | |||
| 97 | let result = quote! { | ||
| 98 | #task_fn | ||
| 99 | #[allow(non_camel_case_types)] | ||
| 100 | type #type_name = impl ::core::future::Future + 'static; | ||
| 101 | |||
| 102 | fn #create_fn_name(#args) -> #type_name { | ||
| 103 | #task_fn_name(#arg_names) | ||
| 104 | } | ||
| 105 | |||
| 106 | #[allow(non_upper_case_globals)] | ||
| 107 | static #pool_name: [::embassy::executor::Task<#type_name>; #pool_size] = [::embassy::executor::Task::new(); #pool_size]; | ||
| 108 | |||
| 109 | #visibility fn #name(#args) -> ::embassy::executor::SpawnToken { | ||
| 110 | unsafe { ::embassy::executor::Task::spawn(&#pool_name, || #create_fn_name(#arg_names)) } | ||
| 111 | } | ||
| 112 | }; | ||
| 113 | result.into() | ||
| 114 | } | ||
diff --git a/embassy/Cargo.toml b/embassy/Cargo.toml index 7b8e6cfad..4abcda99e 100644 --- a/embassy/Cargo.toml +++ b/embassy/Cargo.toml | |||
| @@ -13,4 +13,4 @@ cortex-m = "0.6.3" | |||
| 13 | futures = { version = "0.3.5", default-features = false, features = [ "async-await" ] } | 13 | futures = { version = "0.3.5", default-features = false, features = [ "async-await" ] } |
| 14 | pin-project = { version = "0.4.23", default-features = false } | 14 | pin-project = { version = "0.4.23", default-features = false } |
| 15 | futures-intrusive = { version = "0.3.1", default-features = false } | 15 | futures-intrusive = { version = "0.3.1", default-features = false } |
| 16 | static-executor = { version = "0.1.0", features=["defmt"]} | 16 | embassy-macros = { version = "0.1.0", path = "../embassy-macros"} |
diff --git a/embassy/src/executor.rs b/embassy/src/executor.rs deleted file mode 100644 index d42a19b9a..000000000 --- a/embassy/src/executor.rs +++ /dev/null | |||
| @@ -1,48 +0,0 @@ | |||
| 1 | use core::marker::PhantomData; | ||
| 2 | use static_executor as se; | ||
| 3 | |||
| 4 | use crate::time; | ||
| 5 | use crate::time::Alarm; | ||
| 6 | |||
| 7 | pub use se::{task, SpawnError, SpawnToken}; | ||
| 8 | |||
| 9 | pub struct Executor<A: Alarm> { | ||
| 10 | inner: se::Executor, | ||
| 11 | alarm: A, | ||
| 12 | timer: time::TimerService, | ||
| 13 | } | ||
| 14 | |||
| 15 | impl<A: Alarm> Executor<A> { | ||
| 16 | pub fn new(alarm: A, signal_fn: fn()) -> Self { | ||
| 17 | alarm.set_callback(signal_fn); | ||
| 18 | Self { | ||
| 19 | inner: se::Executor::new(signal_fn), | ||
| 20 | alarm, | ||
| 21 | timer: time::TimerService::new(time::IntrusiveClock), | ||
| 22 | } | ||
| 23 | } | ||
| 24 | |||
| 25 | /// Spawn a future on this executor. | ||
| 26 | /// | ||
| 27 | /// safety: can only be called from the executor thread | ||
| 28 | pub unsafe fn spawn(&'static self, token: SpawnToken) -> Result<(), SpawnError> { | ||
| 29 | self.inner.spawn(token) | ||
| 30 | } | ||
| 31 | |||
| 32 | /// Runs the executor until the queue is empty. | ||
| 33 | /// | ||
| 34 | /// safety: can only be called from the executor thread | ||
| 35 | pub unsafe fn run(&'static self) { | ||
| 36 | time::with_timer_service(&self.timer, || { | ||
| 37 | self.timer.check_expirations(); | ||
| 38 | self.inner.run(); | ||
| 39 | |||
| 40 | match self.timer.next_expiration() { | ||
| 41 | // If this is in the past, set_alarm will immediately trigger the alarm, | ||
| 42 | // which will make the wfe immediately return so we do another loop iteration. | ||
| 43 | Some(at) => self.alarm.set(at), | ||
| 44 | None => self.alarm.clear(), | ||
| 45 | } | ||
| 46 | }) | ||
| 47 | } | ||
| 48 | } | ||
diff --git a/embassy/src/executor/executor.rs b/embassy/src/executor/executor.rs new file mode 100644 index 000000000..7ef9230e4 --- /dev/null +++ b/embassy/src/executor/executor.rs | |||
| @@ -0,0 +1,305 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![feature(const_fn)] | ||
| 3 | |||
| 4 | use core::cell::Cell; | ||
| 5 | use core::cell::UnsafeCell; | ||
| 6 | use core::future::Future; | ||
| 7 | use core::mem; | ||
| 8 | use core::mem::MaybeUninit; | ||
| 9 | use core::pin::Pin; | ||
| 10 | use core::ptr; | ||
| 11 | use core::ptr::NonNull; | ||
| 12 | use core::sync::atomic::{AtomicPtr, AtomicU32, Ordering}; | ||
| 13 | use core::task::{Context, Poll, RawWaker, RawWakerVTable, Waker}; | ||
| 14 | |||
| 15 | //============= | ||
| 16 | // UninitCell | ||
| 17 | |||
| 18 | struct UninitCell<T>(MaybeUninit<UnsafeCell<T>>); | ||
| 19 | impl<T> UninitCell<T> { | ||
| 20 | const fn uninit() -> Self { | ||
| 21 | Self(MaybeUninit::uninit()) | ||
| 22 | } | ||
| 23 | |||
| 24 | unsafe fn as_mut_ptr(&self) -> *mut T { | ||
| 25 | (*self.0.as_ptr()).get() | ||
| 26 | } | ||
| 27 | |||
| 28 | unsafe fn as_mut(&self) -> &mut T { | ||
| 29 | &mut *self.as_mut_ptr() | ||
| 30 | } | ||
| 31 | |||
| 32 | unsafe fn write(&self, val: T) { | ||
| 33 | ptr::write(self.as_mut_ptr(), val) | ||
| 34 | } | ||
| 35 | |||
| 36 | unsafe fn drop_in_place(&self) { | ||
| 37 | ptr::drop_in_place(self.as_mut_ptr()) | ||
| 38 | } | ||
| 39 | } | ||
| 40 | |||
| 41 | impl<T: Copy> UninitCell<T> { | ||
| 42 | unsafe fn read(&self) -> T { | ||
| 43 | ptr::read(self.as_mut_ptr()) | ||
| 44 | } | ||
| 45 | } | ||
| 46 | |||
| 47 | //============= | ||
| 48 | // Data structures | ||
| 49 | |||
| 50 | const STATE_RUNNING: u32 = 1 << 0; | ||
| 51 | const STATE_QUEUED: u32 = 1 << 1; | ||
| 52 | |||
| 53 | struct Header { | ||
| 54 | state: AtomicU32, | ||
| 55 | next: AtomicPtr<Header>, | ||
| 56 | executor: Cell<*const Executor>, | ||
| 57 | poll_fn: UninitCell<unsafe fn(*mut Header)>, // Valid if STATE_RUNNING | ||
| 58 | } | ||
| 59 | |||
| 60 | // repr(C) is needed to guarantee that header is located at offset 0 | ||
| 61 | // This makes it safe to cast between Header and Task pointers. | ||
| 62 | #[repr(C)] | ||
| 63 | pub struct Task<F: Future + 'static> { | ||
| 64 | header: Header, | ||
| 65 | future: UninitCell<F>, // Valid if STATE_RUNNING | ||
| 66 | } | ||
| 67 | |||
| 68 | #[derive(Copy, Clone, Debug)] | ||
| 69 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 70 | pub enum SpawnError { | ||
| 71 | Busy, | ||
| 72 | } | ||
| 73 | |||
| 74 | //============= | ||
| 75 | // Atomic task queue using a very, very simple lock-free linked-list queue: | ||
| 76 | // | ||
| 77 | // To enqueue a task, task.next is set to the old head, and head is atomically set to task. | ||
| 78 | // | ||
| 79 | // Dequeuing is done in batches: the queue is emptied by atomically replacing head with | ||
| 80 | // null. Then the batch is iterated following the next pointers until null is reached. | ||
| 81 | // | ||
| 82 | // Note that batches will be iterated in the opposite order as they were enqueued. This should | ||
| 83 | // be OK for our use case. Hopefully it doesn't create executor fairness problems. | ||
| 84 | |||
| 85 | struct Queue { | ||
| 86 | head: AtomicPtr<Header>, | ||
| 87 | } | ||
| 88 | |||
| 89 | impl Queue { | ||
| 90 | const fn new() -> Self { | ||
| 91 | Self { | ||
| 92 | head: AtomicPtr::new(ptr::null_mut()), | ||
| 93 | } | ||
| 94 | } | ||
| 95 | |||
| 96 | /// Enqueues an item. Returns true if the queue was empty. | ||
| 97 | unsafe fn enqueue(&self, item: *mut Header) -> bool { | ||
| 98 | let mut prev = self.head.load(Ordering::Acquire); | ||
| 99 | loop { | ||
| 100 | (*item).next.store(prev, Ordering::Relaxed); | ||
| 101 | match self | ||
| 102 | .head | ||
| 103 | .compare_exchange_weak(prev, item, Ordering::AcqRel, Ordering::Acquire) | ||
| 104 | { | ||
| 105 | Ok(_) => break, | ||
| 106 | Err(next_prev) => prev = next_prev, | ||
| 107 | } | ||
| 108 | } | ||
| 109 | |||
| 110 | prev.is_null() | ||
| 111 | } | ||
| 112 | |||
| 113 | unsafe fn dequeue_all(&self, on_task: impl Fn(*mut Header)) { | ||
| 114 | loop { | ||
| 115 | let mut task = self.head.swap(ptr::null_mut(), Ordering::AcqRel); | ||
| 116 | |||
| 117 | if task.is_null() { | ||
| 118 | // Queue is empty, we're done | ||
| 119 | return; | ||
| 120 | } | ||
| 121 | |||
| 122 | while !task.is_null() { | ||
| 123 | on_task(task); | ||
| 124 | task = (*task).next.load(Ordering::Relaxed); | ||
| 125 | } | ||
| 126 | } | ||
| 127 | } | ||
| 128 | } | ||
| 129 | |||
| 130 | //============= | ||
| 131 | // Waker | ||
| 132 | |||
| 133 | static WAKER_VTABLE: RawWakerVTable = | ||
| 134 | RawWakerVTable::new(waker_clone, waker_wake, waker_wake, waker_drop); | ||
| 135 | |||
| 136 | unsafe fn waker_clone(p: *const ()) -> RawWaker { | ||
| 137 | RawWaker::new(p, &WAKER_VTABLE) | ||
| 138 | } | ||
| 139 | |||
| 140 | unsafe fn waker_wake(p: *const ()) { | ||
| 141 | let header = &*(p as *const Header); | ||
| 142 | |||
| 143 | let mut current = header.state.load(Ordering::Acquire); | ||
| 144 | loop { | ||
| 145 | // If already scheduled, or if not started, | ||
| 146 | if (current & STATE_QUEUED != 0) || (current & STATE_RUNNING == 0) { | ||
| 147 | return; | ||
| 148 | } | ||
| 149 | |||
| 150 | // Mark it as scheduled | ||
| 151 | let new = current | STATE_QUEUED; | ||
| 152 | |||
| 153 | match header | ||
| 154 | .state | ||
| 155 | .compare_exchange_weak(current, new, Ordering::AcqRel, Ordering::Acquire) | ||
| 156 | { | ||
| 157 | Ok(_) => break, | ||
| 158 | Err(next_current) => current = next_current, | ||
| 159 | } | ||
| 160 | } | ||
| 161 | |||
| 162 | // We have just marked the task as scheduled, so enqueue it. | ||
| 163 | let executor = &*header.executor.get(); | ||
| 164 | executor.enqueue(p as *mut Header); | ||
| 165 | } | ||
| 166 | |||
| 167 | unsafe fn waker_drop(_: *const ()) { | ||
| 168 | // nop | ||
| 169 | } | ||
| 170 | |||
| 171 | //============= | ||
| 172 | // Task | ||
| 173 | |||
| 174 | impl<F: Future + 'static> Task<F> { | ||
| 175 | pub const fn new() -> Self { | ||
| 176 | Self { | ||
| 177 | header: Header { | ||
| 178 | state: AtomicU32::new(0), | ||
| 179 | next: AtomicPtr::new(ptr::null_mut()), | ||
| 180 | executor: Cell::new(ptr::null()), | ||
| 181 | poll_fn: UninitCell::uninit(), | ||
| 182 | }, | ||
| 183 | future: UninitCell::uninit(), | ||
| 184 | } | ||
| 185 | } | ||
| 186 | |||
| 187 | pub unsafe fn spawn(pool: &'static [Self], future: impl FnOnce() -> F) -> SpawnToken { | ||
| 188 | for task in pool { | ||
| 189 | let state = STATE_RUNNING | STATE_QUEUED; | ||
| 190 | if task | ||
| 191 | .header | ||
| 192 | .state | ||
| 193 | .compare_and_swap(0, state, Ordering::AcqRel) | ||
| 194 | == 0 | ||
| 195 | { | ||
| 196 | // Initialize the task | ||
| 197 | task.header.poll_fn.write(Self::poll); | ||
| 198 | task.future.write(future()); | ||
| 199 | |||
| 200 | return SpawnToken { | ||
| 201 | header: Some(NonNull::new_unchecked(&task.header as *const Header as _)), | ||
| 202 | }; | ||
| 203 | } | ||
| 204 | } | ||
| 205 | |||
| 206 | return SpawnToken { header: None }; | ||
| 207 | } | ||
| 208 | |||
| 209 | unsafe fn poll(p: *mut Header) { | ||
| 210 | let this = &*(p as *const Task<F>); | ||
| 211 | |||
| 212 | let future = Pin::new_unchecked(this.future.as_mut()); | ||
| 213 | let waker = Waker::from_raw(RawWaker::new(p as _, &WAKER_VTABLE)); | ||
| 214 | let mut cx = Context::from_waker(&waker); | ||
| 215 | match future.poll(&mut cx) { | ||
| 216 | Poll::Ready(_) => { | ||
| 217 | this.future.drop_in_place(); | ||
| 218 | this.header | ||
| 219 | .state | ||
| 220 | .fetch_and(!STATE_RUNNING, Ordering::AcqRel); | ||
| 221 | } | ||
| 222 | Poll::Pending => {} | ||
| 223 | } | ||
| 224 | } | ||
| 225 | } | ||
| 226 | |||
| 227 | unsafe impl<F: Future + 'static> Sync for Task<F> {} | ||
| 228 | |||
| 229 | //============= | ||
| 230 | // Spawn token | ||
| 231 | |||
| 232 | #[must_use = "Calling a task function does nothing on its own. To spawn a task, pass the result to Executor::spawn()"] | ||
| 233 | pub struct SpawnToken { | ||
| 234 | header: Option<NonNull<Header>>, | ||
| 235 | } | ||
| 236 | |||
| 237 | impl Drop for SpawnToken { | ||
| 238 | fn drop(&mut self) { | ||
| 239 | // TODO maybe we can deallocate the task instead. | ||
| 240 | panic!("Please do not drop SpawnToken instances") | ||
| 241 | } | ||
| 242 | } | ||
| 243 | |||
| 244 | //============= | ||
| 245 | // Executor | ||
| 246 | |||
| 247 | pub struct Executor { | ||
| 248 | queue: Queue, | ||
| 249 | signal_fn: fn(), | ||
| 250 | } | ||
| 251 | |||
| 252 | impl Executor { | ||
| 253 | pub const fn new(signal_fn: fn()) -> Self { | ||
| 254 | Self { | ||
| 255 | queue: Queue::new(), | ||
| 256 | signal_fn: signal_fn, | ||
| 257 | } | ||
| 258 | } | ||
| 259 | |||
| 260 | unsafe fn enqueue(&self, item: *mut Header) { | ||
| 261 | if self.queue.enqueue(item) { | ||
| 262 | (self.signal_fn)() | ||
| 263 | } | ||
| 264 | } | ||
| 265 | |||
| 266 | /// Spawn a future on this executor. | ||
| 267 | /// | ||
| 268 | /// safety: can only be called from the executor thread | ||
| 269 | pub unsafe fn spawn(&'static self, token: SpawnToken) -> Result<(), SpawnError> { | ||
| 270 | let header = token.header; | ||
| 271 | mem::forget(token); | ||
| 272 | |||
| 273 | match header { | ||
| 274 | Some(header) => { | ||
| 275 | let header = header.as_ref(); | ||
| 276 | header.executor.set(self); | ||
| 277 | self.enqueue(header as *const _ as _); | ||
| 278 | Ok(()) | ||
| 279 | } | ||
| 280 | None => Err(SpawnError::Busy), | ||
| 281 | } | ||
| 282 | } | ||
| 283 | |||
| 284 | /// Runs the executor until the queue is empty. | ||
| 285 | /// | ||
| 286 | /// safety: can only be called from the executor thread | ||
| 287 | pub unsafe fn run(&self) { | ||
| 288 | self.queue.dequeue_all(|p| { | ||
| 289 | let header = &*p; | ||
| 290 | |||
| 291 | let state = header.state.fetch_and(!STATE_QUEUED, Ordering::AcqRel); | ||
| 292 | if state & STATE_RUNNING == 0 { | ||
| 293 | // If task is not running, ignore it. This can happen in the following scenario: | ||
| 294 | // - Task gets dequeued, poll starts | ||
| 295 | // - While task is being polled, it gets woken. It gets placed in the queue. | ||
| 296 | // - Task poll finishes, returning done=true | ||
| 297 | // - RUNNING bit is cleared, but the task is already in the queue. | ||
| 298 | return; | ||
| 299 | } | ||
| 300 | |||
| 301 | // Run the task | ||
| 302 | header.poll_fn.read()(p as _); | ||
| 303 | }); | ||
| 304 | } | ||
| 305 | } | ||
diff --git a/embassy/src/executor/mod.rs b/embassy/src/executor/mod.rs new file mode 100644 index 000000000..1a68bdfde --- /dev/null +++ b/embassy/src/executor/mod.rs | |||
| @@ -0,0 +1,9 @@ | |||
| 1 | mod executor; | ||
| 2 | mod timer_executor; | ||
| 3 | |||
| 4 | // for time::Timer | ||
| 5 | pub(crate) use timer_executor::current_timer_queue; | ||
| 6 | |||
| 7 | pub use embassy_macros::task; | ||
| 8 | pub use executor::{Executor, SpawnError, SpawnToken, Task}; | ||
| 9 | pub use timer_executor::TimerExecutor; | ||
diff --git a/embassy/src/executor/timer_executor.rs b/embassy/src/executor/timer_executor.rs new file mode 100644 index 000000000..21a81383a --- /dev/null +++ b/embassy/src/executor/timer_executor.rs | |||
| @@ -0,0 +1,77 @@ | |||
| 1 | use super::executor::{Executor, SpawnError, SpawnToken}; | ||
| 2 | use core::ptr; | ||
| 3 | use core::sync::atomic::{AtomicPtr, Ordering}; | ||
| 4 | use futures_intrusive::timer as fi; | ||
| 5 | |||
| 6 | use crate::time::Alarm; | ||
| 7 | |||
| 8 | pub(crate) struct IntrusiveClock; | ||
| 9 | |||
| 10 | impl fi::Clock for IntrusiveClock { | ||
| 11 | fn now(&self) -> u64 { | ||
| 12 | crate::time::now() | ||
| 13 | } | ||
| 14 | } | ||
| 15 | |||
| 16 | pub(crate) type TimerQueue = fi::LocalTimerService; | ||
| 17 | |||
| 18 | pub struct TimerExecutor<A: Alarm> { | ||
| 19 | inner: Executor, | ||
| 20 | alarm: A, | ||
| 21 | timer_queue: TimerQueue, | ||
| 22 | } | ||
| 23 | |||
| 24 | impl<A: Alarm> TimerExecutor<A> { | ||
| 25 | pub fn new(alarm: A, signal_fn: fn()) -> Self { | ||
| 26 | alarm.set_callback(signal_fn); | ||
| 27 | Self { | ||
| 28 | inner: Executor::new(signal_fn), | ||
| 29 | alarm, | ||
| 30 | timer_queue: TimerQueue::new(&IntrusiveClock), | ||
| 31 | } | ||
| 32 | } | ||
| 33 | |||
| 34 | /// Spawn a future on this executor. | ||
| 35 | /// | ||
| 36 | /// safety: can only be called from the executor thread | ||
| 37 | pub unsafe fn spawn(&'static self, token: SpawnToken) -> Result<(), SpawnError> { | ||
| 38 | self.inner.spawn(token) | ||
| 39 | } | ||
| 40 | |||
| 41 | /// Runs the executor until the queue is empty. | ||
| 42 | /// | ||
| 43 | /// safety: can only be called from the executor thread | ||
| 44 | pub unsafe fn run(&'static self) { | ||
| 45 | with_timer_queue(&self.timer_queue, || { | ||
| 46 | self.timer_queue.check_expirations(); | ||
| 47 | self.inner.run(); | ||
| 48 | |||
| 49 | match self.timer_queue.next_expiration() { | ||
| 50 | // If this is in the past, set_alarm will immediately trigger the alarm, | ||
| 51 | // which will make the wfe immediately return so we do another loop iteration. | ||
| 52 | Some(at) => self.alarm.set(at), | ||
| 53 | None => self.alarm.clear(), | ||
| 54 | } | ||
| 55 | }) | ||
| 56 | } | ||
| 57 | } | ||
| 58 | |||
| 59 | static CURRENT_TIMER_QUEUE: AtomicPtr<TimerQueue> = AtomicPtr::new(ptr::null_mut()); | ||
| 60 | |||
| 61 | fn with_timer_queue<R>(svc: &'static TimerQueue, f: impl FnOnce() -> R) -> R { | ||
| 62 | let svc = svc as *const _ as *mut _; | ||
| 63 | let prev_svc = CURRENT_TIMER_QUEUE.swap(svc, Ordering::Relaxed); | ||
| 64 | let r = f(); | ||
| 65 | let svc2 = CURRENT_TIMER_QUEUE.swap(prev_svc, Ordering::Relaxed); | ||
| 66 | assert_eq!(svc, svc2); | ||
| 67 | r | ||
| 68 | } | ||
| 69 | |||
| 70 | pub(crate) fn current_timer_queue() -> &'static TimerQueue { | ||
| 71 | unsafe { | ||
| 72 | CURRENT_TIMER_QUEUE | ||
| 73 | .load(Ordering::Relaxed) | ||
| 74 | .as_ref() | ||
| 75 | .unwrap() | ||
| 76 | } | ||
| 77 | } | ||
diff --git a/embassy/src/time.rs b/embassy/src/time.rs deleted file mode 100644 index 532a53a38..000000000 --- a/embassy/src/time.rs +++ /dev/null | |||
| @@ -1,331 +0,0 @@ | |||
| 1 | use core::cell::Cell; | ||
| 2 | use core::convert::TryInto; | ||
| 3 | use core::future::Future; | ||
| 4 | use core::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Sub, SubAssign}; | ||
| 5 | use core::pin::Pin; | ||
| 6 | use core::ptr; | ||
| 7 | use core::sync::atomic::{AtomicPtr, Ordering}; | ||
| 8 | use core::task::{Context, Poll}; | ||
| 9 | |||
| 10 | use crate::util::*; | ||
| 11 | use fi::LocalTimer; | ||
| 12 | use futures_intrusive::timer as fi; | ||
| 13 | static mut CLOCK: Option<&'static dyn Clock> = None; | ||
| 14 | |||
| 15 | pub unsafe fn set_clock(clock: &'static dyn Clock) { | ||
| 16 | CLOCK = Some(clock); | ||
| 17 | } | ||
| 18 | |||
| 19 | fn now() -> u64 { | ||
| 20 | unsafe { CLOCK.dexpect(defmt::intern!("No clock set")).now() } | ||
| 21 | } | ||
| 22 | |||
| 23 | #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] | ||
| 24 | pub struct Instant { | ||
| 25 | ticks: u64, | ||
| 26 | } | ||
| 27 | |||
| 28 | // TODO allow customizing, probably via Cargo features `tick-hz-32768` or something. | ||
| 29 | pub const TICKS_PER_SECOND: u32 = 32768; | ||
| 30 | |||
| 31 | impl Instant { | ||
| 32 | pub fn now() -> Instant { | ||
| 33 | Instant { ticks: now() } | ||
| 34 | } | ||
| 35 | |||
| 36 | pub fn into_ticks(&self) -> u64 { | ||
| 37 | self.ticks | ||
| 38 | } | ||
| 39 | |||
| 40 | pub fn duration_since(&self, earlier: Instant) -> Duration { | ||
| 41 | Duration { | ||
| 42 | ticks: (self.ticks - earlier.ticks).try_into().unwrap(), | ||
| 43 | } | ||
| 44 | } | ||
| 45 | |||
| 46 | pub fn checked_duration_since(&self, earlier: Instant) -> Option<Duration> { | ||
| 47 | if self.ticks < earlier.ticks { | ||
| 48 | None | ||
| 49 | } else { | ||
| 50 | Some(Duration { | ||
| 51 | ticks: (self.ticks - earlier.ticks).try_into().unwrap(), | ||
| 52 | }) | ||
| 53 | } | ||
| 54 | } | ||
| 55 | |||
| 56 | pub fn saturating_duration_since(&self, earlier: Instant) -> Duration { | ||
| 57 | Duration { | ||
| 58 | ticks: if self.ticks < earlier.ticks { | ||
| 59 | 0 | ||
| 60 | } else { | ||
| 61 | (self.ticks - earlier.ticks).try_into().unwrap() | ||
| 62 | }, | ||
| 63 | } | ||
| 64 | } | ||
| 65 | |||
| 66 | pub fn elapsed(&self) -> Duration { | ||
| 67 | Instant::now() - *self | ||
| 68 | } | ||
| 69 | |||
| 70 | pub fn checked_add(&self, duration: Duration) -> Option<Instant> { | ||
| 71 | self.ticks | ||
| 72 | .checked_add(duration.ticks.into()) | ||
| 73 | .map(|ticks| Instant { ticks }) | ||
| 74 | } | ||
| 75 | pub fn checked_sub(&self, duration: Duration) -> Option<Instant> { | ||
| 76 | self.ticks | ||
| 77 | .checked_sub(duration.ticks.into()) | ||
| 78 | .map(|ticks| Instant { ticks }) | ||
| 79 | } | ||
| 80 | } | ||
| 81 | |||
| 82 | impl Add<Duration> for Instant { | ||
| 83 | type Output = Instant; | ||
| 84 | |||
| 85 | fn add(self, other: Duration) -> Instant { | ||
| 86 | self.checked_add(other) | ||
| 87 | .expect("overflow when adding duration to instant") | ||
| 88 | } | ||
| 89 | } | ||
| 90 | |||
| 91 | impl AddAssign<Duration> for Instant { | ||
| 92 | fn add_assign(&mut self, other: Duration) { | ||
| 93 | *self = *self + other; | ||
| 94 | } | ||
| 95 | } | ||
| 96 | |||
| 97 | impl Sub<Duration> for Instant { | ||
| 98 | type Output = Instant; | ||
| 99 | |||
| 100 | fn sub(self, other: Duration) -> Instant { | ||
| 101 | self.checked_sub(other) | ||
| 102 | .expect("overflow when subtracting duration from instant") | ||
| 103 | } | ||
| 104 | } | ||
| 105 | |||
| 106 | impl SubAssign<Duration> for Instant { | ||
| 107 | fn sub_assign(&mut self, other: Duration) { | ||
| 108 | *self = *self - other; | ||
| 109 | } | ||
| 110 | } | ||
| 111 | |||
| 112 | impl Sub<Instant> for Instant { | ||
| 113 | type Output = Duration; | ||
| 114 | |||
| 115 | fn sub(self, other: Instant) -> Duration { | ||
| 116 | self.duration_since(other) | ||
| 117 | } | ||
| 118 | } | ||
| 119 | |||
| 120 | #[derive(defmt::Format, Debug, Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] | ||
| 121 | pub struct Duration { | ||
| 122 | ticks: u32, | ||
| 123 | } | ||
| 124 | |||
| 125 | impl Duration { | ||
| 126 | pub fn into_ticks(&self) -> u32 { | ||
| 127 | self.ticks | ||
| 128 | } | ||
| 129 | |||
| 130 | pub const fn from_ticks(ticks: u32) -> Duration { | ||
| 131 | Duration { ticks } | ||
| 132 | } | ||
| 133 | |||
| 134 | pub const fn from_secs(secs: u32) -> Duration { | ||
| 135 | Duration { | ||
| 136 | ticks: secs * TICKS_PER_SECOND, | ||
| 137 | } | ||
| 138 | } | ||
| 139 | |||
| 140 | pub const fn from_millis(millis: u32) -> Duration { | ||
| 141 | Duration { | ||
| 142 | ticks: millis * TICKS_PER_SECOND / 1000, | ||
| 143 | } | ||
| 144 | } | ||
| 145 | |||
| 146 | pub fn checked_add(self, rhs: Duration) -> Option<Duration> { | ||
| 147 | self.ticks | ||
| 148 | .checked_add(rhs.ticks) | ||
| 149 | .map(|ticks| Duration { ticks }) | ||
| 150 | } | ||
| 151 | |||
| 152 | pub fn checked_sub(self, rhs: Duration) -> Option<Duration> { | ||
| 153 | self.ticks | ||
| 154 | .checked_sub(rhs.ticks) | ||
| 155 | .map(|ticks| Duration { ticks }) | ||
| 156 | } | ||
| 157 | |||
| 158 | pub fn checked_mul(self, rhs: u32) -> Option<Duration> { | ||
| 159 | self.ticks.checked_mul(rhs).map(|ticks| Duration { ticks }) | ||
| 160 | } | ||
| 161 | |||
| 162 | pub fn checked_div(self, rhs: u32) -> Option<Duration> { | ||
| 163 | self.ticks.checked_div(rhs).map(|ticks| Duration { ticks }) | ||
| 164 | } | ||
| 165 | } | ||
| 166 | |||
| 167 | impl Add for Duration { | ||
| 168 | type Output = Duration; | ||
| 169 | |||
| 170 | fn add(self, rhs: Duration) -> Duration { | ||
| 171 | self.checked_add(rhs) | ||
| 172 | .expect("overflow when adding durations") | ||
| 173 | } | ||
| 174 | } | ||
| 175 | |||
| 176 | impl AddAssign for Duration { | ||
| 177 | fn add_assign(&mut self, rhs: Duration) { | ||
| 178 | *self = *self + rhs; | ||
| 179 | } | ||
| 180 | } | ||
| 181 | |||
| 182 | impl Sub for Duration { | ||
| 183 | type Output = Duration; | ||
| 184 | |||
| 185 | fn sub(self, rhs: Duration) -> Duration { | ||
| 186 | self.checked_sub(rhs) | ||
| 187 | .expect("overflow when subtracting durations") | ||
| 188 | } | ||
| 189 | } | ||
| 190 | |||
| 191 | impl SubAssign for Duration { | ||
| 192 | fn sub_assign(&mut self, rhs: Duration) { | ||
| 193 | *self = *self - rhs; | ||
| 194 | } | ||
| 195 | } | ||
| 196 | |||
| 197 | impl Mul<u32> for Duration { | ||
| 198 | type Output = Duration; | ||
| 199 | |||
| 200 | fn mul(self, rhs: u32) -> Duration { | ||
| 201 | self.checked_mul(rhs) | ||
| 202 | .expect("overflow when multiplying duration by scalar") | ||
| 203 | } | ||
| 204 | } | ||
| 205 | |||
| 206 | impl Mul<Duration> for u32 { | ||
| 207 | type Output = Duration; | ||
| 208 | |||
| 209 | fn mul(self, rhs: Duration) -> Duration { | ||
| 210 | rhs * self | ||
| 211 | } | ||
| 212 | } | ||
| 213 | |||
| 214 | impl MulAssign<u32> for Duration { | ||
| 215 | fn mul_assign(&mut self, rhs: u32) { | ||
| 216 | *self = *self * rhs; | ||
| 217 | } | ||
| 218 | } | ||
| 219 | |||
| 220 | impl Div<u32> for Duration { | ||
| 221 | type Output = Duration; | ||
| 222 | |||
| 223 | fn div(self, rhs: u32) -> Duration { | ||
| 224 | self.checked_div(rhs) | ||
| 225 | .expect("divide by zero error when dividing duration by scalar") | ||
| 226 | } | ||
| 227 | } | ||
| 228 | |||
| 229 | impl DivAssign<u32> for Duration { | ||
| 230 | fn div_assign(&mut self, rhs: u32) { | ||
| 231 | *self = *self / rhs; | ||
| 232 | } | ||
| 233 | } | ||
| 234 | |||
| 235 | pub(crate) struct IntrusiveClock; | ||
| 236 | |||
| 237 | impl fi::Clock for IntrusiveClock { | ||
| 238 | fn now(&self) -> u64 { | ||
| 239 | now() | ||
| 240 | } | ||
| 241 | } | ||
| 242 | |||
| 243 | pub(crate) type TimerService = fi::LocalTimerService<IntrusiveClock>; | ||
| 244 | |||
| 245 | static CURRENT_TIMER_SERVICE: AtomicPtr<TimerService> = AtomicPtr::new(ptr::null_mut()); | ||
| 246 | |||
| 247 | pub(crate) fn with_timer_service<R>(svc: &'static TimerService, f: impl FnOnce() -> R) -> R { | ||
| 248 | let svc = svc as *const _ as *mut _; | ||
| 249 | let prev_svc = CURRENT_TIMER_SERVICE.swap(svc, Ordering::Relaxed); | ||
| 250 | let r = f(); | ||
| 251 | let svc2 = CURRENT_TIMER_SERVICE.swap(prev_svc, Ordering::Relaxed); | ||
| 252 | assert_eq!(svc, svc2); | ||
| 253 | r | ||
| 254 | } | ||
| 255 | |||
| 256 | fn current_timer_service() -> &'static TimerService { | ||
| 257 | unsafe { | ||
| 258 | CURRENT_TIMER_SERVICE | ||
| 259 | .load(Ordering::Relaxed) | ||
| 260 | .as_ref() | ||
| 261 | .unwrap() | ||
| 262 | } | ||
| 263 | } | ||
| 264 | |||
| 265 | pub struct Timer { | ||
| 266 | inner: fi::LocalTimerFuture<'static>, | ||
| 267 | } | ||
| 268 | |||
| 269 | impl Timer { | ||
| 270 | pub fn at(when: Instant) -> Self { | ||
| 271 | let svc: &TimerService = current_timer_service(); | ||
| 272 | Self { | ||
| 273 | inner: svc.deadline(when.into_ticks()), | ||
| 274 | } | ||
| 275 | } | ||
| 276 | |||
| 277 | pub fn after(dur: Duration) -> Self { | ||
| 278 | Self::at(Instant::now() + dur) | ||
| 279 | } | ||
| 280 | } | ||
| 281 | |||
| 282 | impl Future for Timer { | ||
| 283 | type Output = (); | ||
| 284 | fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> { | ||
| 285 | unsafe { Pin::new_unchecked(&mut self.get_unchecked_mut().inner) }.poll(cx) | ||
| 286 | } | ||
| 287 | } | ||
| 288 | /// Monotonic clock | ||
| 289 | pub trait Clock { | ||
| 290 | /// Return the current timestamp in ticks. | ||
| 291 | /// This is guaranteed to be monotonic, i.e. a call to now() will always return | ||
| 292 | /// a greater or equal value than earler calls. | ||
| 293 | fn now(&self) -> u64; | ||
| 294 | } | ||
| 295 | |||
| 296 | impl<T: Clock + ?Sized> Clock for &T { | ||
| 297 | fn now(&self) -> u64 { | ||
| 298 | T::now(self) | ||
| 299 | } | ||
| 300 | } | ||
| 301 | |||
| 302 | /// Trait to register a callback at a given timestamp. | ||
| 303 | pub trait Alarm { | ||
| 304 | /// Sets the callback function to be called when the alarm triggers. | ||
| 305 | /// The callback may be called from any context (interrupt or thread mode). | ||
| 306 | fn set_callback(&self, callback: fn()); | ||
| 307 | |||
| 308 | /// Sets an alarm at the given timestamp. When the clock reaches that | ||
| 309 | /// timestamp, the provided callback funcion will be called. | ||
| 310 | /// | ||
| 311 | /// When callback is called, it is guaranteed that now() will return a value greater or equal than timestamp. | ||
| 312 | /// | ||
| 313 | /// Only one alarm can be active at a time. This overwrites any previously-set alarm if any. | ||
| 314 | fn set(&self, timestamp: u64); | ||
| 315 | |||
| 316 | /// Clears the previously-set alarm. | ||
| 317 | /// If no alarm was set, this is a noop. | ||
| 318 | fn clear(&self); | ||
| 319 | } | ||
| 320 | |||
| 321 | impl<T: Alarm + ?Sized> Alarm for &T { | ||
| 322 | fn set_callback(&self, callback: fn()) { | ||
| 323 | T::set_callback(self, callback); | ||
| 324 | } | ||
| 325 | fn set(&self, timestamp: u64) { | ||
| 326 | T::set(self, timestamp); | ||
| 327 | } | ||
| 328 | fn clear(&self) { | ||
| 329 | T::clear(self) | ||
| 330 | } | ||
| 331 | } | ||
diff --git a/embassy/src/time/duration.rs b/embassy/src/time/duration.rs new file mode 100644 index 000000000..1d5ad7542 --- /dev/null +++ b/embassy/src/time/duration.rs | |||
| @@ -0,0 +1,118 @@ | |||
| 1 | use core::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Sub, SubAssign}; | ||
| 2 | |||
| 3 | use super::TICKS_PER_SECOND; | ||
| 4 | |||
| 5 | #[derive(defmt::Format, Debug, Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] | ||
| 6 | pub struct Duration { | ||
| 7 | pub(crate) ticks: u32, | ||
| 8 | } | ||
| 9 | |||
| 10 | impl Duration { | ||
| 11 | pub fn into_ticks(&self) -> u32 { | ||
| 12 | self.ticks | ||
| 13 | } | ||
| 14 | |||
| 15 | pub const fn from_ticks(ticks: u32) -> Duration { | ||
| 16 | Duration { ticks } | ||
| 17 | } | ||
| 18 | |||
| 19 | pub const fn from_secs(secs: u32) -> Duration { | ||
| 20 | Duration { | ||
| 21 | ticks: secs * TICKS_PER_SECOND, | ||
| 22 | } | ||
| 23 | } | ||
| 24 | |||
| 25 | pub const fn from_millis(millis: u32) -> Duration { | ||
| 26 | Duration { | ||
| 27 | ticks: millis * TICKS_PER_SECOND / 1000, | ||
| 28 | } | ||
| 29 | } | ||
| 30 | |||
| 31 | pub fn checked_add(self, rhs: Duration) -> Option<Duration> { | ||
| 32 | self.ticks | ||
| 33 | .checked_add(rhs.ticks) | ||
| 34 | .map(|ticks| Duration { ticks }) | ||
| 35 | } | ||
| 36 | |||
| 37 | pub fn checked_sub(self, rhs: Duration) -> Option<Duration> { | ||
| 38 | self.ticks | ||
| 39 | .checked_sub(rhs.ticks) | ||
| 40 | .map(|ticks| Duration { ticks }) | ||
| 41 | } | ||
| 42 | |||
| 43 | pub fn checked_mul(self, rhs: u32) -> Option<Duration> { | ||
| 44 | self.ticks.checked_mul(rhs).map(|ticks| Duration { ticks }) | ||
| 45 | } | ||
| 46 | |||
| 47 | pub fn checked_div(self, rhs: u32) -> Option<Duration> { | ||
| 48 | self.ticks.checked_div(rhs).map(|ticks| Duration { ticks }) | ||
| 49 | } | ||
| 50 | } | ||
| 51 | |||
| 52 | impl Add for Duration { | ||
| 53 | type Output = Duration; | ||
| 54 | |||
| 55 | fn add(self, rhs: Duration) -> Duration { | ||
| 56 | self.checked_add(rhs) | ||
| 57 | .expect("overflow when adding durations") | ||
| 58 | } | ||
| 59 | } | ||
| 60 | |||
| 61 | impl AddAssign for Duration { | ||
| 62 | fn add_assign(&mut self, rhs: Duration) { | ||
| 63 | *self = *self + rhs; | ||
| 64 | } | ||
| 65 | } | ||
| 66 | |||
| 67 | impl Sub for Duration { | ||
| 68 | type Output = Duration; | ||
| 69 | |||
| 70 | fn sub(self, rhs: Duration) -> Duration { | ||
| 71 | self.checked_sub(rhs) | ||
| 72 | .expect("overflow when subtracting durations") | ||
| 73 | } | ||
| 74 | } | ||
| 75 | |||
| 76 | impl SubAssign for Duration { | ||
| 77 | fn sub_assign(&mut self, rhs: Duration) { | ||
| 78 | *self = *self - rhs; | ||
| 79 | } | ||
| 80 | } | ||
| 81 | |||
| 82 | impl Mul<u32> for Duration { | ||
| 83 | type Output = Duration; | ||
| 84 | |||
| 85 | fn mul(self, rhs: u32) -> Duration { | ||
| 86 | self.checked_mul(rhs) | ||
| 87 | .expect("overflow when multiplying duration by scalar") | ||
| 88 | } | ||
| 89 | } | ||
| 90 | |||
| 91 | impl Mul<Duration> for u32 { | ||
| 92 | type Output = Duration; | ||
| 93 | |||
| 94 | fn mul(self, rhs: Duration) -> Duration { | ||
| 95 | rhs * self | ||
| 96 | } | ||
| 97 | } | ||
| 98 | |||
| 99 | impl MulAssign<u32> for Duration { | ||
| 100 | fn mul_assign(&mut self, rhs: u32) { | ||
| 101 | *self = *self * rhs; | ||
| 102 | } | ||
| 103 | } | ||
| 104 | |||
| 105 | impl Div<u32> for Duration { | ||
| 106 | type Output = Duration; | ||
| 107 | |||
| 108 | fn div(self, rhs: u32) -> Duration { | ||
| 109 | self.checked_div(rhs) | ||
| 110 | .expect("divide by zero error when dividing duration by scalar") | ||
| 111 | } | ||
| 112 | } | ||
| 113 | |||
| 114 | impl DivAssign<u32> for Duration { | ||
| 115 | fn div_assign(&mut self, rhs: u32) { | ||
| 116 | *self = *self / rhs; | ||
| 117 | } | ||
| 118 | } | ||
diff --git a/embassy/src/time/instant.rs b/embassy/src/time/instant.rs new file mode 100644 index 000000000..9c23d088b --- /dev/null +++ b/embassy/src/time/instant.rs | |||
| @@ -0,0 +1,102 @@ | |||
| 1 | use core::convert::TryInto; | ||
| 2 | use core::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Sub, SubAssign}; | ||
| 3 | use core::pin::Pin; | ||
| 4 | use core::ptr; | ||
| 5 | use core::sync::atomic::{AtomicPtr, Ordering}; | ||
| 6 | use core::task::{Context, Poll}; | ||
| 7 | |||
| 8 | use super::{now, Duration}; | ||
| 9 | |||
| 10 | #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] | ||
| 11 | pub struct Instant { | ||
| 12 | ticks: u64, | ||
| 13 | } | ||
| 14 | |||
| 15 | impl Instant { | ||
| 16 | pub fn now() -> Instant { | ||
| 17 | Instant { ticks: now() } | ||
| 18 | } | ||
| 19 | |||
| 20 | pub fn into_ticks(&self) -> u64 { | ||
| 21 | self.ticks | ||
| 22 | } | ||
| 23 | |||
| 24 | pub fn duration_since(&self, earlier: Instant) -> Duration { | ||
| 25 | Duration { | ||
| 26 | ticks: (self.ticks - earlier.ticks).try_into().unwrap(), | ||
| 27 | } | ||
| 28 | } | ||
| 29 | |||
| 30 | pub fn checked_duration_since(&self, earlier: Instant) -> Option<Duration> { | ||
| 31 | if self.ticks < earlier.ticks { | ||
| 32 | None | ||
| 33 | } else { | ||
| 34 | Some(Duration { | ||
| 35 | ticks: (self.ticks - earlier.ticks).try_into().unwrap(), | ||
| 36 | }) | ||
| 37 | } | ||
| 38 | } | ||
| 39 | |||
| 40 | pub fn saturating_duration_since(&self, earlier: Instant) -> Duration { | ||
| 41 | Duration { | ||
| 42 | ticks: if self.ticks < earlier.ticks { | ||
| 43 | 0 | ||
| 44 | } else { | ||
| 45 | (self.ticks - earlier.ticks).try_into().unwrap() | ||
| 46 | }, | ||
| 47 | } | ||
| 48 | } | ||
| 49 | |||
| 50 | pub fn elapsed(&self) -> Duration { | ||
| 51 | Instant::now() - *self | ||
| 52 | } | ||
| 53 | |||
| 54 | pub fn checked_add(&self, duration: Duration) -> Option<Instant> { | ||
| 55 | self.ticks | ||
| 56 | .checked_add(duration.ticks.into()) | ||
| 57 | .map(|ticks| Instant { ticks }) | ||
| 58 | } | ||
| 59 | pub fn checked_sub(&self, duration: Duration) -> Option<Instant> { | ||
| 60 | self.ticks | ||
| 61 | .checked_sub(duration.ticks.into()) | ||
| 62 | .map(|ticks| Instant { ticks }) | ||
| 63 | } | ||
| 64 | } | ||
| 65 | |||
| 66 | impl Add<Duration> for Instant { | ||
| 67 | type Output = Instant; | ||
| 68 | |||
| 69 | fn add(self, other: Duration) -> Instant { | ||
| 70 | self.checked_add(other) | ||
| 71 | .expect("overflow when adding duration to instant") | ||
| 72 | } | ||
| 73 | } | ||
| 74 | |||
| 75 | impl AddAssign<Duration> for Instant { | ||
| 76 | fn add_assign(&mut self, other: Duration) { | ||
| 77 | *self = *self + other; | ||
| 78 | } | ||
| 79 | } | ||
| 80 | |||
| 81 | impl Sub<Duration> for Instant { | ||
| 82 | type Output = Instant; | ||
| 83 | |||
| 84 | fn sub(self, other: Duration) -> Instant { | ||
| 85 | self.checked_sub(other) | ||
| 86 | .expect("overflow when subtracting duration from instant") | ||
| 87 | } | ||
| 88 | } | ||
| 89 | |||
| 90 | impl SubAssign<Duration> for Instant { | ||
| 91 | fn sub_assign(&mut self, other: Duration) { | ||
| 92 | *self = *self - other; | ||
| 93 | } | ||
| 94 | } | ||
| 95 | |||
| 96 | impl Sub<Instant> for Instant { | ||
| 97 | type Output = Duration; | ||
| 98 | |||
| 99 | fn sub(self, other: Instant) -> Duration { | ||
| 100 | self.duration_since(other) | ||
| 101 | } | ||
| 102 | } | ||
diff --git a/embassy/src/time/mod.rs b/embassy/src/time/mod.rs new file mode 100644 index 000000000..b3ae10e72 --- /dev/null +++ b/embassy/src/time/mod.rs | |||
| @@ -0,0 +1,24 @@ | |||
| 1 | mod duration; | ||
| 2 | mod instant; | ||
| 3 | mod timer; | ||
| 4 | mod traits; | ||
| 5 | |||
| 6 | pub use duration::Duration; | ||
| 7 | pub use instant::Instant; | ||
| 8 | pub use timer::Timer; | ||
| 9 | pub use traits::*; | ||
| 10 | |||
| 11 | use crate::util::Dewrap; | ||
| 12 | |||
| 13 | // TODO allow customizing, probably via Cargo features `tick-hz-32768` or something. | ||
| 14 | pub const TICKS_PER_SECOND: u32 = 32768; | ||
| 15 | |||
| 16 | static mut CLOCK: Option<&'static dyn Clock> = None; | ||
| 17 | |||
| 18 | pub unsafe fn set_clock(clock: &'static dyn Clock) { | ||
| 19 | CLOCK = Some(clock); | ||
| 20 | } | ||
| 21 | |||
| 22 | pub(crate) fn now() -> u64 { | ||
| 23 | unsafe { CLOCK.dexpect(defmt::intern!("No clock set")).now() } | ||
| 24 | } | ||
diff --git a/embassy/src/time/timer.rs b/embassy/src/time/timer.rs new file mode 100644 index 000000000..0315d9fba --- /dev/null +++ b/embassy/src/time/timer.rs | |||
| @@ -0,0 +1,30 @@ | |||
| 1 | use core::future::Future; | ||
| 2 | use core::pin::Pin; | ||
| 3 | use core::task::{Context, Poll}; | ||
| 4 | use futures_intrusive::timer::{LocalTimer, LocalTimerFuture}; | ||
| 5 | |||
| 6 | use super::{Duration, Instant}; | ||
| 7 | use crate::executor::current_timer_queue; | ||
| 8 | |||
| 9 | pub struct Timer { | ||
| 10 | inner: LocalTimerFuture<'static>, | ||
| 11 | } | ||
| 12 | |||
| 13 | impl Timer { | ||
| 14 | pub fn at(when: Instant) -> Self { | ||
| 15 | Self { | ||
| 16 | inner: current_timer_queue().deadline(when.into_ticks()), | ||
| 17 | } | ||
| 18 | } | ||
| 19 | |||
| 20 | pub fn after(dur: Duration) -> Self { | ||
| 21 | Self::at(Instant::now() + dur) | ||
| 22 | } | ||
| 23 | } | ||
| 24 | |||
| 25 | impl Future for Timer { | ||
| 26 | type Output = (); | ||
| 27 | fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> { | ||
| 28 | unsafe { Pin::new_unchecked(&mut self.get_unchecked_mut().inner) }.poll(cx) | ||
| 29 | } | ||
| 30 | } | ||
diff --git a/embassy/src/time/traits.rs b/embassy/src/time/traits.rs new file mode 100644 index 000000000..7faa27cdb --- /dev/null +++ b/embassy/src/time/traits.rs | |||
| @@ -0,0 +1,44 @@ | |||
| 1 | /// Monotonic clock | ||
| 2 | pub trait Clock { | ||
| 3 | /// Return the current timestamp in ticks. | ||
| 4 | /// This is guaranteed to be monotonic, i.e. a call to now() will always return | ||
| 5 | /// a greater or equal value than earler calls. | ||
| 6 | fn now(&self) -> u64; | ||
| 7 | } | ||
| 8 | |||
| 9 | impl<T: Clock + ?Sized> Clock for &T { | ||
| 10 | fn now(&self) -> u64 { | ||
| 11 | T::now(self) | ||
| 12 | } | ||
| 13 | } | ||
| 14 | |||
| 15 | /// Trait to register a callback at a given timestamp. | ||
| 16 | pub trait Alarm { | ||
| 17 | /// Sets the callback function to be called when the alarm triggers. | ||
| 18 | /// The callback may be called from any context (interrupt or thread mode). | ||
| 19 | fn set_callback(&self, callback: fn()); | ||
| 20 | |||
| 21 | /// Sets an alarm at the given timestamp. When the clock reaches that | ||
| 22 | /// timestamp, the provided callback funcion will be called. | ||
| 23 | /// | ||
| 24 | /// When callback is called, it is guaranteed that now() will return a value greater or equal than timestamp. | ||
| 25 | /// | ||
| 26 | /// Only one alarm can be active at a time. This overwrites any previously-set alarm if any. | ||
| 27 | fn set(&self, timestamp: u64); | ||
| 28 | |||
| 29 | /// Clears the previously-set alarm. | ||
| 30 | /// If no alarm was set, this is a noop. | ||
| 31 | fn clear(&self); | ||
| 32 | } | ||
| 33 | |||
| 34 | impl<T: Alarm + ?Sized> Alarm for &T { | ||
| 35 | fn set_callback(&self, callback: fn()) { | ||
| 36 | T::set_callback(self, callback); | ||
| 37 | } | ||
| 38 | fn set(&self, timestamp: u64) { | ||
| 39 | T::set(self, timestamp); | ||
| 40 | } | ||
| 41 | fn clear(&self) { | ||
| 42 | T::clear(self) | ||
| 43 | } | ||
| 44 | } | ||
diff --git a/examples/Cargo.toml b/examples/Cargo.toml index 7b49ffb1f..7c44b07a4 100644 --- a/examples/Cargo.toml +++ b/examples/Cargo.toml | |||
| @@ -26,5 +26,5 @@ panic-probe = "0.1.0" | |||
| 26 | nrf52840-hal = { version = "0.11.0" } | 26 | nrf52840-hal = { version = "0.11.0" } |
| 27 | embassy = { version = "0.1.0", path = "../embassy" } | 27 | embassy = { version = "0.1.0", path = "../embassy" } |
| 28 | embassy-nrf = { version = "0.1.0", path = "../embassy-nrf", features = ["defmt-trace", "52840"] } | 28 | embassy-nrf = { version = "0.1.0", path = "../embassy-nrf", features = ["defmt-trace", "52840"] } |
| 29 | static-executor = { version = "0.1.0", features=["defmt"]} | 29 | futures = { version = "0.3.5", default-features = false } |
| 30 | futures = { version = "0.3.5", default-features = false } \ No newline at end of file | 30 | cortex-m-rtic = { git = "https://github.com/rtic-rs/cortex-m-rtic", branch = "master"} \ No newline at end of file |
diff --git a/examples/src/bin/gpiote.rs b/examples/src/bin/gpiote.rs index 5578062fa..5a6ae9333 100644 --- a/examples/src/bin/gpiote.rs +++ b/examples/src/bin/gpiote.rs | |||
| @@ -13,7 +13,7 @@ use embassy_nrf::gpiote; | |||
| 13 | use futures::pin_mut; | 13 | use futures::pin_mut; |
| 14 | use nrf52840_hal::gpio; | 14 | use nrf52840_hal::gpio; |
| 15 | 15 | ||
| 16 | use static_executor::{task, Executor}; | 16 | use embassy::executor::{task, Executor}; |
| 17 | static EXECUTOR: Executor = Executor::new(|| cortex_m::asm::sev()); | 17 | static EXECUTOR: Executor = Executor::new(|| cortex_m::asm::sev()); |
| 18 | 18 | ||
| 19 | #[task] | 19 | #[task] |
diff --git a/examples/src/bin/qspi.rs b/examples/src/bin/qspi.rs index 4e6ee53ea..c60a666ec 100644 --- a/examples/src/bin/qspi.rs +++ b/examples/src/bin/qspi.rs | |||
| @@ -11,7 +11,7 @@ use embassy::flash::Flash; | |||
| 11 | use embassy_nrf::qspi; | 11 | use embassy_nrf::qspi; |
| 12 | use nrf52840_hal::gpio; | 12 | use nrf52840_hal::gpio; |
| 13 | 13 | ||
| 14 | use static_executor::{task, Executor}; | 14 | use embassy::executor::{task, Executor}; |
| 15 | static EXECUTOR: Executor = Executor::new(|| cortex_m::asm::sev()); | 15 | static EXECUTOR: Executor = Executor::new(|| cortex_m::asm::sev()); |
| 16 | 16 | ||
| 17 | const PAGE_SIZE: usize = 4096; | 17 | const PAGE_SIZE: usize = 4096; |
diff --git a/examples/src/bin/uart.rs b/examples/src/bin/uart.rs index 6b9df380a..0eec2cd8d 100644 --- a/examples/src/bin/uart.rs +++ b/examples/src/bin/uart.rs | |||
| @@ -12,7 +12,7 @@ use embassy_nrf::uarte; | |||
| 12 | use futures::pin_mut; | 12 | use futures::pin_mut; |
| 13 | use nrf52840_hal::gpio; | 13 | use nrf52840_hal::gpio; |
| 14 | 14 | ||
| 15 | use static_executor::{task, Executor}; | 15 | use embassy::executor::{task, Executor}; |
| 16 | static EXECUTOR: Executor = Executor::new(|| cortex_m::asm::sev()); | 16 | static EXECUTOR: Executor = Executor::new(|| cortex_m::asm::sev()); |
| 17 | 17 | ||
| 18 | #[task] | 18 | #[task] |
