aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDario Nieuwenhuis <[email protected]>2021-08-26 23:37:37 +0200
committerGitHub <[email protected]>2021-08-26 23:37:37 +0200
commite56c6166dcac9132cde1769e5ef8d60e03963329 (patch)
tree81c076e27a66a3d825bfc19b08a45ab0035bf271
parent37e0ad299c027785f90bba4ffdb86d6c91a83633 (diff)
parentc4b9c8ac8762fdca52b6c37dd99346b2371387ab (diff)
Merge pull request #373 from embassy-rs/docs
Time driver improvements, docs.
-rw-r--r--Cargo.example.toml1
-rw-r--r--embassy-nrf/src/time_driver.rs55
-rw-r--r--embassy-rp/src/timer.rs124
-rw-r--r--embassy-stm32/src/time_driver.rs62
-rw-r--r--embassy/Cargo.toml10
-rw-r--r--embassy/src/executor/arch/std.rs67
-rw-r--r--embassy/src/executor/mod.rs3
-rw-r--r--embassy/src/io/mod.rs4
-rw-r--r--embassy/src/io/std.rs35
-rw-r--r--embassy/src/io/traits.rs37
-rw-r--r--embassy/src/time/delay.rs9
-rw-r--r--embassy/src/time/driver.rs110
-rw-r--r--embassy/src/time/driver_std.rs208
-rw-r--r--embassy/src/time/duration.rs9
-rw-r--r--embassy/src/time/instant.rs26
-rw-r--r--embassy/src/time/mod.rs59
-rw-r--r--embassy/src/time/timer.rs6
-rw-r--r--examples/std/Cargo.toml3
-rw-r--r--examples/std/src/bin/net.rs3
-rw-r--r--examples/std/src/bin/serial.rs2
-rw-r--r--examples/std/src/bin/tick.rs2
21 files changed, 597 insertions, 238 deletions
diff --git a/Cargo.example.toml b/Cargo.example.toml
index f072c2f96..0e9d1e32d 100644
--- a/Cargo.example.toml
+++ b/Cargo.example.toml
@@ -45,7 +45,6 @@ members = [
45 #"examples/rp", 45 #"examples/rp",
46 46
47 # std 47 # std
48 #"embassy-std",
49 #"examples/std", 48 #"examples/std",
50] 49]
51 50
diff --git a/embassy-nrf/src/time_driver.rs b/embassy-nrf/src/time_driver.rs
index 30461633a..f93ebb54a 100644
--- a/embassy-nrf/src/time_driver.rs
+++ b/embassy-nrf/src/time_driver.rs
@@ -82,7 +82,7 @@ impl AlarmState {
82 82
83const ALARM_COUNT: usize = 3; 83const ALARM_COUNT: usize = 3;
84 84
85struct State { 85struct RtcDriver {
86 /// Number of 2^23 periods elapsed since boot. 86 /// Number of 2^23 periods elapsed since boot.
87 period: AtomicU32, 87 period: AtomicU32,
88 alarm_count: AtomicU8, 88 alarm_count: AtomicU8,
@@ -91,13 +91,13 @@ struct State {
91} 91}
92 92
93const ALARM_STATE_NEW: AlarmState = AlarmState::new(); 93const ALARM_STATE_NEW: AlarmState = AlarmState::new();
94static STATE: State = State { 94embassy::time_driver_impl!(static DRIVER: RtcDriver = RtcDriver {
95 period: AtomicU32::new(0), 95 period: AtomicU32::new(0),
96 alarm_count: AtomicU8::new(0), 96 alarm_count: AtomicU8::new(0),
97 alarms: Mutex::new([ALARM_STATE_NEW; ALARM_COUNT]), 97 alarms: Mutex::new([ALARM_STATE_NEW; ALARM_COUNT]),
98}; 98});
99 99
100impl State { 100impl RtcDriver {
101 fn init(&'static self, irq_prio: crate::interrupt::Priority) { 101 fn init(&'static self, irq_prio: crate::interrupt::Priority) {
102 let r = rtc(); 102 let r = rtc();
103 r.cc[3].write(|w| unsafe { w.bits(0x800000) }); 103 r.cc[3].write(|w| unsafe { w.bits(0x800000) });
@@ -159,14 +159,6 @@ impl State {
159 }) 159 })
160 } 160 }
161 161
162 fn now(&self) -> u64 {
163 // `period` MUST be read before `counter`, see comment at the top for details.
164 let period = self.period.load(Ordering::Relaxed);
165 compiler_fence(Ordering::Acquire);
166 let counter = rtc().counter.read().bits();
167 calc_now(period, counter)
168 }
169
170 fn get_alarm<'a>(&'a self, cs: CriticalSection<'a>, alarm: AlarmHandle) -> &'a AlarmState { 162 fn get_alarm<'a>(&'a self, cs: CriticalSection<'a>, alarm: AlarmHandle) -> &'a AlarmState {
171 // safety: we're allowed to assume the AlarmState is created by us, and 163 // safety: we're allowed to assume the AlarmState is created by us, and
172 // we never create one that's out of bounds. 164 // we never create one that's out of bounds.
@@ -188,8 +180,18 @@ impl State {
188 let f: fn(*mut ()) = unsafe { mem::transmute(alarm.callback.get()) }; 180 let f: fn(*mut ()) = unsafe { mem::transmute(alarm.callback.get()) };
189 f(alarm.ctx.get()); 181 f(alarm.ctx.get());
190 } 182 }
183}
191 184
192 fn allocate_alarm(&self) -> Option<AlarmHandle> { 185impl Driver for RtcDriver {
186 fn now(&self) -> u64 {
187 // `period` MUST be read before `counter`, see comment at the top for details.
188 let period = self.period.load(Ordering::Relaxed);
189 compiler_fence(Ordering::Acquire);
190 let counter = rtc().counter.read().bits();
191 calc_now(period, counter)
192 }
193
194 unsafe fn allocate_alarm(&self) -> Option<AlarmHandle> {
193 let id = self 195 let id = self
194 .alarm_count 196 .alarm_count
195 .fetch_update(Ordering::AcqRel, Ordering::Acquire, |x| { 197 .fetch_update(Ordering::AcqRel, Ordering::Acquire, |x| {
@@ -201,7 +203,7 @@ impl State {
201 }); 203 });
202 204
203 match id { 205 match id {
204 Ok(id) => Some(unsafe { AlarmHandle::new(id) }), 206 Ok(id) => Some(AlarmHandle::new(id)),
205 Err(_) => None, 207 Err(_) => None,
206 } 208 }
207 } 209 }
@@ -263,32 +265,11 @@ impl State {
263 } 265 }
264} 266}
265 267
266struct RtcDriver;
267embassy::time_driver_impl!(RtcDriver);
268
269impl Driver for RtcDriver {
270 fn now() -> u64 {
271 STATE.now()
272 }
273
274 unsafe fn allocate_alarm() -> Option<AlarmHandle> {
275 STATE.allocate_alarm()
276 }
277
278 fn set_alarm_callback(alarm: AlarmHandle, callback: fn(*mut ()), ctx: *mut ()) {
279 STATE.set_alarm_callback(alarm, callback, ctx)
280 }
281
282 fn set_alarm(alarm: AlarmHandle, timestamp: u64) {
283 STATE.set_alarm(alarm, timestamp)
284 }
285}
286
287#[interrupt] 268#[interrupt]
288fn RTC1() { 269fn RTC1() {
289 STATE.on_interrupt() 270 DRIVER.on_interrupt()
290} 271}
291 272
292pub(crate) fn init(irq_prio: crate::interrupt::Priority) { 273pub(crate) fn init(irq_prio: crate::interrupt::Priority) {
293 STATE.init(irq_prio) 274 DRIVER.init(irq_prio)
294} 275}
diff --git a/embassy-rp/src/timer.rs b/embassy-rp/src/timer.rs
index 71c59ec8d..ed265c47f 100644
--- a/embassy-rp/src/timer.rs
+++ b/embassy-rp/src/timer.rs
@@ -19,38 +19,40 @@ const DUMMY_ALARM: AlarmState = AlarmState {
19 callback: Cell::new(None), 19 callback: Cell::new(None),
20}; 20};
21 21
22static ALARMS: Mutex<[AlarmState; ALARM_COUNT]> = Mutex::new([DUMMY_ALARM; ALARM_COUNT]); 22struct TimerDriver {
23static NEXT_ALARM: AtomicU8 = AtomicU8::new(0); 23 alarms: Mutex<[AlarmState; ALARM_COUNT]>,
24 24 next_alarm: AtomicU8,
25fn now() -> u64 {
26 loop {
27 unsafe {
28 let hi = pac::TIMER.timerawh().read();
29 let lo = pac::TIMER.timerawl().read();
30 let hi2 = pac::TIMER.timerawh().read();
31 if hi == hi2 {
32 return (hi as u64) << 32 | (lo as u64);
33 }
34 }
35 }
36} 25}
37 26
38struct TimerDriver; 27embassy::time_driver_impl!(static DRIVER: TimerDriver = TimerDriver{
39embassy::time_driver_impl!(TimerDriver); 28 alarms: Mutex::new([DUMMY_ALARM; ALARM_COUNT]),
29 next_alarm: AtomicU8::new(0),
30});
40 31
41impl Driver for TimerDriver { 32impl Driver for TimerDriver {
42 fn now() -> u64 { 33 fn now(&self) -> u64 {
43 now() 34 loop {
35 unsafe {
36 let hi = pac::TIMER.timerawh().read();
37 let lo = pac::TIMER.timerawl().read();
38 let hi2 = pac::TIMER.timerawh().read();
39 if hi == hi2 {
40 return (hi as u64) << 32 | (lo as u64);
41 }
42 }
43 }
44 } 44 }
45 45
46 unsafe fn allocate_alarm() -> Option<AlarmHandle> { 46 unsafe fn allocate_alarm(&self) -> Option<AlarmHandle> {
47 let id = NEXT_ALARM.fetch_update(Ordering::AcqRel, Ordering::Acquire, |x| { 47 let id = self
48 if x < ALARM_COUNT as u8 { 48 .next_alarm
49 Some(x + 1) 49 .fetch_update(Ordering::AcqRel, Ordering::Acquire, |x| {
50 } else { 50 if x < ALARM_COUNT as u8 {
51 None 51 Some(x + 1)
52 } 52 } else {
53 }); 53 None
54 }
55 });
54 56
55 match id { 57 match id {
56 Ok(id) => Some(AlarmHandle::new(id)), 58 Ok(id) => Some(AlarmHandle::new(id)),
@@ -58,18 +60,18 @@ impl Driver for TimerDriver {
58 } 60 }
59 } 61 }
60 62
61 fn set_alarm_callback(alarm: AlarmHandle, callback: fn(*mut ()), ctx: *mut ()) { 63 fn set_alarm_callback(&self, alarm: AlarmHandle, callback: fn(*mut ()), ctx: *mut ()) {
62 let n = alarm.id() as usize; 64 let n = alarm.id() as usize;
63 critical_section::with(|cs| { 65 critical_section::with(|cs| {
64 let alarm = &ALARMS.borrow(cs)[n]; 66 let alarm = &self.alarms.borrow(cs)[n];
65 alarm.callback.set(Some((callback, ctx))); 67 alarm.callback.set(Some((callback, ctx)));
66 }) 68 })
67 } 69 }
68 70
69 fn set_alarm(alarm: AlarmHandle, timestamp: u64) { 71 fn set_alarm(&self, alarm: AlarmHandle, timestamp: u64) {
70 let n = alarm.id() as usize; 72 let n = alarm.id() as usize;
71 critical_section::with(|cs| { 73 critical_section::with(|cs| {
72 let alarm = &ALARMS.borrow(cs)[n]; 74 let alarm = &self.alarms.borrow(cs)[n];
73 alarm.timestamp.set(timestamp); 75 alarm.timestamp.set(timestamp);
74 76
75 // Arm it. 77 // Arm it.
@@ -78,44 +80,46 @@ impl Driver for TimerDriver {
78 // it is checked if the alarm time has passed. 80 // it is checked if the alarm time has passed.
79 unsafe { pac::TIMER.alarm(n).write_value(timestamp as u32) }; 81 unsafe { pac::TIMER.alarm(n).write_value(timestamp as u32) };
80 82
81 let now = now(); 83 let now = self.now();
82 84
83 // If alarm timestamp has passed, trigger it instantly. 85 // If alarm timestamp has passed, trigger it instantly.
84 // This disarms it. 86 // This disarms it.
85 if timestamp <= now { 87 if timestamp <= now {
86 trigger_alarm(n, cs); 88 self.trigger_alarm(n, cs);
87 } 89 }
88 }) 90 })
89 } 91 }
90} 92}
91 93
92fn check_alarm(n: usize) { 94impl TimerDriver {
93 critical_section::with(|cs| { 95 fn check_alarm(&self, n: usize) {
94 let alarm = &ALARMS.borrow(cs)[n]; 96 critical_section::with(|cs| {
95 let timestamp = alarm.timestamp.get(); 97 let alarm = &self.alarms.borrow(cs)[n];
96 if timestamp <= now() { 98 let timestamp = alarm.timestamp.get();
97 trigger_alarm(n, cs) 99 if timestamp <= self.now() {
98 } else { 100 self.trigger_alarm(n, cs)
99 // Not elapsed, arm it again. 101 } else {
100 // This can happen if it was set more than 2^32 us in the future. 102 // Not elapsed, arm it again.
101 unsafe { pac::TIMER.alarm(n).write_value(timestamp as u32) }; 103 // This can happen if it was set more than 2^32 us in the future.
102 } 104 unsafe { pac::TIMER.alarm(n).write_value(timestamp as u32) };
103 }); 105 }
106 });
104 107
105 // clear the irq 108 // clear the irq
106 unsafe { pac::TIMER.intr().write(|w| w.set_alarm(n, true)) } 109 unsafe { pac::TIMER.intr().write(|w| w.set_alarm(n, true)) }
107} 110 }
108 111
109fn trigger_alarm(n: usize, cs: CriticalSection) { 112 fn trigger_alarm(&self, n: usize, cs: CriticalSection) {
110 // disarm 113 // disarm
111 unsafe { pac::TIMER.armed().write(|w| w.set_armed(1 << n)) } 114 unsafe { pac::TIMER.armed().write(|w| w.set_armed(1 << n)) }
112 115
113 let alarm = &ALARMS.borrow(cs)[n]; 116 let alarm = &self.alarms.borrow(cs)[n];
114 alarm.timestamp.set(u64::MAX); 117 alarm.timestamp.set(u64::MAX);
115 118
116 // Call after clearing alarm, so the callback can set another alarm. 119 // Call after clearing alarm, so the callback can set another alarm.
117 if let Some((f, ctx)) = alarm.callback.get() { 120 if let Some((f, ctx)) = alarm.callback.get() {
118 f(ctx); 121 f(ctx);
122 }
119 } 123 }
120} 124}
121 125
@@ -123,7 +127,7 @@ fn trigger_alarm(n: usize, cs: CriticalSection) {
123pub unsafe fn init() { 127pub unsafe fn init() {
124 // init alarms 128 // init alarms
125 critical_section::with(|cs| { 129 critical_section::with(|cs| {
126 let alarms = ALARMS.borrow(cs); 130 let alarms = DRIVER.alarms.borrow(cs);
127 for a in alarms { 131 for a in alarms {
128 a.timestamp.set(u64::MAX); 132 a.timestamp.set(u64::MAX);
129 } 133 }
@@ -144,20 +148,20 @@ pub unsafe fn init() {
144 148
145#[interrupt] 149#[interrupt]
146unsafe fn TIMER_IRQ_0() { 150unsafe fn TIMER_IRQ_0() {
147 check_alarm(0) 151 DRIVER.check_alarm(0)
148} 152}
149 153
150#[interrupt] 154#[interrupt]
151unsafe fn TIMER_IRQ_1() { 155unsafe fn TIMER_IRQ_1() {
152 check_alarm(1) 156 DRIVER.check_alarm(1)
153} 157}
154 158
155#[interrupt] 159#[interrupt]
156unsafe fn TIMER_IRQ_2() { 160unsafe fn TIMER_IRQ_2() {
157 check_alarm(2) 161 DRIVER.check_alarm(2)
158} 162}
159 163
160#[interrupt] 164#[interrupt]
161unsafe fn TIMER_IRQ_3() { 165unsafe fn TIMER_IRQ_3() {
162 check_alarm(3) 166 DRIVER.check_alarm(3)
163} 167}
diff --git a/embassy-stm32/src/time_driver.rs b/embassy-stm32/src/time_driver.rs
index 91b8525ae..4e1eb7aaa 100644
--- a/embassy-stm32/src/time_driver.rs
+++ b/embassy-stm32/src/time_driver.rs
@@ -26,12 +26,12 @@ type T = peripherals::TIM3;
26#[cfg(feature = "time-driver-tim2")] 26#[cfg(feature = "time-driver-tim2")]
27#[interrupt] 27#[interrupt]
28fn TIM2() { 28fn TIM2() {
29 STATE.on_interrupt() 29 DRIVER.on_interrupt()
30} 30}
31#[cfg(feature = "time-driver-tim3")] 31#[cfg(feature = "time-driver-tim3")]
32#[interrupt] 32#[interrupt]
33fn TIM3() { 33fn TIM3() {
34 STATE.on_interrupt() 34 DRIVER.on_interrupt()
35} 35}
36 36
37// Clock timekeeping works with something we call "periods", which are time intervals 37// Clock timekeeping works with something we call "periods", which are time intervals
@@ -76,7 +76,7 @@ impl AlarmState {
76 } 76 }
77} 77}
78 78
79struct State { 79struct RtcDriver {
80 /// Number of 2^15 periods elapsed since boot. 80 /// Number of 2^15 periods elapsed since boot.
81 period: AtomicU32, 81 period: AtomicU32,
82 alarm_count: AtomicU8, 82 alarm_count: AtomicU8,
@@ -85,13 +85,14 @@ struct State {
85} 85}
86 86
87const ALARM_STATE_NEW: AlarmState = AlarmState::new(); 87const ALARM_STATE_NEW: AlarmState = AlarmState::new();
88static STATE: State = State { 88
89embassy::time_driver_impl!(static DRIVER: RtcDriver = RtcDriver {
89 period: AtomicU32::new(0), 90 period: AtomicU32::new(0),
90 alarm_count: AtomicU8::new(0), 91 alarm_count: AtomicU8::new(0),
91 alarms: Mutex::new([ALARM_STATE_NEW; ALARM_COUNT]), 92 alarms: Mutex::new([ALARM_STATE_NEW; ALARM_COUNT]),
92}; 93});
93 94
94impl State { 95impl RtcDriver {
95 fn init(&'static self) { 96 fn init(&'static self) {
96 let r = T::regs(); 97 let r = T::regs();
97 98
@@ -185,16 +186,6 @@ impl State {
185 }) 186 })
186 } 187 }
187 188
188 fn now(&self) -> u64 {
189 let r = T::regs();
190
191 let period = self.period.load(Ordering::Relaxed);
192 compiler_fence(Ordering::Acquire);
193 // NOTE(unsafe) Atomic read with no side-effects
194 let counter = unsafe { r.cnt().read().cnt() };
195 calc_now(period, counter)
196 }
197
198 fn get_alarm<'a>(&'a self, cs: CriticalSection<'a>, alarm: AlarmHandle) -> &'a AlarmState { 189 fn get_alarm<'a>(&'a self, cs: CriticalSection<'a>, alarm: AlarmHandle) -> &'a AlarmState {
199 // safety: we're allowed to assume the AlarmState is created by us, and 190 // safety: we're allowed to assume the AlarmState is created by us, and
200 // we never create one that's out of bounds. 191 // we never create one that's out of bounds.
@@ -213,8 +204,20 @@ impl State {
213 let f: fn(*mut ()) = unsafe { mem::transmute(alarm.callback.get()) }; 204 let f: fn(*mut ()) = unsafe { mem::transmute(alarm.callback.get()) };
214 f(alarm.ctx.get()); 205 f(alarm.ctx.get());
215 } 206 }
207}
208
209impl Driver for RtcDriver {
210 fn now(&self) -> u64 {
211 let r = T::regs();
212
213 let period = self.period.load(Ordering::Relaxed);
214 compiler_fence(Ordering::Acquire);
215 // NOTE(unsafe) Atomic read with no side-effects
216 let counter = unsafe { r.cnt().read().cnt() };
217 calc_now(period, counter)
218 }
216 219
217 fn allocate_alarm(&self) -> Option<AlarmHandle> { 220 unsafe fn allocate_alarm(&self) -> Option<AlarmHandle> {
218 let id = self 221 let id = self
219 .alarm_count 222 .alarm_count
220 .fetch_update(Ordering::AcqRel, Ordering::Acquire, |x| { 223 .fetch_update(Ordering::AcqRel, Ordering::Acquire, |x| {
@@ -226,7 +229,7 @@ impl State {
226 }); 229 });
227 230
228 match id { 231 match id {
229 Ok(id) => Some(unsafe { AlarmHandle::new(id) }), 232 Ok(id) => Some(AlarmHandle::new(id)),
230 Err(_) => None, 233 Err(_) => None,
231 } 234 }
232 } 235 }
@@ -269,29 +272,8 @@ impl State {
269 } 272 }
270} 273}
271 274
272struct RtcDriver;
273embassy::time_driver_impl!(RtcDriver);
274
275impl Driver for RtcDriver {
276 fn now() -> u64 {
277 STATE.now()
278 }
279
280 unsafe fn allocate_alarm() -> Option<AlarmHandle> {
281 STATE.allocate_alarm()
282 }
283
284 fn set_alarm_callback(alarm: AlarmHandle, callback: fn(*mut ()), ctx: *mut ()) {
285 STATE.set_alarm_callback(alarm, callback, ctx)
286 }
287
288 fn set_alarm(alarm: AlarmHandle, timestamp: u64) {
289 STATE.set_alarm(alarm, timestamp)
290 }
291}
292
293pub(crate) fn init() { 275pub(crate) fn init() {
294 STATE.init() 276 DRIVER.init()
295} 277}
296 278
297// ------------------------------------------------------ 279// ------------------------------------------------------
diff --git a/embassy/Cargo.toml b/embassy/Cargo.toml
index ca7d38a55..0ff71ce1d 100644
--- a/embassy/Cargo.toml
+++ b/embassy/Cargo.toml
@@ -7,9 +7,17 @@ resolver = "2"
7 7
8[features] 8[features]
9default = [] 9default = []
10std = ["futures/std", "embassy-traits/std"] 10std = ["futures/std", "embassy-traits/std", "time", "time-tick-1mhz"]
11 11
12# Enable `embassy::time` module.
13# NOTE: This feature is only intended to be enabled by crates providing the time driver implementation.
14# Enabling it directly without supplying a time driver will fail to link.
12time = [] 15time = []
16
17# Set the `embassy::time` tick rate.
18# NOTE: This feature is only intended to be enabled by crates providing the time driver implementation.
19# If you're not writing your own driver, check the driver documentation to customize the tick rate.
20# If you're writing a driver and your tick rate is not listed here, please add it and send a PR!
13time-tick-32768hz = ["time"] 21time-tick-32768hz = ["time"]
14time-tick-1000hz = ["time"] 22time-tick-1000hz = ["time"]
15time-tick-1mhz = ["time"] 23time-tick-1mhz = ["time"]
diff --git a/embassy/src/executor/arch/std.rs b/embassy/src/executor/arch/std.rs
new file mode 100644
index 000000000..fb7880542
--- /dev/null
+++ b/embassy/src/executor/arch/std.rs
@@ -0,0 +1,67 @@
1use std::marker::PhantomData;
2use std::sync::{Condvar, Mutex};
3
4use super::{raw, Spawner};
5
6pub struct Executor {
7 inner: raw::Executor,
8 not_send: PhantomData<*mut ()>,
9 signaler: &'static Signaler,
10}
11
12impl Executor {
13 pub fn new() -> Self {
14 let signaler = &*Box::leak(Box::new(Signaler::new()));
15 Self {
16 inner: raw::Executor::new(
17 |p| unsafe {
18 let s = &*(p as *const () as *const Signaler);
19 s.signal()
20 },
21 signaler as *const _ as _,
22 ),
23 not_send: PhantomData,
24 signaler,
25 }
26 }
27
28 /// Runs the executor.
29 ///
30 /// This function never returns.
31 pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! {
32 init(unsafe { self.inner.spawner() });
33
34 loop {
35 unsafe { self.inner.run_queued() };
36 self.signaler.wait()
37 }
38 }
39}
40
41struct Signaler {
42 mutex: Mutex<bool>,
43 condvar: Condvar,
44}
45
46impl Signaler {
47 fn new() -> Self {
48 Self {
49 mutex: Mutex::new(false),
50 condvar: Condvar::new(),
51 }
52 }
53
54 fn wait(&self) {
55 let mut signaled = self.mutex.lock().unwrap();
56 while !*signaled {
57 signaled = self.condvar.wait(signaled).unwrap();
58 }
59 *signaled = false;
60 }
61
62 fn signal(&self) {
63 let mut signaled = self.mutex.lock().unwrap();
64 *signaled = true;
65 self.condvar.notify_one();
66 }
67}
diff --git a/embassy/src/executor/mod.rs b/embassy/src/executor/mod.rs
index e0ac566f1..5ffbe689e 100644
--- a/embassy/src/executor/mod.rs
+++ b/embassy/src/executor/mod.rs
@@ -1,4 +1,5 @@
1#[path = "arch/arm.rs"] 1#[cfg_attr(feature = "std", path = "arch/std.rs")]
2#[cfg_attr(not(feature = "std"), path = "arch/arm.rs")]
2mod arch; 3mod arch;
3pub mod raw; 4pub mod raw;
4mod spawner; 5mod spawner;
diff --git a/embassy/src/io/mod.rs b/embassy/src/io/mod.rs
index 8445f6e80..52b050971 100644
--- a/embassy/src/io/mod.rs
+++ b/embassy/src/io/mod.rs
@@ -1,7 +1,11 @@
1mod error; 1mod error;
2#[cfg(feature = "std")]
3mod std;
2mod traits; 4mod traits;
3mod util; 5mod util;
4 6
5pub use self::error::*; 7pub use self::error::*;
8#[cfg(feature = "std")]
9pub use self::std::*;
6pub use self::traits::*; 10pub use self::traits::*;
7pub use self::util::*; 11pub use self::util::*;
diff --git a/embassy/src/io/std.rs b/embassy/src/io/std.rs
new file mode 100644
index 000000000..ddec8d56d
--- /dev/null
+++ b/embassy/src/io/std.rs
@@ -0,0 +1,35 @@
1use core::pin::Pin;
2use core::task::{Context, Poll};
3use futures::io as std_io;
4
5use super::{AsyncBufRead, AsyncWrite, Result};
6
7pub struct FromStdIo<T>(T);
8
9impl<T> FromStdIo<T> {
10 pub fn new(inner: T) -> Self {
11 Self(inner)
12 }
13}
14
15impl<T: std_io::AsyncBufRead> AsyncBufRead for FromStdIo<T> {
16 fn poll_fill_buf(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<&[u8]>> {
17 let Self(inner) = unsafe { self.get_unchecked_mut() };
18 unsafe { Pin::new_unchecked(inner) }
19 .poll_fill_buf(cx)
20 .map_err(|e| e.into())
21 }
22 fn consume(self: Pin<&mut Self>, amt: usize) {
23 let Self(inner) = unsafe { self.get_unchecked_mut() };
24 unsafe { Pin::new_unchecked(inner) }.consume(amt)
25 }
26}
27
28impl<T: std_io::AsyncWrite> AsyncWrite for FromStdIo<T> {
29 fn poll_write(self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &[u8]) -> Poll<Result<usize>> {
30 let Self(inner) = unsafe { self.get_unchecked_mut() };
31 unsafe { Pin::new_unchecked(inner) }
32 .poll_write(cx, buf)
33 .map_err(|e| e.into())
34 }
35}
diff --git a/embassy/src/io/traits.rs b/embassy/src/io/traits.rs
index b59cdc0d4..8e4a981da 100644
--- a/embassy/src/io/traits.rs
+++ b/embassy/src/io/traits.rs
@@ -5,9 +5,6 @@ use core::task::{Context, Poll};
5#[cfg(feature = "alloc")] 5#[cfg(feature = "alloc")]
6use alloc::boxed::Box; 6use alloc::boxed::Box;
7 7
8#[cfg(feature = "std")]
9use futures::io as std_io;
10
11use super::error::Result; 8use super::error::Result;
12 9
13/// Read bytes asynchronously. 10/// Read bytes asynchronously.
@@ -159,37 +156,3 @@ where
159 self.get_mut().as_mut().poll_write(cx, buf) 156 self.get_mut().as_mut().poll_write(cx, buf)
160 } 157 }
161} 158}
162
163#[cfg(feature = "std")]
164pub struct FromStdIo<T>(T);
165
166#[cfg(feature = "std")]
167impl<T> FromStdIo<T> {
168 pub fn new(inner: T) -> Self {
169 Self(inner)
170 }
171}
172
173#[cfg(feature = "std")]
174impl<T: std_io::AsyncBufRead> AsyncBufRead for FromStdIo<T> {
175 fn poll_fill_buf(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<&[u8]>> {
176 let Self(inner) = unsafe { self.get_unchecked_mut() };
177 unsafe { Pin::new_unchecked(inner) }
178 .poll_fill_buf(cx)
179 .map_err(|e| e.into())
180 }
181 fn consume(self: Pin<&mut Self>, amt: usize) {
182 let Self(inner) = unsafe { self.get_unchecked_mut() };
183 unsafe { Pin::new_unchecked(inner) }.consume(amt)
184 }
185}
186
187#[cfg(feature = "std")]
188impl<T: std_io::AsyncWrite> AsyncWrite for FromStdIo<T> {
189 fn poll_write(self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &[u8]) -> Poll<Result<usize>> {
190 let Self(inner) = unsafe { self.get_unchecked_mut() };
191 unsafe { Pin::new_unchecked(inner) }
192 .poll_write(cx, buf)
193 .map_err(|e| e.into())
194 }
195}
diff --git a/embassy/src/time/delay.rs b/embassy/src/time/delay.rs
index c97e8b5c2..a46ee3a4f 100644
--- a/embassy/src/time/delay.rs
+++ b/embassy/src/time/delay.rs
@@ -4,11 +4,10 @@ use super::{Duration, Instant, Timer};
4 4
5/// Type implementing async delays and blocking `embedded-hal` delays. 5/// Type implementing async delays and blocking `embedded-hal` delays.
6/// 6///
7/// For this interface to work, the Executor's clock must be correctly initialized before using it.
8/// The delays are implemented in a "best-effort" way, meaning that the cpu will block for at least 7/// The delays are implemented in a "best-effort" way, meaning that the cpu will block for at least
9/// the amount provided, but accuracy can be affected by many factors, including interrupt usage. 8/// the amount provided, but accuracy can be affected by many factors, including interrupt usage.
10/// Make sure to use a suitable tick rate for your use case. The tick rate can be chosen through 9/// Make sure to use a suitable tick rate for your use case. The tick rate is defined by the currently
11/// features flags of this crate. 10/// active driver.
12pub struct Delay; 11pub struct Delay;
13 12
14impl crate::traits::delay::Delay for Delay { 13impl crate::traits::delay::Delay for Delay {
@@ -58,9 +57,7 @@ impl embedded_hal::blocking::delay::DelayUs<u32> for Delay {
58 } 57 }
59} 58}
60 59
61/// Blocks the cpu for at least `duration`. 60/// Blocks for at least `duration`.
62///
63/// For this interface to work, the Executor's clock must be correctly initialized before using it.
64pub fn block_for(duration: Duration) { 61pub fn block_for(duration: Duration) {
65 let expires_at = Instant::now() + duration; 62 let expires_at = Instant::now() + duration;
66 while Instant::now() < expires_at {} 63 while Instant::now() < expires_at {}
diff --git a/embassy/src/time/driver.rs b/embassy/src/time/driver.rs
index b09cffd8c..1b8949ae1 100644
--- a/embassy/src/time/driver.rs
+++ b/embassy/src/time/driver.rs
@@ -1,3 +1,60 @@
1//! Time driver interface
2//!
3//! This module defines the interface a driver needs to implement to power the `embassy::time` module.
4//!
5//! # Implementing a driver
6//!
7//! - Define a struct `MyDriver`
8//! - Implement [`Driver`] for it
9//! - Register it as the global driver with [`time_driver_impl`].
10//! - Enable the Cargo features `embassy/time` and one of `embassy/time-tick-*` corresponding to the
11//! tick rate of your driver.
12//!
13//! If you wish to make the tick rate configurable by the end user, you should do so by exposing your own
14//! Cargo features and having each enable the corresponding `embassy/time-tick-*`.
15//!
16//! # Linkage details
17//!
18//! Instead of the usual "trait + generic params" approach, calls from embassy to the driver are done via `extern` functions.
19//!
20//! `embassy` internally defines the driver functions as `extern "Rust" { fn _embassy_time_now() -> u64; }` and calls them.
21//! The driver crate defines the functions as `#[no_mangle] fn _embassy_time_now() -> u64`. The linker will resolve the
22//! calls from the `embassy` crate to call into the driver crate.
23//!
24//! If there is none or multiple drivers in the crate tree, linking will fail.
25//!
26//! This method has a few key advantages for something as foundational as timekeeping:
27//!
28//! - The time driver is available everywhere easily, without having to thread the implementation
29//! through generic parameters. This is especially helpful for libraries.
30//! - It means comparing `Instant`s will always make sense: if there were multiple drivers
31//! active, one could compare an `Instant` from driver A to an `Instant` from driver B, which
32//! would yield incorrect results.
33//!
34//! # Example
35//!
36//! ```
37//! use embassy::time::driver::{Driver, AlarmHandle};
38//!
39//! struct MyDriver{}; // not public!
40//! embassy::time_driver_impl!(static DRIVER: MyDriver = MyDriver{});
41//!
42//! impl Driver for MyDriver {
43//! fn now(&self) -> u64 {
44//! todo!()
45//! }
46//! unsafe fn allocate_alarm(&self) -> Option<AlarmHandle> {
47//! todo!()
48//! }
49//! fn set_alarm_callback(&self, alarm: AlarmHandle, callback: fn(*mut ()), ctx: *mut ()) {
50//! todo!()
51//! }
52//! fn set_alarm(&self, alarm: AlarmHandle, timestamp: u64) {
53//! todo!()
54//! }
55//! }
56//! ```
57
1/// Alarm handle, assigned by the driver. 58/// Alarm handle, assigned by the driver.
2#[derive(Clone, Copy)] 59#[derive(Clone, Copy)]
3pub struct AlarmHandle { 60pub struct AlarmHandle {
@@ -21,22 +78,22 @@ impl AlarmHandle {
21} 78}
22 79
23/// Time driver 80/// Time driver
24pub trait Driver { 81pub trait Driver: Send + Sync + 'static {
25 /// Return the current timestamp in ticks. 82 /// Return the current timestamp in ticks.
26 /// This is guaranteed to be monotonic, i.e. a call to now() will always return 83 /// This is guaranteed to be monotonic, i.e. a call to now() will always return
27 /// a greater or equal value than earler calls. 84 /// a greater or equal value than earler calls.
28 fn now() -> u64; 85 fn now(&self) -> u64;
29 86
30 /// Try allocating an alarm handle. Returns None if no alarms left. 87 /// Try allocating an alarm handle. Returns None if no alarms left.
31 /// Initially the alarm has no callback set, and a null `ctx` pointer. 88 /// Initially the alarm has no callback set, and a null `ctx` pointer.
32 /// 89 ///
33 /// # Safety 90 /// # Safety
34 /// It is UB to make the alarm fire before setting a callback. 91 /// It is UB to make the alarm fire before setting a callback.
35 unsafe fn allocate_alarm() -> Option<AlarmHandle>; 92 unsafe fn allocate_alarm(&self) -> Option<AlarmHandle>;
36 93
37 /// Sets the callback function to be called when the alarm triggers. 94 /// Sets the callback function to be called when the alarm triggers.
38 /// The callback may be called from any context (interrupt or thread mode). 95 /// The callback may be called from any context (interrupt or thread mode).
39 fn set_alarm_callback(alarm: AlarmHandle, callback: fn(*mut ()), ctx: *mut ()); 96 fn set_alarm_callback(&self, alarm: AlarmHandle, callback: fn(*mut ()), ctx: *mut ());
40 97
41 /// Sets an alarm at the given timestamp. When the current timestamp reaches that 98 /// Sets an alarm at the given timestamp. When the current timestamp reaches that
42 /// timestamp, the provided callback funcion will be called. 99 /// timestamp, the provided callback funcion will be called.
@@ -44,7 +101,7 @@ pub trait Driver {
44 /// When callback is called, it is guaranteed that now() will return a value greater or equal than timestamp. 101 /// When callback is called, it is guaranteed that now() will return a value greater or equal than timestamp.
45 /// 102 ///
46 /// Only one alarm can be active at a time. This overwrites any previously-set alarm if any. 103 /// Only one alarm can be active at a time. This overwrites any previously-set alarm if any.
47 fn set_alarm(alarm: AlarmHandle, timestamp: u64); 104 fn set_alarm(&self, alarm: AlarmHandle, timestamp: u64);
48} 105}
49 106
50extern "Rust" { 107extern "Rust" {
@@ -70,49 +127,34 @@ pub(crate) fn set_alarm(alarm: AlarmHandle, timestamp: u64) {
70 127
71/// Set the time Driver implementation. 128/// Set the time Driver implementation.
72/// 129///
73/// # Example 130/// See the module documentation for an example.
74///
75/// ```
76/// struct MyDriver;
77/// embassy::time_driver_impl!(MyDriver);
78///
79/// unsafe impl embassy::time::driver::Driver for MyDriver {
80/// fn now() -> u64 {
81/// todo!()
82/// }
83/// unsafe fn allocate_alarm() -> Option<AlarmHandle> {
84/// todo!()
85/// }
86/// fn set_alarm_callback(alarm: AlarmHandle, callback: fn(*mut ()), ctx: *mut ()) {
87/// todo!()
88/// }
89/// fn set_alarm(alarm: AlarmHandle, timestamp: u64) {
90/// todo!()
91/// }
92/// }
93///
94#[macro_export] 131#[macro_export]
95macro_rules! time_driver_impl { 132macro_rules! time_driver_impl {
96 ($t: ty) => { 133 (static $name:ident: $t: ty = $val:expr) => {
134 static $name: $t = $val;
135
97 #[no_mangle] 136 #[no_mangle]
98 fn _embassy_time_now() -> u64 { 137 fn _embassy_time_now() -> u64 {
99 <$t as $crate::time::driver::Driver>::now() 138 <$t as $crate::time::driver::Driver>::now(&$name)
100 } 139 }
140
101 #[no_mangle] 141 #[no_mangle]
102 unsafe fn _embassy_time_allocate_alarm() -> Option<AlarmHandle> { 142 unsafe fn _embassy_time_allocate_alarm() -> Option<$crate::time::driver::AlarmHandle> {
103 <$t as $crate::time::driver::Driver>::allocate_alarm() 143 <$t as $crate::time::driver::Driver>::allocate_alarm(&$name)
104 } 144 }
145
105 #[no_mangle] 146 #[no_mangle]
106 fn _embassy_time_set_alarm_callback( 147 fn _embassy_time_set_alarm_callback(
107 alarm: AlarmHandle, 148 alarm: $crate::time::driver::AlarmHandle,
108 callback: fn(*mut ()), 149 callback: fn(*mut ()),
109 ctx: *mut (), 150 ctx: *mut (),
110 ) { 151 ) {
111 <$t as $crate::time::driver::Driver>::set_alarm_callback(alarm, callback, ctx) 152 <$t as $crate::time::driver::Driver>::set_alarm_callback(&$name, alarm, callback, ctx)
112 } 153 }
154
113 #[no_mangle] 155 #[no_mangle]
114 fn _embassy_time_set_alarm(alarm: AlarmHandle, timestamp: u64) { 156 fn _embassy_time_set_alarm(alarm: $crate::time::driver::AlarmHandle, timestamp: u64) {
115 <$t as $crate::time::driver::Driver>::set_alarm(alarm, timestamp) 157 <$t as $crate::time::driver::Driver>::set_alarm(&$name, alarm, timestamp)
116 } 158 }
117 }; 159 };
118} 160}
diff --git a/embassy/src/time/driver_std.rs b/embassy/src/time/driver_std.rs
new file mode 100644
index 000000000..29911c4d2
--- /dev/null
+++ b/embassy/src/time/driver_std.rs
@@ -0,0 +1,208 @@
1use atomic_polyfill::{AtomicU8, Ordering};
2use std::cell::UnsafeCell;
3use std::mem;
4use std::mem::MaybeUninit;
5use std::sync::{Condvar, Mutex, Once};
6use std::time::Duration as StdDuration;
7use std::time::Instant as StdInstant;
8use std::{ptr, thread};
9
10use crate::time::driver::{AlarmHandle, Driver};
11
12const ALARM_COUNT: usize = 4;
13
14struct AlarmState {
15 timestamp: u64,
16
17 // This is really a Option<(fn(*mut ()), *mut ())>
18 // but fn pointers aren't allowed in const yet
19 callback: *const (),
20 ctx: *mut (),
21}
22
23unsafe impl Send for AlarmState {}
24
25impl AlarmState {
26 const fn new() -> Self {
27 Self {
28 timestamp: u64::MAX,
29 callback: ptr::null(),
30 ctx: ptr::null_mut(),
31 }
32 }
33}
34
35struct TimeDriver {
36 alarm_count: AtomicU8,
37
38 once: Once,
39 alarms: UninitCell<Mutex<[AlarmState; ALARM_COUNT]>>,
40 zero_instant: UninitCell<StdInstant>,
41 signaler: UninitCell<Signaler>,
42}
43
44const ALARM_NEW: AlarmState = AlarmState::new();
45crate::time_driver_impl!(static DRIVER: TimeDriver = TimeDriver {
46 alarm_count: AtomicU8::new(0),
47
48 once: Once::new(),
49 alarms: UninitCell::uninit(),
50 zero_instant: UninitCell::uninit(),
51 signaler: UninitCell::uninit(),
52});
53
54impl TimeDriver {
55 fn init(&self) {
56 self.once.call_once(|| unsafe {
57 self.alarms.write(Mutex::new([ALARM_NEW; ALARM_COUNT]));
58 self.zero_instant.write(StdInstant::now());
59 self.signaler.write(Signaler::new());
60
61 thread::spawn(Self::alarm_thread);
62 });
63 }
64
65 fn alarm_thread() {
66 loop {
67 let now = DRIVER.now();
68
69 let mut next_alarm = u64::MAX;
70 {
71 let alarms = &mut *unsafe { DRIVER.alarms.as_ref() }.lock().unwrap();
72 for alarm in alarms {
73 if alarm.timestamp <= now {
74 alarm.timestamp = u64::MAX;
75
76 // Call after clearing alarm, so the callback can set another alarm.
77
78 // safety:
79 // - we can ignore the possiblity of `f` being unset (null) because of the safety contract of `allocate_alarm`.
80 // - other than that we only store valid function pointers into alarm.callback
81 let f: fn(*mut ()) = unsafe { mem::transmute(alarm.callback) };
82 f(alarm.ctx);
83 } else {
84 next_alarm = next_alarm.min(alarm.timestamp);
85 }
86 }
87 }
88
89 let until =
90 unsafe { DRIVER.zero_instant.read() } + StdDuration::from_micros(next_alarm);
91
92 unsafe { DRIVER.signaler.as_ref() }.wait_until(until);
93 }
94 }
95}
96
97impl Driver for TimeDriver {
98 fn now(&self) -> u64 {
99 self.init();
100
101 let zero = unsafe { self.zero_instant.read() };
102 StdInstant::now().duration_since(zero).as_micros() as u64
103 }
104
105 unsafe fn allocate_alarm(&self) -> Option<AlarmHandle> {
106 let id = self
107 .alarm_count
108 .fetch_update(Ordering::AcqRel, Ordering::Acquire, |x| {
109 if x < ALARM_COUNT as u8 {
110 Some(x + 1)
111 } else {
112 None
113 }
114 });
115
116 match id {
117 Ok(id) => Some(AlarmHandle::new(id)),
118 Err(_) => None,
119 }
120 }
121
122 fn set_alarm_callback(&self, alarm: AlarmHandle, callback: fn(*mut ()), ctx: *mut ()) {
123 self.init();
124 let mut alarms = unsafe { self.alarms.as_ref() }.lock().unwrap();
125 let alarm = &mut alarms[alarm.id() as usize];
126 alarm.callback = callback as *const ();
127 alarm.ctx = ctx;
128 }
129
130 fn set_alarm(&self, alarm: AlarmHandle, timestamp: u64) {
131 self.init();
132 let mut alarms = unsafe { self.alarms.as_ref() }.lock().unwrap();
133 let alarm = &mut alarms[alarm.id() as usize];
134 alarm.timestamp = timestamp;
135 unsafe { self.signaler.as_ref() }.signal();
136 }
137}
138
139struct Signaler {
140 mutex: Mutex<bool>,
141 condvar: Condvar,
142}
143
144impl Signaler {
145 fn new() -> Self {
146 Self {
147 mutex: Mutex::new(false),
148 condvar: Condvar::new(),
149 }
150 }
151
152 fn wait_until(&self, until: StdInstant) {
153 let mut signaled = self.mutex.lock().unwrap();
154 while !*signaled {
155 let now = StdInstant::now();
156
157 if now >= until {
158 break;
159 }
160
161 let dur = until - now;
162 let (signaled2, timeout) = self.condvar.wait_timeout(signaled, dur).unwrap();
163 signaled = signaled2;
164 if timeout.timed_out() {
165 break;
166 }
167 }
168 *signaled = false;
169 }
170
171 fn signal(&self) {
172 let mut signaled = self.mutex.lock().unwrap();
173 *signaled = true;
174 self.condvar.notify_one();
175 }
176}
177
178pub(crate) struct UninitCell<T>(MaybeUninit<UnsafeCell<T>>);
179unsafe impl<T> Send for UninitCell<T> {}
180unsafe impl<T> Sync for UninitCell<T> {}
181
182impl<T> UninitCell<T> {
183 pub const fn uninit() -> Self {
184 Self(MaybeUninit::uninit())
185 }
186
187 pub unsafe fn as_ptr(&self) -> *const T {
188 (*self.0.as_ptr()).get()
189 }
190
191 pub unsafe fn as_mut_ptr(&self) -> *mut T {
192 (*self.0.as_ptr()).get()
193 }
194
195 pub unsafe fn as_ref(&self) -> &T {
196 &*self.as_ptr()
197 }
198
199 pub unsafe fn write(&self, val: T) {
200 ptr::write(self.as_mut_ptr(), val)
201 }
202}
203
204impl<T: Copy> UninitCell<T> {
205 pub unsafe fn read(&self) -> T {
206 ptr::read(self.as_mut_ptr())
207 }
208}
diff --git a/embassy/src/time/duration.rs b/embassy/src/time/duration.rs
index 5157450ad..e196a00c7 100644
--- a/embassy/src/time/duration.rs
+++ b/embassy/src/time/duration.rs
@@ -11,18 +11,27 @@ pub struct Duration {
11} 11}
12 12
13impl Duration { 13impl Duration {
14 /// The smallest value that can be represented by the `Duration` type.
15 pub const MIN: Duration = Duration { ticks: u64::MIN };
16 /// The largest value that can be represented by the `Duration` type.
17 pub const MAX: Duration = Duration { ticks: u64::MAX };
18
19 /// Tick count of the `Duration`.
14 pub const fn as_ticks(&self) -> u64 { 20 pub const fn as_ticks(&self) -> u64 {
15 self.ticks 21 self.ticks
16 } 22 }
17 23
24 /// Convert the `Duration` to seconds, rounding down.
18 pub const fn as_secs(&self) -> u64 { 25 pub const fn as_secs(&self) -> u64 {
19 self.ticks / TICKS_PER_SECOND 26 self.ticks / TICKS_PER_SECOND
20 } 27 }
21 28
29 /// Convert the `Duration` to milliseconds, rounding down.
22 pub const fn as_millis(&self) -> u64 { 30 pub const fn as_millis(&self) -> u64 {
23 self.ticks * 1000 / TICKS_PER_SECOND 31 self.ticks * 1000 / TICKS_PER_SECOND
24 } 32 }
25 33
34 /// Convert the `Duration` to microseconds, rounding down.
26 pub const fn as_micros(&self) -> u64 { 35 pub const fn as_micros(&self) -> u64 {
27 self.ticks * 1_000_000 / TICKS_PER_SECOND 36 self.ticks * 1_000_000 / TICKS_PER_SECOND
28 } 37 }
diff --git a/embassy/src/time/instant.rs b/embassy/src/time/instant.rs
index 3d430d886..36c2b2dc2 100644
--- a/embassy/src/time/instant.rs
+++ b/embassy/src/time/instant.rs
@@ -11,7 +11,9 @@ pub struct Instant {
11} 11}
12 12
13impl Instant { 13impl Instant {
14 /// The smallest (earliest) value that can be represented by the `Instant` type.
14 pub const MIN: Instant = Instant { ticks: u64::MIN }; 15 pub const MIN: Instant = Instant { ticks: u64::MIN };
16 /// The largest (latest) value that can be represented by the `Instant` type.
15 pub const MAX: Instant = Instant { ticks: u64::MAX }; 17 pub const MAX: Instant = Instant { ticks: u64::MAX };
16 18
17 /// Returns an Instant representing the current time. 19 /// Returns an Instant representing the current time.
@@ -21,39 +23,38 @@ impl Instant {
21 } 23 }
22 } 24 }
23 25
24 /// Instant as clock ticks since MCU start. 26 /// Create an Instant from a tick count since system boot.
25 pub const fn from_ticks(ticks: u64) -> Self { 27 pub const fn from_ticks(ticks: u64) -> Self {
26 Self { ticks } 28 Self { ticks }
27 } 29 }
28 30
29 /// Instant as milliseconds since MCU start. 31 /// Create an Instant from a millisecond count since system boot.
30 pub const fn from_millis(millis: u64) -> Self { 32 pub const fn from_millis(millis: u64) -> Self {
31 Self { 33 Self {
32 ticks: millis * TICKS_PER_SECOND as u64 / 1000, 34 ticks: millis * TICKS_PER_SECOND / 1000,
33 } 35 }
34 } 36 }
35 37
36 /// Instant representing seconds since MCU start. 38 /// Create an Instant from a second count since system boot.
37 pub const fn from_secs(seconds: u64) -> Self { 39 pub const fn from_secs(seconds: u64) -> Self {
38 Self { 40 Self {
39 ticks: seconds * TICKS_PER_SECOND as u64, 41 ticks: seconds * TICKS_PER_SECOND,
40 } 42 }
41 } 43 }
42 44
43 /// Instant as ticks since MCU start. 45 /// Tick count since system boot.
44
45 pub const fn as_ticks(&self) -> u64 { 46 pub const fn as_ticks(&self) -> u64 {
46 self.ticks 47 self.ticks
47 } 48 }
48 /// Instant as seconds since MCU start.
49 49
50 /// Seconds since system boot.
50 pub const fn as_secs(&self) -> u64 { 51 pub const fn as_secs(&self) -> u64 {
51 self.ticks / TICKS_PER_SECOND as u64 52 self.ticks / TICKS_PER_SECOND
52 } 53 }
53 /// Instant as miliseconds since MCU start.
54 54
55 /// Milliseconds since system boot.
55 pub const fn as_millis(&self) -> u64 { 56 pub const fn as_millis(&self) -> u64 {
56 self.ticks * 1000 / TICKS_PER_SECOND as u64 57 self.ticks * 1000 / TICKS_PER_SECOND
57 } 58 }
58 59
59 /// Duration between this Instant and another Instant 60 /// Duration between this Instant and another Instant
@@ -92,11 +93,14 @@ impl Instant {
92 Instant::now() - *self 93 Instant::now() - *self
93 } 94 }
94 95
96 /// Adds one Duration to self, returning a new `Instant` or None in the event of an overflow.
95 pub fn checked_add(&self, duration: Duration) -> Option<Instant> { 97 pub fn checked_add(&self, duration: Duration) -> Option<Instant> {
96 self.ticks 98 self.ticks
97 .checked_add(duration.ticks) 99 .checked_add(duration.ticks)
98 .map(|ticks| Instant { ticks }) 100 .map(|ticks| Instant { ticks })
99 } 101 }
102
103 /// Subtracts one Duration to self, returning a new `Instant` or None in the event of an overflow.
100 pub fn checked_sub(&self, duration: Duration) -> Option<Instant> { 104 pub fn checked_sub(&self, duration: Duration) -> Option<Instant> {
101 self.ticks 105 self.ticks
102 .checked_sub(duration.ticks) 106 .checked_sub(duration.ticks)
diff --git a/embassy/src/time/mod.rs b/embassy/src/time/mod.rs
index f5ad1b3ff..e50742040 100644
--- a/embassy/src/time/mod.rs
+++ b/embassy/src/time/mod.rs
@@ -1,4 +1,44 @@
1//! Time abstractions 1//! Timekeeping, delays and timeouts.
2//!
3//! Timekeeping is done with elapsed time since system boot. Time is represented in
4//! ticks, where the tick rate is defined by the current driver, usually to match
5//! the tick rate of the hardware.
6//!
7//! Tick counts are 64 bits. At the highest supported tick rate of 1Mhz this supports
8//! representing time spans of up to ~584558 years, which is big enough for all practical
9//! purposes and allows not having to worry about overflows.
10//!
11//! [`Instant`] represents a given instant of time (relative to system boot), and [`Duration`]
12//! represents the duration of a span of time. They implement the math operations you'd expect,
13//! like addition and substraction.
14//!
15//! # Delays and timeouts
16//!
17//! [`Timer`] allows performing async delays. [`Ticker`] allows periodic delays without drifting over time.
18//!
19//! An implementation of the `embedded-hal` delay traits is provided by [`Delay`], for compatibility
20//! with libraries from the ecosystem.
21//!
22//! # Wall-clock time
23//!
24//! The `time` module deals exclusively with a monotonically increasing tick count.
25//! Therefore it has no direct support for wall-clock time ("real life" datetimes
26//! like `2021-08-24 13:33:21`).
27//!
28//! If persistence across reboots is not needed, support can be built on top of
29//! `embassy::time` by storing the offset between "seconds elapsed since boot"
30//! and "seconds since unix epoch".
31//!
32//! # Time driver
33//!
34//! The `time` module is backed by a global "time driver" specified at build time.
35//! Only one driver can be active in a program.
36//!
37//! All methods and structs transparently call into the active driver. This makes it
38//! possible for libraries to use `embassy::time` in a driver-agnostic way without
39//! requiring generic parameters.
40//!
41//! For more details, check the [`driver`] module.
2 42
3mod delay; 43mod delay;
4pub mod driver; 44pub mod driver;
@@ -6,16 +46,27 @@ mod duration;
6mod instant; 46mod instant;
7mod timer; 47mod timer;
8 48
49#[cfg(feature = "std")]
50mod driver_std;
51
9pub use delay::{block_for, Delay}; 52pub use delay::{block_for, Delay};
10pub use duration::Duration; 53pub use duration::Duration;
11pub use instant::Instant; 54pub use instant::Instant;
12pub use timer::{with_timeout, Ticker, TimeoutError, Timer}; 55pub use timer::{with_timeout, Ticker, TimeoutError, Timer};
13 56
14#[cfg(feature = "time-tick-1000hz")] 57#[cfg(feature = "time-tick-1000hz")]
15pub const TICKS_PER_SECOND: u64 = 1_000; 58const TPS: u64 = 1_000;
16 59
17#[cfg(feature = "time-tick-32768hz")] 60#[cfg(feature = "time-tick-32768hz")]
18pub const TICKS_PER_SECOND: u64 = 32_768; 61const TPS: u64 = 32_768;
19 62
20#[cfg(feature = "time-tick-1mhz")] 63#[cfg(feature = "time-tick-1mhz")]
21pub const TICKS_PER_SECOND: u64 = 1_000_000; 64const TPS: u64 = 1_000_000;
65
66/// Ticks per second of the global timebase.
67///
68/// This value is specified by the `time-tick-*` Cargo features, which
69/// should be set by the time driver. Some drivers support a fixed tick rate, others
70/// allow you to choose a tick rate with Cargo features of their own. You should not
71/// set the `time-tick-*` features for embassy yourself as an end user.
72pub const TICKS_PER_SECOND: u64 = TPS;
diff --git a/embassy/src/time/timer.rs b/embassy/src/time/timer.rs
index 22f1ffe30..d1ed6a512 100644
--- a/embassy/src/time/timer.rs
+++ b/embassy/src/time/timer.rs
@@ -6,7 +6,13 @@ use futures::{future::select, future::Either, pin_mut, Stream};
6use crate::executor::raw; 6use crate::executor::raw;
7use crate::time::{Duration, Instant}; 7use crate::time::{Duration, Instant};
8 8
9/// Error returned by [`with_timeout`] on timeout.
9pub struct TimeoutError; 10pub struct TimeoutError;
11
12/// Runs a given future with a timeout.
13///
14/// If the future completes before the timeout, its output is returned. Otherwise, on timeout,
15/// work on the future is stopped (`poll` is no longer called), the future is dropped and `Err(TimeoutError)` is returned.
10pub async fn with_timeout<F: Future>(timeout: Duration, fut: F) -> Result<F::Output, TimeoutError> { 16pub async fn with_timeout<F: Future>(timeout: Duration, fut: F) -> Result<F::Output, TimeoutError> {
11 let timeout_fut = Timer::after(timeout); 17 let timeout_fut = Timer::after(timeout);
12 pin_mut!(fut); 18 pin_mut!(fut);
diff --git a/examples/std/Cargo.toml b/examples/std/Cargo.toml
index 466814850..34b8ebb67 100644
--- a/examples/std/Cargo.toml
+++ b/examples/std/Cargo.toml
@@ -5,8 +5,7 @@ name = "embassy-std-examples"
5version = "0.1.0" 5version = "0.1.0"
6 6
7[dependencies] 7[dependencies]
8embassy = { version = "0.1.0", path = "../../embassy", features = ["log"] } 8embassy = { version = "0.1.0", path = "../../embassy", features = ["log", "std", "time"] }
9embassy-std = { version = "0.1.0", path = "../../embassy-std" }
10embassy-net = { version = "0.1.0", path = "../../embassy-net", features=["std", "log", "medium-ethernet", "tcp", "dhcpv4"] } 9embassy-net = { version = "0.1.0", path = "../../embassy-net", features=["std", "log", "medium-ethernet", "tcp", "dhcpv4"] }
11smoltcp = { git = "https://github.com/smoltcp-rs/smoltcp", rev="e4241510337e095b9d21136c5f58b2eaa1b78479", default-features = false } 10smoltcp = { git = "https://github.com/smoltcp-rs/smoltcp", rev="e4241510337e095b9d21136c5f58b2eaa1b78479", default-features = false }
12 11
diff --git a/examples/std/src/bin/net.rs b/examples/std/src/bin/net.rs
index 323d711a8..b98b97090 100644
--- a/examples/std/src/bin/net.rs
+++ b/examples/std/src/bin/net.rs
@@ -2,11 +2,10 @@
2#![allow(incomplete_features)] 2#![allow(incomplete_features)]
3 3
4use clap::{AppSettings, Clap}; 4use clap::{AppSettings, Clap};
5use embassy::executor::Spawner; 5use embassy::executor::{Executor, Spawner};
6use embassy::io::AsyncWriteExt; 6use embassy::io::AsyncWriteExt;
7use embassy::util::Forever; 7use embassy::util::Forever;
8use embassy_net::*; 8use embassy_net::*;
9use embassy_std::Executor;
10use heapless::Vec; 9use heapless::Vec;
11use log::*; 10use log::*;
12 11
diff --git a/examples/std/src/bin/serial.rs b/examples/std/src/bin/serial.rs
index ca596a34c..181c5dfaa 100644
--- a/examples/std/src/bin/serial.rs
+++ b/examples/std/src/bin/serial.rs
@@ -5,9 +5,9 @@
5mod serial_port; 5mod serial_port;
6 6
7use async_io::Async; 7use async_io::Async;
8use embassy::executor::Executor;
8use embassy::io::AsyncBufReadExt; 9use embassy::io::AsyncBufReadExt;
9use embassy::util::Forever; 10use embassy::util::Forever;
10use embassy_std::Executor;
11use log::*; 11use log::*;
12use nix::sys::termios; 12use nix::sys::termios;
13 13
diff --git a/examples/std/src/bin/tick.rs b/examples/std/src/bin/tick.rs
index 16f54b2c6..385b317d4 100644
--- a/examples/std/src/bin/tick.rs
+++ b/examples/std/src/bin/tick.rs
@@ -1,9 +1,9 @@
1#![feature(type_alias_impl_trait)] 1#![feature(type_alias_impl_trait)]
2#![allow(incomplete_features)] 2#![allow(incomplete_features)]
3 3
4use embassy::executor::Executor;
4use embassy::time::{Duration, Timer}; 5use embassy::time::{Duration, Timer};
5use embassy::util::Forever; 6use embassy::util::Forever;
6use embassy_std::Executor;
7use log::*; 7use log::*;
8 8
9#[embassy::task] 9#[embassy::task]