aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDario Nieuwenhuis <[email protected]>2020-10-19 21:15:24 +0200
committerDario Nieuwenhuis <[email protected]>2020-10-19 21:25:54 +0200
commitcd9ecaef57982b33376dcfea3a0406a3df4b09c5 (patch)
tree41567981cfe2ba9a65e84a6025daf83cb2204fa5
parent0e1adc58f48a65c6af1d2ededa8712426fb3ab6e (diff)
integrate static-executor, cleanup time module.
-rw-r--r--Cargo.toml2
-rw-r--r--embassy-macros/Cargo.toml13
-rw-r--r--embassy-macros/src/lib.rs114
-rw-r--r--embassy/Cargo.toml2
-rw-r--r--embassy/src/executor.rs48
-rw-r--r--embassy/src/executor/executor.rs305
-rw-r--r--embassy/src/executor/mod.rs9
-rw-r--r--embassy/src/executor/timer_executor.rs77
-rw-r--r--embassy/src/time.rs331
-rw-r--r--embassy/src/time/duration.rs118
-rw-r--r--embassy/src/time/instant.rs102
-rw-r--r--embassy/src/time/mod.rs24
-rw-r--r--embassy/src/time/timer.rs30
-rw-r--r--embassy/src/time/traits.rs44
-rw-r--r--examples/Cargo.toml4
-rw-r--r--examples/src/bin/gpiote.rs2
-rw-r--r--examples/src/bin/qspi.rs2
-rw-r--r--examples/src/bin/uart.rs2
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 = [
14panic-probe = { git = "https://github.com/knurling-rs/probe-run", branch="main" } 14panic-probe = { git = "https://github.com/knurling-rs/probe-run", branch="main" }
15defmt-rtt = { git = "https://github.com/knurling-rs/defmt", branch="cursed-symbol-names-linkers-must-repent-for-their-sins" } 15defmt-rtt = { git = "https://github.com/knurling-rs/defmt", branch="cursed-symbol-names-linkers-must-repent-for-their-sins" }
16defmt = { git = "https://github.com/knurling-rs/defmt", branch="cursed-symbol-names-linkers-must-repent-for-their-sins" } 16defmt = { git = "https://github.com/knurling-rs/defmt", branch="cursed-symbol-names-linkers-must-repent-for-their-sins" }
17static-executor = { git = "https://github.com/Dirbaio/static-executor", branch="multi"}
18futures-intrusive = { git = "https://github.com/Dirbaio/futures-intrusive", branch="master"}
19 17
20[profile.dev] 18[profile.dev]
21codegen-units = 1 19codegen-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]
2name = "embassy-macros"
3version = "0.1.0"
4authors = ["Dario Nieuwenhuis <[email protected]>"]
5edition = "2018"
6
7[dependencies]
8syn = { version = "1.0.39", features = ["full", "extra-traits"] }
9quote = "1.0.7"
10darling = "0.10.2"
11
12[lib]
13proc-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
3extern crate proc_macro;
4
5use darling::FromMeta;
6use proc_macro::{Diagnostic, Level, Span, TokenStream};
7use quote::{format_ident, quote};
8use syn::spanned::Spanned;
9
10#[derive(Debug, FromMeta)]
11struct MacroArgs {
12 #[darling(default)]
13 pool_size: Option<usize>,
14}
15
16#[proc_macro_attribute]
17pub 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"
13futures = { version = "0.3.5", default-features = false, features = [ "async-await" ] } 13futures = { version = "0.3.5", default-features = false, features = [ "async-await" ] }
14pin-project = { version = "0.4.23", default-features = false } 14pin-project = { version = "0.4.23", default-features = false }
15futures-intrusive = { version = "0.3.1", default-features = false } 15futures-intrusive = { version = "0.3.1", default-features = false }
16static-executor = { version = "0.1.0", features=["defmt"]} 16embassy-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 @@
1use core::marker::PhantomData;
2use static_executor as se;
3
4use crate::time;
5use crate::time::Alarm;
6
7pub use se::{task, SpawnError, SpawnToken};
8
9pub struct Executor<A: Alarm> {
10 inner: se::Executor,
11 alarm: A,
12 timer: time::TimerService,
13}
14
15impl<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
4use core::cell::Cell;
5use core::cell::UnsafeCell;
6use core::future::Future;
7use core::mem;
8use core::mem::MaybeUninit;
9use core::pin::Pin;
10use core::ptr;
11use core::ptr::NonNull;
12use core::sync::atomic::{AtomicPtr, AtomicU32, Ordering};
13use core::task::{Context, Poll, RawWaker, RawWakerVTable, Waker};
14
15//=============
16// UninitCell
17
18struct UninitCell<T>(MaybeUninit<UnsafeCell<T>>);
19impl<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
41impl<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
50const STATE_RUNNING: u32 = 1 << 0;
51const STATE_QUEUED: u32 = 1 << 1;
52
53struct 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)]
63pub 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))]
70pub 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
85struct Queue {
86 head: AtomicPtr<Header>,
87}
88
89impl 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
133static WAKER_VTABLE: RawWakerVTable =
134 RawWakerVTable::new(waker_clone, waker_wake, waker_wake, waker_drop);
135
136unsafe fn waker_clone(p: *const ()) -> RawWaker {
137 RawWaker::new(p, &WAKER_VTABLE)
138}
139
140unsafe 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
167unsafe fn waker_drop(_: *const ()) {
168 // nop
169}
170
171//=============
172// Task
173
174impl<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
227unsafe 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()"]
233pub struct SpawnToken {
234 header: Option<NonNull<Header>>,
235}
236
237impl 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
247pub struct Executor {
248 queue: Queue,
249 signal_fn: fn(),
250}
251
252impl 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 @@
1mod executor;
2mod timer_executor;
3
4// for time::Timer
5pub(crate) use timer_executor::current_timer_queue;
6
7pub use embassy_macros::task;
8pub use executor::{Executor, SpawnError, SpawnToken, Task};
9pub 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 @@
1use super::executor::{Executor, SpawnError, SpawnToken};
2use core::ptr;
3use core::sync::atomic::{AtomicPtr, Ordering};
4use futures_intrusive::timer as fi;
5
6use crate::time::Alarm;
7
8pub(crate) struct IntrusiveClock;
9
10impl fi::Clock for IntrusiveClock {
11 fn now(&self) -> u64 {
12 crate::time::now()
13 }
14}
15
16pub(crate) type TimerQueue = fi::LocalTimerService;
17
18pub struct TimerExecutor<A: Alarm> {
19 inner: Executor,
20 alarm: A,
21 timer_queue: TimerQueue,
22}
23
24impl<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
59static CURRENT_TIMER_QUEUE: AtomicPtr<TimerQueue> = AtomicPtr::new(ptr::null_mut());
60
61fn 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
70pub(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 @@
1use core::cell::Cell;
2use core::convert::TryInto;
3use core::future::Future;
4use core::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Sub, SubAssign};
5use core::pin::Pin;
6use core::ptr;
7use core::sync::atomic::{AtomicPtr, Ordering};
8use core::task::{Context, Poll};
9
10use crate::util::*;
11use fi::LocalTimer;
12use futures_intrusive::timer as fi;
13static mut CLOCK: Option<&'static dyn Clock> = None;
14
15pub unsafe fn set_clock(clock: &'static dyn Clock) {
16 CLOCK = Some(clock);
17}
18
19fn now() -> u64 {
20 unsafe { CLOCK.dexpect(defmt::intern!("No clock set")).now() }
21}
22
23#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
24pub struct Instant {
25 ticks: u64,
26}
27
28// TODO allow customizing, probably via Cargo features `tick-hz-32768` or something.
29pub const TICKS_PER_SECOND: u32 = 32768;
30
31impl 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
82impl 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
91impl AddAssign<Duration> for Instant {
92 fn add_assign(&mut self, other: Duration) {
93 *self = *self + other;
94 }
95}
96
97impl 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
106impl SubAssign<Duration> for Instant {
107 fn sub_assign(&mut self, other: Duration) {
108 *self = *self - other;
109 }
110}
111
112impl 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)]
121pub struct Duration {
122 ticks: u32,
123}
124
125impl 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
167impl 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
176impl AddAssign for Duration {
177 fn add_assign(&mut self, rhs: Duration) {
178 *self = *self + rhs;
179 }
180}
181
182impl 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
191impl SubAssign for Duration {
192 fn sub_assign(&mut self, rhs: Duration) {
193 *self = *self - rhs;
194 }
195}
196
197impl 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
206impl Mul<Duration> for u32 {
207 type Output = Duration;
208
209 fn mul(self, rhs: Duration) -> Duration {
210 rhs * self
211 }
212}
213
214impl MulAssign<u32> for Duration {
215 fn mul_assign(&mut self, rhs: u32) {
216 *self = *self * rhs;
217 }
218}
219
220impl 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
229impl DivAssign<u32> for Duration {
230 fn div_assign(&mut self, rhs: u32) {
231 *self = *self / rhs;
232 }
233}
234
235pub(crate) struct IntrusiveClock;
236
237impl fi::Clock for IntrusiveClock {
238 fn now(&self) -> u64 {
239 now()
240 }
241}
242
243pub(crate) type TimerService = fi::LocalTimerService<IntrusiveClock>;
244
245static CURRENT_TIMER_SERVICE: AtomicPtr<TimerService> = AtomicPtr::new(ptr::null_mut());
246
247pub(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
256fn current_timer_service() -> &'static TimerService {
257 unsafe {
258 CURRENT_TIMER_SERVICE
259 .load(Ordering::Relaxed)
260 .as_ref()
261 .unwrap()
262 }
263}
264
265pub struct Timer {
266 inner: fi::LocalTimerFuture<'static>,
267}
268
269impl 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
282impl 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
289pub 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
296impl<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.
303pub 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
321impl<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 @@
1use core::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Sub, SubAssign};
2
3use super::TICKS_PER_SECOND;
4
5#[derive(defmt::Format, Debug, Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
6pub struct Duration {
7 pub(crate) ticks: u32,
8}
9
10impl 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
52impl 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
61impl AddAssign for Duration {
62 fn add_assign(&mut self, rhs: Duration) {
63 *self = *self + rhs;
64 }
65}
66
67impl 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
76impl SubAssign for Duration {
77 fn sub_assign(&mut self, rhs: Duration) {
78 *self = *self - rhs;
79 }
80}
81
82impl 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
91impl Mul<Duration> for u32 {
92 type Output = Duration;
93
94 fn mul(self, rhs: Duration) -> Duration {
95 rhs * self
96 }
97}
98
99impl MulAssign<u32> for Duration {
100 fn mul_assign(&mut self, rhs: u32) {
101 *self = *self * rhs;
102 }
103}
104
105impl 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
114impl 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 @@
1use core::convert::TryInto;
2use core::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Sub, SubAssign};
3use core::pin::Pin;
4use core::ptr;
5use core::sync::atomic::{AtomicPtr, Ordering};
6use core::task::{Context, Poll};
7
8use super::{now, Duration};
9
10#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
11pub struct Instant {
12 ticks: u64,
13}
14
15impl 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
66impl 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
75impl AddAssign<Duration> for Instant {
76 fn add_assign(&mut self, other: Duration) {
77 *self = *self + other;
78 }
79}
80
81impl 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
90impl SubAssign<Duration> for Instant {
91 fn sub_assign(&mut self, other: Duration) {
92 *self = *self - other;
93 }
94}
95
96impl 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 @@
1mod duration;
2mod instant;
3mod timer;
4mod traits;
5
6pub use duration::Duration;
7pub use instant::Instant;
8pub use timer::Timer;
9pub use traits::*;
10
11use crate::util::Dewrap;
12
13// TODO allow customizing, probably via Cargo features `tick-hz-32768` or something.
14pub const TICKS_PER_SECOND: u32 = 32768;
15
16static mut CLOCK: Option<&'static dyn Clock> = None;
17
18pub unsafe fn set_clock(clock: &'static dyn Clock) {
19 CLOCK = Some(clock);
20}
21
22pub(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 @@
1use core::future::Future;
2use core::pin::Pin;
3use core::task::{Context, Poll};
4use futures_intrusive::timer::{LocalTimer, LocalTimerFuture};
5
6use super::{Duration, Instant};
7use crate::executor::current_timer_queue;
8
9pub struct Timer {
10 inner: LocalTimerFuture<'static>,
11}
12
13impl 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
25impl 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
2pub 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
9impl<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.
16pub 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
34impl<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"
26nrf52840-hal = { version = "0.11.0" } 26nrf52840-hal = { version = "0.11.0" }
27embassy = { version = "0.1.0", path = "../embassy" } 27embassy = { version = "0.1.0", path = "../embassy" }
28embassy-nrf = { version = "0.1.0", path = "../embassy-nrf", features = ["defmt-trace", "52840"] } 28embassy-nrf = { version = "0.1.0", path = "../embassy-nrf", features = ["defmt-trace", "52840"] }
29static-executor = { version = "0.1.0", features=["defmt"]} 29futures = { version = "0.3.5", default-features = false }
30futures = { version = "0.3.5", default-features = false } \ No newline at end of file 30cortex-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;
13use futures::pin_mut; 13use futures::pin_mut;
14use nrf52840_hal::gpio; 14use nrf52840_hal::gpio;
15 15
16use static_executor::{task, Executor}; 16use embassy::executor::{task, Executor};
17static EXECUTOR: Executor = Executor::new(|| cortex_m::asm::sev()); 17static 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;
11use embassy_nrf::qspi; 11use embassy_nrf::qspi;
12use nrf52840_hal::gpio; 12use nrf52840_hal::gpio;
13 13
14use static_executor::{task, Executor}; 14use embassy::executor::{task, Executor};
15static EXECUTOR: Executor = Executor::new(|| cortex_m::asm::sev()); 15static EXECUTOR: Executor = Executor::new(|| cortex_m::asm::sev());
16 16
17const PAGE_SIZE: usize = 4096; 17const 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;
12use futures::pin_mut; 12use futures::pin_mut;
13use nrf52840_hal::gpio; 13use nrf52840_hal::gpio;
14 14
15use static_executor::{task, Executor}; 15use embassy::executor::{task, Executor};
16static EXECUTOR: Executor = Executor::new(|| cortex_m::asm::sev()); 16static EXECUTOR: Executor = Executor::new(|| cortex_m::asm::sev());
17 17
18#[task] 18#[task]