diff options
| author | Dániel Buga <[email protected]> | 2024-11-26 23:54:21 +0100 |
|---|---|---|
| committer | Dániel Buga <[email protected]> | 2024-12-10 21:31:42 +0100 |
| commit | 5a5495aac43d75610735f2ca80fb6c8e8f31ed71 (patch) | |
| tree | 7a4336917894730692589359e9d1a285ec5a0a05 | |
| parent | 406d377b7564d16e12b7fae4f42c0c709bf4f243 (diff) | |
Refactor integrated-timers
32 files changed, 613 insertions, 1184 deletions
diff --git a/.github/ci/test.sh b/.github/ci/test.sh index 0fe088bfe..285f3f29e 100755 --- a/.github/ci/test.sh +++ b/.github/ci/test.sh | |||
| @@ -17,7 +17,7 @@ cargo test --manifest-path ./embassy-futures/Cargo.toml | |||
| 17 | cargo test --manifest-path ./embassy-sync/Cargo.toml | 17 | cargo test --manifest-path ./embassy-sync/Cargo.toml |
| 18 | cargo test --manifest-path ./embassy-embedded-hal/Cargo.toml | 18 | cargo test --manifest-path ./embassy-embedded-hal/Cargo.toml |
| 19 | cargo test --manifest-path ./embassy-hal-internal/Cargo.toml | 19 | cargo test --manifest-path ./embassy-hal-internal/Cargo.toml |
| 20 | cargo test --manifest-path ./embassy-time/Cargo.toml --features generic-queue,mock-driver | 20 | cargo test --manifest-path ./embassy-time/Cargo.toml --features mock-driver |
| 21 | cargo test --manifest-path ./embassy-time-driver/Cargo.toml | 21 | cargo test --manifest-path ./embassy-time-driver/Cargo.toml |
| 22 | 22 | ||
| 23 | cargo test --manifest-path ./embassy-boot/Cargo.toml | 23 | cargo test --manifest-path ./embassy-boot/Cargo.toml |
diff --git a/ci-xtensa.sh b/ci-xtensa.sh index 32d362def..2cac7444c 100755 --- a/ci-xtensa.sh +++ b/ci-xtensa.sh | |||
| @@ -24,7 +24,9 @@ cargo batch \ | |||
| 24 | --- build --release --manifest-path embassy-executor/Cargo.toml --target xtensa-esp32-none-elf --features arch-spin,executor-thread \ | 24 | --- build --release --manifest-path embassy-executor/Cargo.toml --target xtensa-esp32-none-elf --features arch-spin,executor-thread \ |
| 25 | --- build --release --manifest-path embassy-executor/Cargo.toml --target xtensa-esp32-none-elf --features arch-spin,executor-thread,integrated-timers \ | 25 | --- build --release --manifest-path embassy-executor/Cargo.toml --target xtensa-esp32-none-elf --features arch-spin,executor-thread,integrated-timers \ |
| 26 | --- build --release --manifest-path embassy-sync/Cargo.toml --target xtensa-esp32s2-none-elf --features defmt \ | 26 | --- build --release --manifest-path embassy-sync/Cargo.toml --target xtensa-esp32s2-none-elf --features defmt \ |
| 27 | --- build --release --manifest-path embassy-time/Cargo.toml --target xtensa-esp32s2-none-elf --features defmt,defmt-timestamp-uptime,generic-queue-8,mock-driver \ | 27 | --- build --release --manifest-path embassy-time/Cargo.toml --target xtensa-esp32s2-none-elf --features defmt,defmt-timestamp-uptime,mock-driver \ |
| 28 | --- build --release --manifest-path embassy-time-queue-driver/Cargo.toml --target xtensa-esp32s2-none-elf --features integrated-timers \ | ||
| 29 | --- build --release --manifest-path embassy-time-queue-driver/Cargo.toml --target xtensa-esp32s2-none-elf --features generic-queue-8 \ | ||
| 28 | --- build --release --manifest-path embassy-net/Cargo.toml --target xtensa-esp32-none-elf --features defmt,tcp,udp,dns,proto-ipv4,medium-ethernet,packet-trace \ | 30 | --- build --release --manifest-path embassy-net/Cargo.toml --target xtensa-esp32-none-elf --features defmt,tcp,udp,dns,proto-ipv4,medium-ethernet,packet-trace \ |
| 29 | --- build --release --manifest-path embassy-net/Cargo.toml --target xtensa-esp32-none-elf --features defmt,tcp,udp,dns,proto-ipv4,multicast,medium-ethernet \ | 31 | --- build --release --manifest-path embassy-net/Cargo.toml --target xtensa-esp32-none-elf --features defmt,tcp,udp,dns,proto-ipv4,multicast,medium-ethernet \ |
| 30 | --- build --release --manifest-path embassy-net/Cargo.toml --target xtensa-esp32-none-elf --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet \ | 32 | --- build --release --manifest-path embassy-net/Cargo.toml --target xtensa-esp32-none-elf --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet \ |
| @@ -45,7 +45,9 @@ cargo batch \ | |||
| 45 | --- build --release --manifest-path embassy-executor/Cargo.toml --target riscv32imac-unknown-none-elf --features arch-riscv32,executor-thread \ | 45 | --- build --release --manifest-path embassy-executor/Cargo.toml --target riscv32imac-unknown-none-elf --features arch-riscv32,executor-thread \ |
| 46 | --- build --release --manifest-path embassy-executor/Cargo.toml --target riscv32imac-unknown-none-elf --features arch-riscv32,executor-thread,integrated-timers \ | 46 | --- build --release --manifest-path embassy-executor/Cargo.toml --target riscv32imac-unknown-none-elf --features arch-riscv32,executor-thread,integrated-timers \ |
| 47 | --- build --release --manifest-path embassy-sync/Cargo.toml --target thumbv6m-none-eabi --features defmt \ | 47 | --- build --release --manifest-path embassy-sync/Cargo.toml --target thumbv6m-none-eabi --features defmt \ |
| 48 | --- build --release --manifest-path embassy-time/Cargo.toml --target thumbv6m-none-eabi --features defmt,defmt-timestamp-uptime,generic-queue-8,mock-driver \ | 48 | --- build --release --manifest-path embassy-time/Cargo.toml --target thumbv6m-none-eabi --features defmt,defmt-timestamp-uptime,mock-driver \ |
| 49 | --- build --release --manifest-path embassy-time-queue-driver/Cargo.toml --target thumbv6m-none-eabi --features integrated-timers \ | ||
| 50 | --- build --release --manifest-path embassy-time-queue-driver/Cargo.toml --target thumbv6m-none-eabi --features generic-queue-8 \ | ||
| 49 | --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv4,medium-ethernet,packet-trace \ | 51 | --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv4,medium-ethernet,packet-trace \ |
| 50 | --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv4,multicast,medium-ethernet \ | 52 | --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv4,multicast,medium-ethernet \ |
| 51 | --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet \ | 53 | --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet \ |
diff --git a/embassy-executor/Cargo.toml b/embassy-executor/Cargo.toml index 0a5360e5d..862d25b59 100644 --- a/embassy-executor/Cargo.toml +++ b/embassy-executor/Cargo.toml | |||
| @@ -68,7 +68,7 @@ nightly = ["embassy-executor-macros/nightly"] | |||
| 68 | turbowakers = [] | 68 | turbowakers = [] |
| 69 | 69 | ||
| 70 | ## Use the executor-integrated `embassy-time` timer queue. | 70 | ## Use the executor-integrated `embassy-time` timer queue. |
| 71 | integrated-timers = ["dep:embassy-time-driver", "dep:embassy-time-queue-driver"] | 71 | integrated-timers = ["dep:embassy-time-driver"] |
| 72 | 72 | ||
| 73 | #! ### Architecture | 73 | #! ### Architecture |
| 74 | _arch = [] # some arch was picked | 74 | _arch = [] # some arch was picked |
diff --git a/embassy-executor/src/arch/avr.rs b/embassy-executor/src/arch/avr.rs index 7f9ed4421..70085d04d 100644 --- a/embassy-executor/src/arch/avr.rs +++ b/embassy-executor/src/arch/avr.rs | |||
| @@ -53,10 +53,6 @@ mod thread { | |||
| 53 | /// | 53 | /// |
| 54 | /// This function never returns. | 54 | /// This function never returns. |
| 55 | pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! { | 55 | pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! { |
| 56 | unsafe { | ||
| 57 | self.inner.initialize(); | ||
| 58 | } | ||
| 59 | |||
| 60 | init(self.inner.spawner()); | 56 | init(self.inner.spawner()); |
| 61 | 57 | ||
| 62 | loop { | 58 | loop { |
diff --git a/embassy-executor/src/arch/cortex_m.rs b/embassy-executor/src/arch/cortex_m.rs index 0c2af88a6..5c517e0a2 100644 --- a/embassy-executor/src/arch/cortex_m.rs +++ b/embassy-executor/src/arch/cortex_m.rs | |||
| @@ -98,9 +98,6 @@ mod thread { | |||
| 98 | /// | 98 | /// |
| 99 | /// This function never returns. | 99 | /// This function never returns. |
| 100 | pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! { | 100 | pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! { |
| 101 | unsafe { | ||
| 102 | self.inner.initialize(); | ||
| 103 | } | ||
| 104 | init(self.inner.spawner()); | 101 | init(self.inner.spawner()); |
| 105 | 102 | ||
| 106 | loop { | 103 | loop { |
| @@ -210,9 +207,6 @@ mod interrupt { | |||
| 210 | } | 207 | } |
| 211 | 208 | ||
| 212 | let executor = unsafe { (&*self.executor.get()).assume_init_ref() }; | 209 | let executor = unsafe { (&*self.executor.get()).assume_init_ref() }; |
| 213 | unsafe { | ||
| 214 | executor.initialize(); | ||
| 215 | } | ||
| 216 | 210 | ||
| 217 | unsafe { NVIC::unmask(irq) } | 211 | unsafe { NVIC::unmask(irq) } |
| 218 | 212 | ||
diff --git a/embassy-executor/src/arch/riscv32.rs b/embassy-executor/src/arch/riscv32.rs index 715e5f3cf..01e63a9fd 100644 --- a/embassy-executor/src/arch/riscv32.rs +++ b/embassy-executor/src/arch/riscv32.rs | |||
| @@ -54,10 +54,6 @@ mod thread { | |||
| 54 | /// | 54 | /// |
| 55 | /// This function never returns. | 55 | /// This function never returns. |
| 56 | pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! { | 56 | pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! { |
| 57 | unsafe { | ||
| 58 | self.inner.initialize(); | ||
| 59 | } | ||
| 60 | |||
| 61 | init(self.inner.spawner()); | 57 | init(self.inner.spawner()); |
| 62 | 58 | ||
| 63 | loop { | 59 | loop { |
diff --git a/embassy-executor/src/arch/spin.rs b/embassy-executor/src/arch/spin.rs index 54c7458b3..340023620 100644 --- a/embassy-executor/src/arch/spin.rs +++ b/embassy-executor/src/arch/spin.rs | |||
| @@ -48,10 +48,6 @@ mod thread { | |||
| 48 | /// | 48 | /// |
| 49 | /// This function never returns. | 49 | /// This function never returns. |
| 50 | pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! { | 50 | pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! { |
| 51 | unsafe { | ||
| 52 | self.inner.initialize(); | ||
| 53 | } | ||
| 54 | |||
| 55 | init(self.inner.spawner()); | 51 | init(self.inner.spawner()); |
| 56 | 52 | ||
| 57 | loop { | 53 | loop { |
diff --git a/embassy-executor/src/arch/std.rs b/embassy-executor/src/arch/std.rs index 948c7711b..b02b15988 100644 --- a/embassy-executor/src/arch/std.rs +++ b/embassy-executor/src/arch/std.rs | |||
| @@ -55,10 +55,6 @@ mod thread { | |||
| 55 | /// | 55 | /// |
| 56 | /// This function never returns. | 56 | /// This function never returns. |
| 57 | pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! { | 57 | pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! { |
| 58 | unsafe { | ||
| 59 | self.inner.initialize(); | ||
| 60 | } | ||
| 61 | |||
| 62 | init(self.inner.spawner()); | 58 | init(self.inner.spawner()); |
| 63 | 59 | ||
| 64 | loop { | 60 | loop { |
diff --git a/embassy-executor/src/arch/wasm.rs b/embassy-executor/src/arch/wasm.rs index 35025f11f..f9d0f935c 100644 --- a/embassy-executor/src/arch/wasm.rs +++ b/embassy-executor/src/arch/wasm.rs | |||
| @@ -71,10 +71,6 @@ mod thread { | |||
| 71 | /// - a local variable in a function you know never returns (like `fn main() -> !`), upgrading its lifetime with `transmute`. (unsafe) | 71 | /// - a local variable in a function you know never returns (like `fn main() -> !`), upgrading its lifetime with `transmute`. (unsafe) |
| 72 | pub fn start(&'static mut self, init: impl FnOnce(Spawner)) { | 72 | pub fn start(&'static mut self, init: impl FnOnce(Spawner)) { |
| 73 | unsafe { | 73 | unsafe { |
| 74 | self.inner.initialize(); | ||
| 75 | } | ||
| 76 | |||
| 77 | unsafe { | ||
| 78 | let executor = &self.inner; | 74 | let executor = &self.inner; |
| 79 | let future = Closure::new(move |_| { | 75 | let future = Closure::new(move |_| { |
| 80 | executor.poll(); | 76 | executor.poll(); |
diff --git a/embassy-executor/src/raw/mod.rs b/embassy-executor/src/raw/mod.rs index 3f93eae6f..80bd49bad 100644 --- a/embassy-executor/src/raw/mod.rs +++ b/embassy-executor/src/raw/mod.rs | |||
| @@ -17,7 +17,7 @@ mod run_queue; | |||
| 17 | mod state; | 17 | mod state; |
| 18 | 18 | ||
| 19 | #[cfg(feature = "integrated-timers")] | 19 | #[cfg(feature = "integrated-timers")] |
| 20 | mod timer_queue; | 20 | pub mod timer_queue; |
| 21 | #[cfg(feature = "trace")] | 21 | #[cfg(feature = "trace")] |
| 22 | mod trace; | 22 | mod trace; |
| 23 | pub(crate) mod util; | 23 | pub(crate) mod util; |
| @@ -31,9 +31,6 @@ use core::pin::Pin; | |||
| 31 | use core::ptr::NonNull; | 31 | use core::ptr::NonNull; |
| 32 | use core::task::{Context, Poll}; | 32 | use core::task::{Context, Poll}; |
| 33 | 33 | ||
| 34 | #[cfg(feature = "integrated-timers")] | ||
| 35 | use embassy_time_driver::AlarmHandle; | ||
| 36 | |||
| 37 | use self::run_queue::{RunQueue, RunQueueItem}; | 34 | use self::run_queue::{RunQueue, RunQueueItem}; |
| 38 | use self::state::State; | 35 | use self::state::State; |
| 39 | use self::util::{SyncUnsafeCell, UninitCell}; | 36 | use self::util::{SyncUnsafeCell, UninitCell}; |
| @@ -47,8 +44,7 @@ pub(crate) struct TaskHeader { | |||
| 47 | pub(crate) executor: SyncUnsafeCell<Option<&'static SyncExecutor>>, | 44 | pub(crate) executor: SyncUnsafeCell<Option<&'static SyncExecutor>>, |
| 48 | poll_fn: SyncUnsafeCell<Option<unsafe fn(TaskRef)>>, | 45 | poll_fn: SyncUnsafeCell<Option<unsafe fn(TaskRef)>>, |
| 49 | 46 | ||
| 50 | #[cfg(feature = "integrated-timers")] | 47 | /// Integrated timer queue storage. This field should not be accessed outside of the timer queue. |
| 51 | pub(crate) expires_at: SyncUnsafeCell<u64>, | ||
| 52 | #[cfg(feature = "integrated-timers")] | 48 | #[cfg(feature = "integrated-timers")] |
| 53 | pub(crate) timer_queue_item: timer_queue::TimerQueueItem, | 49 | pub(crate) timer_queue_item: timer_queue::TimerQueueItem, |
| 54 | } | 50 | } |
| @@ -80,6 +76,12 @@ impl TaskRef { | |||
| 80 | unsafe { self.ptr.as_ref() } | 76 | unsafe { self.ptr.as_ref() } |
| 81 | } | 77 | } |
| 82 | 78 | ||
| 79 | /// Returns a reference to the executor that the task is currently running on. | ||
| 80 | #[cfg(feature = "integrated-timers")] | ||
| 81 | pub unsafe fn executor(self) -> Option<&'static Executor> { | ||
| 82 | self.header().executor.get().map(|e| Executor::wrap(e)) | ||
| 83 | } | ||
| 84 | |||
| 83 | /// The returned pointer is valid for the entire TaskStorage. | 85 | /// The returned pointer is valid for the entire TaskStorage. |
| 84 | pub(crate) fn as_ptr(self) -> *const TaskHeader { | 86 | pub(crate) fn as_ptr(self) -> *const TaskHeader { |
| 85 | self.ptr.as_ptr() | 87 | self.ptr.as_ptr() |
| @@ -121,8 +123,6 @@ impl<F: Future + 'static> TaskStorage<F> { | |||
| 121 | poll_fn: SyncUnsafeCell::new(None), | 123 | poll_fn: SyncUnsafeCell::new(None), |
| 122 | 124 | ||
| 123 | #[cfg(feature = "integrated-timers")] | 125 | #[cfg(feature = "integrated-timers")] |
| 124 | expires_at: SyncUnsafeCell::new(0), | ||
| 125 | #[cfg(feature = "integrated-timers")] | ||
| 126 | timer_queue_item: timer_queue::TimerQueueItem::new(), | 126 | timer_queue_item: timer_queue::TimerQueueItem::new(), |
| 127 | }, | 127 | }, |
| 128 | future: UninitCell::uninit(), | 128 | future: UninitCell::uninit(), |
| @@ -160,9 +160,6 @@ impl<F: Future + 'static> TaskStorage<F> { | |||
| 160 | Poll::Ready(_) => { | 160 | Poll::Ready(_) => { |
| 161 | this.future.drop_in_place(); | 161 | this.future.drop_in_place(); |
| 162 | this.raw.state.despawn(); | 162 | this.raw.state.despawn(); |
| 163 | |||
| 164 | #[cfg(feature = "integrated-timers")] | ||
| 165 | this.raw.expires_at.set(u64::MAX); | ||
| 166 | } | 163 | } |
| 167 | Poll::Pending => {} | 164 | Poll::Pending => {} |
| 168 | } | 165 | } |
| @@ -316,34 +313,16 @@ impl Pender { | |||
| 316 | pub(crate) struct SyncExecutor { | 313 | pub(crate) struct SyncExecutor { |
| 317 | run_queue: RunQueue, | 314 | run_queue: RunQueue, |
| 318 | pender: Pender, | 315 | pender: Pender, |
| 319 | |||
| 320 | #[cfg(feature = "integrated-timers")] | ||
| 321 | pub(crate) timer_queue: timer_queue::TimerQueue, | ||
| 322 | #[cfg(feature = "integrated-timers")] | ||
| 323 | alarm: AlarmHandle, | ||
| 324 | } | 316 | } |
| 325 | 317 | ||
| 326 | impl SyncExecutor { | 318 | impl SyncExecutor { |
| 327 | pub(crate) fn new(pender: Pender) -> Self { | 319 | pub(crate) fn new(pender: Pender) -> Self { |
| 328 | #[cfg(feature = "integrated-timers")] | ||
| 329 | let alarm = unsafe { unwrap!(embassy_time_driver::allocate_alarm()) }; | ||
| 330 | |||
| 331 | Self { | 320 | Self { |
| 332 | run_queue: RunQueue::new(), | 321 | run_queue: RunQueue::new(), |
| 333 | pender, | 322 | pender, |
| 334 | |||
| 335 | #[cfg(feature = "integrated-timers")] | ||
| 336 | timer_queue: timer_queue::TimerQueue::new(), | ||
| 337 | #[cfg(feature = "integrated-timers")] | ||
| 338 | alarm, | ||
| 339 | } | 323 | } |
| 340 | } | 324 | } |
| 341 | 325 | ||
| 342 | pub(crate) unsafe fn initialize(&'static self) { | ||
| 343 | #[cfg(feature = "integrated-timers")] | ||
| 344 | embassy_time_driver::set_alarm_callback(self.alarm, Self::alarm_callback, self as *const _ as *mut ()); | ||
| 345 | } | ||
| 346 | |||
| 347 | /// Enqueue a task in the task queue | 326 | /// Enqueue a task in the task queue |
| 348 | /// | 327 | /// |
| 349 | /// # Safety | 328 | /// # Safety |
| @@ -360,12 +339,6 @@ impl SyncExecutor { | |||
| 360 | } | 339 | } |
| 361 | } | 340 | } |
| 362 | 341 | ||
| 363 | #[cfg(feature = "integrated-timers")] | ||
| 364 | fn alarm_callback(ctx: *mut ()) { | ||
| 365 | let this: &Self = unsafe { &*(ctx as *const Self) }; | ||
| 366 | this.pender.pend(); | ||
| 367 | } | ||
| 368 | |||
| 369 | pub(super) unsafe fn spawn(&'static self, task: TaskRef) { | 342 | pub(super) unsafe fn spawn(&'static self, task: TaskRef) { |
| 370 | task.header().executor.set(Some(self)); | 343 | task.header().executor.set(Some(self)); |
| 371 | 344 | ||
| @@ -379,56 +352,27 @@ impl SyncExecutor { | |||
| 379 | /// | 352 | /// |
| 380 | /// Same as [`Executor::poll`], plus you must only call this on the thread this executor was created. | 353 | /// Same as [`Executor::poll`], plus you must only call this on the thread this executor was created. |
| 381 | pub(crate) unsafe fn poll(&'static self) { | 354 | pub(crate) unsafe fn poll(&'static self) { |
| 382 | #[allow(clippy::never_loop)] | 355 | self.run_queue.dequeue_all(|p| { |
| 383 | loop { | 356 | let task = p.header(); |
| 384 | #[cfg(feature = "integrated-timers")] | 357 | |
| 385 | self.timer_queue | 358 | if !task.state.run_dequeue() { |
| 386 | .dequeue_expired(embassy_time_driver::now(), wake_task_no_pend); | 359 | // If task is not running, ignore it. This can happen in the following scenario: |
| 387 | 360 | // - Task gets dequeued, poll starts | |
| 388 | self.run_queue.dequeue_all(|p| { | 361 | // - While task is being polled, it gets woken. It gets placed in the queue. |
| 389 | let task = p.header(); | 362 | // - Task poll finishes, returning done=true |
| 390 | 363 | // - RUNNING bit is cleared, but the task is already in the queue. | |
| 391 | #[cfg(feature = "integrated-timers")] | 364 | return; |
| 392 | task.expires_at.set(u64::MAX); | 365 | } |
| 393 | |||
| 394 | if !task.state.run_dequeue() { | ||
| 395 | // If task is not running, ignore it. This can happen in the following scenario: | ||
| 396 | // - Task gets dequeued, poll starts | ||
| 397 | // - While task is being polled, it gets woken. It gets placed in the queue. | ||
| 398 | // - Task poll finishes, returning done=true | ||
| 399 | // - RUNNING bit is cleared, but the task is already in the queue. | ||
| 400 | return; | ||
| 401 | } | ||
| 402 | |||
| 403 | #[cfg(feature = "trace")] | ||
| 404 | trace::task_exec_begin(self, &p); | ||
| 405 | 366 | ||
| 406 | // Run the task | 367 | #[cfg(feature = "trace")] |
| 407 | task.poll_fn.get().unwrap_unchecked()(p); | 368 | trace::task_exec_begin(self, &p); |
| 408 | 369 | ||
| 409 | #[cfg(feature = "trace")] | 370 | // Run the task |
| 410 | trace::task_exec_end(self, &p); | 371 | task.poll_fn.get().unwrap_unchecked()(p); |
| 411 | 372 | ||
| 412 | // Enqueue or update into timer_queue | 373 | #[cfg(feature = "trace")] |
| 413 | #[cfg(feature = "integrated-timers")] | 374 | trace::task_exec_end(self, &p); |
| 414 | self.timer_queue.update(p); | 375 | }); |
| 415 | }); | ||
| 416 | |||
| 417 | #[cfg(feature = "integrated-timers")] | ||
| 418 | { | ||
| 419 | // If this is already in the past, set_alarm might return false | ||
| 420 | // In that case do another poll loop iteration. | ||
| 421 | let next_expiration = self.timer_queue.next_expiration(); | ||
| 422 | if embassy_time_driver::set_alarm(self.alarm, next_expiration) { | ||
| 423 | break; | ||
| 424 | } | ||
| 425 | } | ||
| 426 | |||
| 427 | #[cfg(not(feature = "integrated-timers"))] | ||
| 428 | { | ||
| 429 | break; | ||
| 430 | } | ||
| 431 | } | ||
| 432 | 376 | ||
| 433 | #[cfg(feature = "trace")] | 377 | #[cfg(feature = "trace")] |
| 434 | trace::executor_idle(self) | 378 | trace::executor_idle(self) |
| @@ -494,15 +438,6 @@ impl Executor { | |||
| 494 | } | 438 | } |
| 495 | } | 439 | } |
| 496 | 440 | ||
| 497 | /// Initializes the executor. | ||
| 498 | /// | ||
| 499 | /// # Safety | ||
| 500 | /// | ||
| 501 | /// This function must be called once before any other method is called. | ||
| 502 | pub unsafe fn initialize(&'static self) { | ||
| 503 | self.inner.initialize(); | ||
| 504 | } | ||
| 505 | |||
| 506 | /// Spawn a task in this executor. | 441 | /// Spawn a task in this executor. |
| 507 | /// | 442 | /// |
| 508 | /// # Safety | 443 | /// # Safety |
| @@ -575,21 +510,3 @@ pub fn wake_task_no_pend(task: TaskRef) { | |||
| 575 | } | 510 | } |
| 576 | } | 511 | } |
| 577 | } | 512 | } |
| 578 | |||
| 579 | #[cfg(feature = "integrated-timers")] | ||
| 580 | struct TimerQueue; | ||
| 581 | |||
| 582 | #[cfg(feature = "integrated-timers")] | ||
| 583 | impl embassy_time_queue_driver::TimerQueue for TimerQueue { | ||
| 584 | fn schedule_wake(&'static self, at: u64, waker: &core::task::Waker) { | ||
| 585 | let task = waker::task_from_waker(waker); | ||
| 586 | let task = task.header(); | ||
| 587 | unsafe { | ||
| 588 | let expires_at = task.expires_at.get(); | ||
| 589 | task.expires_at.set(expires_at.min(at)); | ||
| 590 | } | ||
| 591 | } | ||
| 592 | } | ||
| 593 | |||
| 594 | #[cfg(feature = "integrated-timers")] | ||
| 595 | embassy_time_queue_driver::timer_queue_impl!(static TIMER_QUEUE: TimerQueue = TimerQueue); | ||
diff --git a/embassy-executor/src/raw/timer_queue.rs b/embassy-executor/src/raw/timer_queue.rs index 94a5f340b..953bf014f 100644 --- a/embassy-executor/src/raw/timer_queue.rs +++ b/embassy-executor/src/raw/timer_queue.rs | |||
| @@ -1,75 +1,100 @@ | |||
| 1 | //! Timer queue operations. | ||
| 1 | use core::cmp::min; | 2 | use core::cmp::min; |
| 2 | 3 | ||
| 4 | use super::util::SyncUnsafeCell; | ||
| 3 | use super::TaskRef; | 5 | use super::TaskRef; |
| 4 | use crate::raw::util::SyncUnsafeCell; | ||
| 5 | 6 | ||
| 6 | pub(crate) struct TimerQueueItem { | 7 | pub(crate) struct TimerQueueItem { |
| 7 | next: SyncUnsafeCell<Option<TaskRef>>, | 8 | next: SyncUnsafeCell<Option<TaskRef>>, |
| 9 | expires_at: SyncUnsafeCell<u64>, | ||
| 8 | } | 10 | } |
| 9 | 11 | ||
| 10 | impl TimerQueueItem { | 12 | impl TimerQueueItem { |
| 11 | pub const fn new() -> Self { | 13 | pub const fn new() -> Self { |
| 12 | Self { | 14 | Self { |
| 13 | next: SyncUnsafeCell::new(None), | 15 | next: SyncUnsafeCell::new(None), |
| 16 | expires_at: SyncUnsafeCell::new(0), | ||
| 14 | } | 17 | } |
| 15 | } | 18 | } |
| 16 | } | 19 | } |
| 17 | 20 | ||
| 18 | pub(crate) struct TimerQueue { | 21 | /// A timer queue, with items integrated into tasks. |
| 22 | pub struct TimerQueue { | ||
| 19 | head: SyncUnsafeCell<Option<TaskRef>>, | 23 | head: SyncUnsafeCell<Option<TaskRef>>, |
| 20 | } | 24 | } |
| 21 | 25 | ||
| 22 | impl TimerQueue { | 26 | impl TimerQueue { |
| 27 | /// Creates a new timer queue. | ||
| 23 | pub const fn new() -> Self { | 28 | pub const fn new() -> Self { |
| 24 | Self { | 29 | Self { |
| 25 | head: SyncUnsafeCell::new(None), | 30 | head: SyncUnsafeCell::new(None), |
| 26 | } | 31 | } |
| 27 | } | 32 | } |
| 28 | 33 | ||
| 29 | pub(crate) unsafe fn update(&self, p: TaskRef) { | 34 | /// Schedules a task to run at a specific time. |
| 30 | let task = p.header(); | 35 | /// |
| 31 | if task.expires_at.get() != u64::MAX { | 36 | /// If this function returns `true`, the called should find the next expiration time and set |
| 37 | /// a new alarm for that time. | ||
| 38 | pub fn schedule_wake(&mut self, at: u64, p: TaskRef) -> bool { | ||
| 39 | unsafe { | ||
| 40 | let task = p.header(); | ||
| 41 | let item = &task.timer_queue_item; | ||
| 32 | if task.state.timer_enqueue() { | 42 | if task.state.timer_enqueue() { |
| 33 | task.timer_queue_item.next.set(self.head.get()); | 43 | // If not in the queue, add it and update. |
| 34 | self.head.set(Some(p)); | 44 | let prev = self.head.replace(Some(p)); |
| 45 | item.next.set(prev); | ||
| 46 | } else if at <= item.expires_at.get() { | ||
| 47 | // If expiration is sooner than previously set, update. | ||
| 48 | } else { | ||
| 49 | // Task does not need to be updated. | ||
| 50 | return false; | ||
| 35 | } | 51 | } |
| 52 | |||
| 53 | item.expires_at.set(at); | ||
| 54 | true | ||
| 36 | } | 55 | } |
| 37 | } | 56 | } |
| 38 | 57 | ||
| 39 | pub(crate) unsafe fn next_expiration(&self) -> u64 { | 58 | /// Dequeues expired timers and returns the next alarm time. |
| 40 | let mut res = u64::MAX; | 59 | /// |
| 41 | self.retain(|p| { | 60 | /// The provided callback will be called for each expired task. Tasks that never expire |
| 42 | let task = p.header(); | 61 | /// will be removed, but the callback will not be called. |
| 43 | let expires = task.expires_at.get(); | 62 | pub fn next_expiration(&mut self, now: u64) -> u64 { |
| 44 | res = min(res, expires); | 63 | let mut next_expiration = u64::MAX; |
| 45 | expires != u64::MAX | ||
| 46 | }); | ||
| 47 | res | ||
| 48 | } | ||
| 49 | 64 | ||
| 50 | pub(crate) unsafe fn dequeue_expired(&self, now: u64, on_task: impl Fn(TaskRef)) { | ||
| 51 | self.retain(|p| { | 65 | self.retain(|p| { |
| 52 | let task = p.header(); | 66 | let task = p.header(); |
| 53 | if task.expires_at.get() <= now { | 67 | let item = &task.timer_queue_item; |
| 54 | on_task(p); | 68 | let expires = unsafe { item.expires_at.get() }; |
| 69 | |||
| 70 | if expires <= now { | ||
| 71 | // Timer expired, process task. | ||
| 72 | super::wake_task(p); | ||
| 55 | false | 73 | false |
| 56 | } else { | 74 | } else { |
| 57 | true | 75 | // Timer didn't yet expire, or never expires. |
| 76 | next_expiration = min(next_expiration, expires); | ||
| 77 | expires != u64::MAX | ||
| 58 | } | 78 | } |
| 59 | }); | 79 | }); |
| 80 | |||
| 81 | next_expiration | ||
| 60 | } | 82 | } |
| 61 | 83 | ||
| 62 | pub(crate) unsafe fn retain(&self, mut f: impl FnMut(TaskRef) -> bool) { | 84 | fn retain(&self, mut f: impl FnMut(TaskRef) -> bool) { |
| 63 | let mut prev = &self.head; | 85 | unsafe { |
| 64 | while let Some(p) = prev.get() { | 86 | let mut prev = &self.head; |
| 65 | let task = p.header(); | 87 | while let Some(p) = prev.get() { |
| 66 | if f(p) { | 88 | let task = p.header(); |
| 67 | // Skip to next | 89 | let item = &task.timer_queue_item; |
| 68 | prev = &task.timer_queue_item.next; | 90 | if f(p) { |
| 69 | } else { | 91 | // Skip to next |
| 70 | // Remove it | 92 | prev = &item.next; |
| 71 | prev.set(task.timer_queue_item.next.get()); | 93 | } else { |
| 72 | task.state.timer_dequeue(); | 94 | // Remove it |
| 95 | prev.set(item.next.get()); | ||
| 96 | task.state.timer_dequeue(); | ||
| 97 | } | ||
| 73 | } | 98 | } |
| 74 | } | 99 | } |
| 75 | } | 100 | } |
diff --git a/embassy-executor/src/raw/util.rs b/embassy-executor/src/raw/util.rs index c46085e45..e2633658a 100644 --- a/embassy-executor/src/raw/util.rs +++ b/embassy-executor/src/raw/util.rs | |||
| @@ -54,4 +54,9 @@ impl<T> SyncUnsafeCell<T> { | |||
| 54 | { | 54 | { |
| 55 | *self.value.get() | 55 | *self.value.get() |
| 56 | } | 56 | } |
| 57 | |||
| 58 | #[cfg(feature = "integrated-timers")] | ||
| 59 | pub unsafe fn replace(&self, value: T) -> T { | ||
| 60 | core::mem::replace(&mut *self.value.get(), value) | ||
| 61 | } | ||
| 57 | } | 62 | } |
diff --git a/embassy-executor/tests/test.rs b/embassy-executor/tests/test.rs index 8054bf7eb..0ce1f1891 100644 --- a/embassy-executor/tests/test.rs +++ b/embassy-executor/tests/test.rs | |||
| @@ -40,9 +40,6 @@ fn setup() -> (&'static Executor, Trace) { | |||
| 40 | let trace = Trace::new(); | 40 | let trace = Trace::new(); |
| 41 | let context = Box::leak(Box::new(trace.clone())) as *mut _ as *mut (); | 41 | let context = Box::leak(Box::new(trace.clone())) as *mut _ as *mut (); |
| 42 | let executor = &*Box::leak(Box::new(Executor::new(context))); | 42 | let executor = &*Box::leak(Box::new(Executor::new(context))); |
| 43 | unsafe { | ||
| 44 | executor.initialize(); | ||
| 45 | } | ||
| 46 | 43 | ||
| 47 | (executor, trace) | 44 | (executor, trace) |
| 48 | } | 45 | } |
diff --git a/embassy-nrf/Cargo.toml b/embassy-nrf/Cargo.toml index 9da050a22..48f80bb5e 100644 --- a/embassy-nrf/Cargo.toml +++ b/embassy-nrf/Cargo.toml | |||
| @@ -119,7 +119,7 @@ _nrf52 = ["_ppi"] | |||
| 119 | _nrf51 = ["_ppi"] | 119 | _nrf51 = ["_ppi"] |
| 120 | _nrf91 = [] | 120 | _nrf91 = [] |
| 121 | 121 | ||
| 122 | _time-driver = ["dep:embassy-time-driver", "embassy-time-driver?/tick-hz-32_768"] | 122 | _time-driver = ["dep:embassy-time-driver", "embassy-time-driver?/tick-hz-32_768", "dep:embassy-time-queue-driver"] |
| 123 | 123 | ||
| 124 | # trustzone state. | 124 | # trustzone state. |
| 125 | _s = [] | 125 | _s = [] |
| @@ -135,6 +135,7 @@ _nrf52832_anomaly_109 = [] | |||
| 135 | 135 | ||
| 136 | [dependencies] | 136 | [dependencies] |
| 137 | embassy-time-driver = { version = "0.1", path = "../embassy-time-driver", optional = true } | 137 | embassy-time-driver = { version = "0.1", path = "../embassy-time-driver", optional = true } |
| 138 | embassy-time-queue-driver = { version = "0.1", path = "../embassy-time-queue-driver", optional = true } | ||
| 138 | embassy-time = { version = "0.3.2", path = "../embassy-time", optional = true } | 139 | embassy-time = { version = "0.3.2", path = "../embassy-time", optional = true } |
| 139 | embassy-sync = { version = "0.6.1", path = "../embassy-sync" } | 140 | embassy-sync = { version = "0.6.1", path = "../embassy-sync" } |
| 140 | embassy-hal-internal = {version = "0.2.0", path = "../embassy-hal-internal", features = ["cortex-m", "prio-bits-3"] } | 141 | embassy-hal-internal = {version = "0.2.0", path = "../embassy-hal-internal", features = ["cortex-m", "prio-bits-3"] } |
diff --git a/embassy-nrf/src/time_driver.rs b/embassy-nrf/src/time_driver.rs index 9ba38ec1b..f8b3c4bbc 100644 --- a/embassy-nrf/src/time_driver.rs +++ b/embassy-nrf/src/time_driver.rs | |||
| @@ -1,11 +1,11 @@ | |||
| 1 | use core::cell::Cell; | 1 | use core::cell::Cell; |
| 2 | use core::sync::atomic::{compiler_fence, AtomicU32, AtomicU8, Ordering}; | 2 | use core::sync::atomic::{compiler_fence, AtomicU32, Ordering}; |
| 3 | use core::{mem, ptr}; | ||
| 4 | 3 | ||
| 5 | use critical_section::CriticalSection; | 4 | use critical_section::CriticalSection; |
| 6 | use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; | 5 | use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; |
| 7 | use embassy_sync::blocking_mutex::CriticalSectionMutex as Mutex; | 6 | use embassy_sync::blocking_mutex::CriticalSectionMutex as Mutex; |
| 8 | use embassy_time_driver::{AlarmHandle, Driver}; | 7 | use embassy_time_driver::Driver; |
| 8 | use embassy_time_queue_driver::GlobalTimerQueue; | ||
| 9 | 9 | ||
| 10 | use crate::interrupt::InterruptExt; | 10 | use crate::interrupt::InterruptExt; |
| 11 | use crate::{interrupt, pac}; | 11 | use crate::{interrupt, pac}; |
| @@ -94,11 +94,6 @@ mod test { | |||
| 94 | 94 | ||
| 95 | struct AlarmState { | 95 | struct AlarmState { |
| 96 | timestamp: Cell<u64>, | 96 | timestamp: Cell<u64>, |
| 97 | |||
| 98 | // This is really a Option<(fn(*mut ()), *mut ())> | ||
| 99 | // but fn pointers aren't allowed in const yet | ||
| 100 | callback: Cell<*const ()>, | ||
| 101 | ctx: Cell<*mut ()>, | ||
| 102 | } | 97 | } |
| 103 | 98 | ||
| 104 | unsafe impl Send for AlarmState {} | 99 | unsafe impl Send for AlarmState {} |
| @@ -107,26 +102,20 @@ impl AlarmState { | |||
| 107 | const fn new() -> Self { | 102 | const fn new() -> Self { |
| 108 | Self { | 103 | Self { |
| 109 | timestamp: Cell::new(u64::MAX), | 104 | timestamp: Cell::new(u64::MAX), |
| 110 | callback: Cell::new(ptr::null()), | ||
| 111 | ctx: Cell::new(ptr::null_mut()), | ||
| 112 | } | 105 | } |
| 113 | } | 106 | } |
| 114 | } | 107 | } |
| 115 | 108 | ||
| 116 | const ALARM_COUNT: usize = 3; | ||
| 117 | |||
| 118 | struct RtcDriver { | 109 | struct RtcDriver { |
| 119 | /// Number of 2^23 periods elapsed since boot. | 110 | /// Number of 2^23 periods elapsed since boot. |
| 120 | period: AtomicU32, | 111 | period: AtomicU32, |
| 121 | alarm_count: AtomicU8, | ||
| 122 | /// Timestamp at which to fire alarm. u64::MAX if no alarm is scheduled. | 112 | /// Timestamp at which to fire alarm. u64::MAX if no alarm is scheduled. |
| 123 | alarms: Mutex<[AlarmState; ALARM_COUNT]>, | 113 | alarms: Mutex<AlarmState>, |
| 124 | } | 114 | } |
| 125 | 115 | ||
| 126 | embassy_time_driver::time_driver_impl!(static DRIVER: RtcDriver = RtcDriver { | 116 | embassy_time_driver::time_driver_impl!(static DRIVER: RtcDriver = RtcDriver { |
| 127 | period: AtomicU32::new(0), | 117 | period: AtomicU32::new(0), |
| 128 | alarm_count: AtomicU8::new(0), | 118 | alarms: Mutex::const_new(CriticalSectionRawMutex::new(), AlarmState::new()), |
| 129 | alarms: Mutex::const_new(CriticalSectionRawMutex::new(), [const {AlarmState::new()}; ALARM_COUNT]), | ||
| 130 | }); | 119 | }); |
| 131 | 120 | ||
| 132 | impl RtcDriver { | 121 | impl RtcDriver { |
| @@ -169,13 +158,12 @@ impl RtcDriver { | |||
| 169 | self.next_period(); | 158 | self.next_period(); |
| 170 | } | 159 | } |
| 171 | 160 | ||
| 172 | for n in 0..ALARM_COUNT { | 161 | let n = 0; |
| 173 | if r.events_compare(n).read() == 1 { | 162 | if r.events_compare(n).read() == 1 { |
| 174 | r.events_compare(n).write_value(0); | 163 | r.events_compare(n).write_value(0); |
| 175 | critical_section::with(|cs| { | 164 | critical_section::with(|cs| { |
| 176 | self.trigger_alarm(n, cs); | 165 | self.trigger_alarm(cs); |
| 177 | }) | 166 | }); |
| 178 | } | ||
| 179 | } | 167 | } |
| 180 | } | 168 | } |
| 181 | 169 | ||
| @@ -186,75 +174,33 @@ impl RtcDriver { | |||
| 186 | self.period.store(period, Ordering::Relaxed); | 174 | self.period.store(period, Ordering::Relaxed); |
| 187 | let t = (period as u64) << 23; | 175 | let t = (period as u64) << 23; |
| 188 | 176 | ||
| 189 | for n in 0..ALARM_COUNT { | 177 | let n = 0; |
| 190 | let alarm = &self.alarms.borrow(cs)[n]; | 178 | let alarm = &self.alarms.borrow(cs); |
| 191 | let at = alarm.timestamp.get(); | 179 | let at = alarm.timestamp.get(); |
| 192 | 180 | ||
| 193 | if at < t + 0xc00000 { | 181 | if at < t + 0xc00000 { |
| 194 | // just enable it. `set_alarm` has already set the correct CC val. | 182 | // just enable it. `set_alarm` has already set the correct CC val. |
| 195 | r.intenset().write(|w| w.0 = compare_n(n)); | 183 | r.intenset().write(|w| w.0 = compare_n(n)); |
| 196 | } | ||
| 197 | } | 184 | } |
| 198 | }) | 185 | }) |
| 199 | } | 186 | } |
| 200 | 187 | ||
| 201 | fn get_alarm<'a>(&'a self, cs: CriticalSection<'a>, alarm: AlarmHandle) -> &'a AlarmState { | 188 | fn trigger_alarm(&self, cs: CriticalSection) { |
| 202 | // safety: we're allowed to assume the AlarmState is created by us, and | 189 | let n = 0; |
| 203 | // we never create one that's out of bounds. | ||
| 204 | unsafe { self.alarms.borrow(cs).get_unchecked(alarm.id() as usize) } | ||
| 205 | } | ||
| 206 | |||
| 207 | fn trigger_alarm(&self, n: usize, cs: CriticalSection) { | ||
| 208 | let r = rtc(); | 190 | let r = rtc(); |
| 209 | r.intenclr().write(|w| w.0 = compare_n(n)); | 191 | r.intenclr().write(|w| w.0 = compare_n(n)); |
| 210 | 192 | ||
| 211 | let alarm = &self.alarms.borrow(cs)[n]; | 193 | let alarm = &self.alarms.borrow(cs); |
| 212 | alarm.timestamp.set(u64::MAX); | 194 | alarm.timestamp.set(u64::MAX); |
| 213 | 195 | ||
| 214 | // Call after clearing alarm, so the callback can set another alarm. | 196 | // Call after clearing alarm, so the callback can set another alarm. |
| 215 | 197 | TIMER_QUEUE_DRIVER.dispatch(); | |
| 216 | // safety: | ||
| 217 | // - we can ignore the possiblity of `f` being unset (null) because of the safety contract of `allocate_alarm`. | ||
| 218 | // - other than that we only store valid function pointers into alarm.callback | ||
| 219 | let f: fn(*mut ()) = unsafe { mem::transmute(alarm.callback.get()) }; | ||
| 220 | f(alarm.ctx.get()); | ||
| 221 | } | ||
| 222 | } | ||
| 223 | |||
| 224 | impl Driver for RtcDriver { | ||
| 225 | fn now(&self) -> u64 { | ||
| 226 | // `period` MUST be read before `counter`, see comment at the top for details. | ||
| 227 | let period = self.period.load(Ordering::Relaxed); | ||
| 228 | compiler_fence(Ordering::Acquire); | ||
| 229 | let counter = rtc().counter().read().0; | ||
| 230 | calc_now(period, counter) | ||
| 231 | } | ||
| 232 | |||
| 233 | unsafe fn allocate_alarm(&self) -> Option<AlarmHandle> { | ||
| 234 | critical_section::with(|_| { | ||
| 235 | let id = self.alarm_count.load(Ordering::Relaxed); | ||
| 236 | if id < ALARM_COUNT as u8 { | ||
| 237 | self.alarm_count.store(id + 1, Ordering::Relaxed); | ||
| 238 | Some(AlarmHandle::new(id)) | ||
| 239 | } else { | ||
| 240 | None | ||
| 241 | } | ||
| 242 | }) | ||
| 243 | } | ||
| 244 | |||
| 245 | fn set_alarm_callback(&self, alarm: AlarmHandle, callback: fn(*mut ()), ctx: *mut ()) { | ||
| 246 | critical_section::with(|cs| { | ||
| 247 | let alarm = self.get_alarm(cs, alarm); | ||
| 248 | |||
| 249 | alarm.callback.set(callback as *const ()); | ||
| 250 | alarm.ctx.set(ctx); | ||
| 251 | }) | ||
| 252 | } | 198 | } |
| 253 | 199 | ||
| 254 | fn set_alarm(&self, alarm: AlarmHandle, timestamp: u64) -> bool { | 200 | fn set_alarm(&self, timestamp: u64) -> bool { |
| 255 | critical_section::with(|cs| { | 201 | critical_section::with(|cs| { |
| 256 | let n = alarm.id() as _; | 202 | let n = 0; |
| 257 | let alarm = self.get_alarm(cs, alarm); | 203 | let alarm = &self.alarms.borrow(cs); |
| 258 | alarm.timestamp.set(timestamp); | 204 | alarm.timestamp.set(timestamp); |
| 259 | 205 | ||
| 260 | let r = rtc(); | 206 | let r = rtc(); |
| @@ -304,6 +250,16 @@ impl Driver for RtcDriver { | |||
| 304 | } | 250 | } |
| 305 | } | 251 | } |
| 306 | 252 | ||
| 253 | impl Driver for RtcDriver { | ||
| 254 | fn now(&self) -> u64 { | ||
| 255 | // `period` MUST be read before `counter`, see comment at the top for details. | ||
| 256 | let period = self.period.load(Ordering::Relaxed); | ||
| 257 | compiler_fence(Ordering::Acquire); | ||
| 258 | let counter = rtc().counter().read().0; | ||
| 259 | calc_now(period, counter) | ||
| 260 | } | ||
| 261 | } | ||
| 262 | |||
| 307 | #[cfg(feature = "_nrf54l")] | 263 | #[cfg(feature = "_nrf54l")] |
| 308 | #[cfg(feature = "rt")] | 264 | #[cfg(feature = "rt")] |
| 309 | #[interrupt] | 265 | #[interrupt] |
| @@ -321,3 +277,8 @@ fn RTC1() { | |||
| 321 | pub(crate) fn init(irq_prio: crate::interrupt::Priority) { | 277 | pub(crate) fn init(irq_prio: crate::interrupt::Priority) { |
| 322 | DRIVER.init(irq_prio) | 278 | DRIVER.init(irq_prio) |
| 323 | } | 279 | } |
| 280 | |||
| 281 | embassy_time_queue_driver::timer_queue_impl!( | ||
| 282 | static TIMER_QUEUE_DRIVER: GlobalTimerQueue | ||
| 283 | = GlobalTimerQueue::new(|next_expiration| DRIVER.set_alarm(next_expiration)) | ||
| 284 | ); | ||
diff --git a/embassy-rp/Cargo.toml b/embassy-rp/Cargo.toml index 3809f1894..94de82fa9 100644 --- a/embassy-rp/Cargo.toml +++ b/embassy-rp/Cargo.toml | |||
| @@ -40,7 +40,7 @@ critical-section-impl = ["critical-section/restore-state-u8"] | |||
| 40 | unstable-pac = [] | 40 | unstable-pac = [] |
| 41 | 41 | ||
| 42 | ## Enable the timer for use with `embassy-time` with a 1MHz tick rate. | 42 | ## Enable the timer for use with `embassy-time` with a 1MHz tick rate. |
| 43 | time-driver = ["dep:embassy-time-driver", "embassy-time-driver?/tick-hz-1_000_000"] | 43 | time-driver = ["dep:embassy-time-driver", "embassy-time-driver?/tick-hz-1_000_000", "dep:embassy-time-queue-driver"] |
| 44 | 44 | ||
| 45 | ## Enable ROM function cache. This will store the address of a ROM function when first used, improving performance of subsequent calls. | 45 | ## Enable ROM function cache. This will store the address of a ROM function when first used, improving performance of subsequent calls. |
| 46 | rom-func-cache = [] | 46 | rom-func-cache = [] |
| @@ -110,6 +110,7 @@ binary-info = ["rt", "dep:rp-binary-info", "rp-binary-info?/binary-info"] | |||
| 110 | [dependencies] | 110 | [dependencies] |
| 111 | embassy-sync = { version = "0.6.1", path = "../embassy-sync" } | 111 | embassy-sync = { version = "0.6.1", path = "../embassy-sync" } |
| 112 | embassy-time-driver = { version = "0.1", path = "../embassy-time-driver", optional = true } | 112 | embassy-time-driver = { version = "0.1", path = "../embassy-time-driver", optional = true } |
| 113 | embassy-time-queue-driver = { version = "0.1", path = "../embassy-time-queue-driver", optional = true } | ||
| 113 | embassy-time = { version = "0.3.2", path = "../embassy-time" } | 114 | embassy-time = { version = "0.3.2", path = "../embassy-time" } |
| 114 | embassy-futures = { version = "0.1.0", path = "../embassy-futures" } | 115 | embassy-futures = { version = "0.1.0", path = "../embassy-futures" } |
| 115 | embassy-hal-internal = {version = "0.2.0", path = "../embassy-hal-internal", features = ["cortex-m", "prio-bits-2"] } | 116 | embassy-hal-internal = {version = "0.2.0", path = "../embassy-hal-internal", features = ["cortex-m", "prio-bits-2"] } |
diff --git a/embassy-rp/src/time_driver.rs b/embassy-rp/src/time_driver.rs index 40fc71bb1..17ae5fff3 100644 --- a/embassy-rp/src/time_driver.rs +++ b/embassy-rp/src/time_driver.rs | |||
| @@ -1,11 +1,10 @@ | |||
| 1 | //! Timer driver. | 1 | //! Timer driver. |
| 2 | use core::cell::Cell; | 2 | use core::cell::Cell; |
| 3 | 3 | ||
| 4 | use atomic_polyfill::{AtomicU8, Ordering}; | ||
| 5 | use critical_section::CriticalSection; | ||
| 6 | use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; | 4 | use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; |
| 7 | use embassy_sync::blocking_mutex::Mutex; | 5 | use embassy_sync::blocking_mutex::Mutex; |
| 8 | use embassy_time_driver::{AlarmHandle, Driver}; | 6 | use embassy_time_driver::Driver; |
| 7 | use embassy_time_queue_driver::GlobalTimerQueue; | ||
| 9 | #[cfg(feature = "rp2040")] | 8 | #[cfg(feature = "rp2040")] |
| 10 | use pac::TIMER; | 9 | use pac::TIMER; |
| 11 | #[cfg(feature = "_rp235x")] | 10 | #[cfg(feature = "_rp235x")] |
| @@ -16,23 +15,17 @@ use crate::{interrupt, pac}; | |||
| 16 | 15 | ||
| 17 | struct AlarmState { | 16 | struct AlarmState { |
| 18 | timestamp: Cell<u64>, | 17 | timestamp: Cell<u64>, |
| 19 | callback: Cell<Option<(fn(*mut ()), *mut ())>>, | ||
| 20 | } | 18 | } |
| 21 | unsafe impl Send for AlarmState {} | 19 | unsafe impl Send for AlarmState {} |
| 22 | 20 | ||
| 23 | const ALARM_COUNT: usize = 4; | ||
| 24 | |||
| 25 | struct TimerDriver { | 21 | struct TimerDriver { |
| 26 | alarms: Mutex<CriticalSectionRawMutex, [AlarmState; ALARM_COUNT]>, | 22 | alarms: Mutex<CriticalSectionRawMutex, AlarmState>, |
| 27 | next_alarm: AtomicU8, | ||
| 28 | } | 23 | } |
| 29 | 24 | ||
| 30 | embassy_time_driver::time_driver_impl!(static DRIVER: TimerDriver = TimerDriver{ | 25 | embassy_time_driver::time_driver_impl!(static DRIVER: TimerDriver = TimerDriver{ |
| 31 | alarms: Mutex::const_new(CriticalSectionRawMutex::new(), [const{AlarmState { | 26 | alarms: Mutex::const_new(CriticalSectionRawMutex::new(), AlarmState { |
| 32 | timestamp: Cell::new(0), | 27 | timestamp: Cell::new(0), |
| 33 | callback: Cell::new(None), | 28 | }), |
| 34 | }}; ALARM_COUNT]), | ||
| 35 | next_alarm: AtomicU8::new(0), | ||
| 36 | }); | 29 | }); |
| 37 | 30 | ||
| 38 | impl Driver for TimerDriver { | 31 | impl Driver for TimerDriver { |
| @@ -46,34 +39,13 @@ impl Driver for TimerDriver { | |||
| 46 | } | 39 | } |
| 47 | } | 40 | } |
| 48 | } | 41 | } |
| 42 | } | ||
| 49 | 43 | ||
| 50 | unsafe fn allocate_alarm(&self) -> Option<AlarmHandle> { | 44 | impl TimerDriver { |
| 51 | let id = self.next_alarm.fetch_update(Ordering::AcqRel, Ordering::Acquire, |x| { | 45 | fn set_alarm(&self, timestamp: u64) -> bool { |
| 52 | if x < ALARM_COUNT as u8 { | 46 | let n = 0; |
| 53 | Some(x + 1) | ||
| 54 | } else { | ||
| 55 | None | ||
| 56 | } | ||
| 57 | }); | ||
| 58 | |||
| 59 | match id { | ||
| 60 | Ok(id) => Some(AlarmHandle::new(id)), | ||
| 61 | Err(_) => None, | ||
| 62 | } | ||
| 63 | } | ||
| 64 | |||
| 65 | fn set_alarm_callback(&self, alarm: AlarmHandle, callback: fn(*mut ()), ctx: *mut ()) { | ||
| 66 | let n = alarm.id() as usize; | ||
| 67 | critical_section::with(|cs| { | ||
| 68 | let alarm = &self.alarms.borrow(cs)[n]; | ||
| 69 | alarm.callback.set(Some((callback, ctx))); | ||
| 70 | }) | ||
| 71 | } | ||
| 72 | |||
| 73 | fn set_alarm(&self, alarm: AlarmHandle, timestamp: u64) -> bool { | ||
| 74 | let n = alarm.id() as usize; | ||
| 75 | critical_section::with(|cs| { | 47 | critical_section::with(|cs| { |
| 76 | let alarm = &self.alarms.borrow(cs)[n]; | 48 | let alarm = &self.alarms.borrow(cs); |
| 77 | alarm.timestamp.set(timestamp); | 49 | alarm.timestamp.set(timestamp); |
| 78 | 50 | ||
| 79 | // Arm it. | 51 | // Arm it. |
| @@ -96,15 +68,14 @@ impl Driver for TimerDriver { | |||
| 96 | } | 68 | } |
| 97 | }) | 69 | }) |
| 98 | } | 70 | } |
| 99 | } | ||
| 100 | 71 | ||
| 101 | impl TimerDriver { | 72 | fn check_alarm(&self) { |
| 102 | fn check_alarm(&self, n: usize) { | 73 | let n = 0; |
| 103 | critical_section::with(|cs| { | 74 | critical_section::with(|cs| { |
| 104 | let alarm = &self.alarms.borrow(cs)[n]; | 75 | let alarm = &self.alarms.borrow(cs); |
| 105 | let timestamp = alarm.timestamp.get(); | 76 | let timestamp = alarm.timestamp.get(); |
| 106 | if timestamp <= self.now() { | 77 | if timestamp <= self.now() { |
| 107 | self.trigger_alarm(n, cs) | 78 | self.trigger_alarm() |
| 108 | } else { | 79 | } else { |
| 109 | // Not elapsed, arm it again. | 80 | // Not elapsed, arm it again. |
| 110 | // This can happen if it was set more than 2^32 us in the future. | 81 | // This can happen if it was set more than 2^32 us in the future. |
| @@ -116,17 +87,8 @@ impl TimerDriver { | |||
| 116 | TIMER.intr().write(|w| w.set_alarm(n, true)); | 87 | TIMER.intr().write(|w| w.set_alarm(n, true)); |
| 117 | } | 88 | } |
| 118 | 89 | ||
| 119 | fn trigger_alarm(&self, n: usize, cs: CriticalSection) { | 90 | fn trigger_alarm(&self) { |
| 120 | // disarm | 91 | TIMER_QUEUE_DRIVER.dispatch(); |
| 121 | TIMER.armed().write(|w| w.set_armed(1 << n)); | ||
| 122 | |||
| 123 | let alarm = &self.alarms.borrow(cs)[n]; | ||
| 124 | alarm.timestamp.set(u64::MAX); | ||
| 125 | |||
| 126 | // Call after clearing alarm, so the callback can set another alarm. | ||
| 127 | if let Some((f, ctx)) = alarm.callback.get() { | ||
| 128 | f(ctx); | ||
| 129 | } | ||
| 130 | } | 92 | } |
| 131 | } | 93 | } |
| 132 | 94 | ||
| @@ -134,79 +96,37 @@ impl TimerDriver { | |||
| 134 | pub unsafe fn init() { | 96 | pub unsafe fn init() { |
| 135 | // init alarms | 97 | // init alarms |
| 136 | critical_section::with(|cs| { | 98 | critical_section::with(|cs| { |
| 137 | let alarms = DRIVER.alarms.borrow(cs); | 99 | let alarm = DRIVER.alarms.borrow(cs); |
| 138 | for a in alarms { | 100 | alarm.timestamp.set(u64::MAX); |
| 139 | a.timestamp.set(u64::MAX); | ||
| 140 | } | ||
| 141 | }); | 101 | }); |
| 142 | 102 | ||
| 143 | // enable all irqs | 103 | // enable irq |
| 144 | TIMER.inte().write(|w| { | 104 | TIMER.inte().write(|w| { |
| 145 | w.set_alarm(0, true); | 105 | w.set_alarm(0, true); |
| 146 | w.set_alarm(1, true); | ||
| 147 | w.set_alarm(2, true); | ||
| 148 | w.set_alarm(3, true); | ||
| 149 | }); | 106 | }); |
| 150 | #[cfg(feature = "rp2040")] | 107 | #[cfg(feature = "rp2040")] |
| 151 | { | 108 | { |
| 152 | interrupt::TIMER_IRQ_0.enable(); | 109 | interrupt::TIMER_IRQ_0.enable(); |
| 153 | interrupt::TIMER_IRQ_1.enable(); | ||
| 154 | interrupt::TIMER_IRQ_2.enable(); | ||
| 155 | interrupt::TIMER_IRQ_3.enable(); | ||
| 156 | } | 110 | } |
| 157 | #[cfg(feature = "_rp235x")] | 111 | #[cfg(feature = "_rp235x")] |
| 158 | { | 112 | { |
| 159 | interrupt::TIMER0_IRQ_0.enable(); | 113 | interrupt::TIMER0_IRQ_0.enable(); |
| 160 | interrupt::TIMER0_IRQ_1.enable(); | ||
| 161 | interrupt::TIMER0_IRQ_2.enable(); | ||
| 162 | interrupt::TIMER0_IRQ_3.enable(); | ||
| 163 | } | 114 | } |
| 164 | } | 115 | } |
| 165 | 116 | ||
| 166 | #[cfg(all(feature = "rt", feature = "rp2040"))] | 117 | #[cfg(all(feature = "rt", feature = "rp2040"))] |
| 167 | #[interrupt] | 118 | #[interrupt] |
| 168 | fn TIMER_IRQ_0() { | 119 | fn TIMER_IRQ_0() { |
| 169 | DRIVER.check_alarm(0) | 120 | DRIVER.check_alarm() |
| 170 | } | ||
| 171 | |||
| 172 | #[cfg(all(feature = "rt", feature = "rp2040"))] | ||
| 173 | #[interrupt] | ||
| 174 | fn TIMER_IRQ_1() { | ||
| 175 | DRIVER.check_alarm(1) | ||
| 176 | } | ||
| 177 | |||
| 178 | #[cfg(all(feature = "rt", feature = "rp2040"))] | ||
| 179 | #[interrupt] | ||
| 180 | fn TIMER_IRQ_2() { | ||
| 181 | DRIVER.check_alarm(2) | ||
| 182 | } | ||
| 183 | |||
| 184 | #[cfg(all(feature = "rt", feature = "rp2040"))] | ||
| 185 | #[interrupt] | ||
| 186 | fn TIMER_IRQ_3() { | ||
| 187 | DRIVER.check_alarm(3) | ||
| 188 | } | 121 | } |
| 189 | 122 | ||
| 190 | #[cfg(all(feature = "rt", feature = "_rp235x"))] | 123 | #[cfg(all(feature = "rt", feature = "_rp235x"))] |
| 191 | #[interrupt] | 124 | #[interrupt] |
| 192 | fn TIMER0_IRQ_0() { | 125 | fn TIMER0_IRQ_0() { |
| 193 | DRIVER.check_alarm(0) | 126 | DRIVER.check_alarm() |
| 194 | } | 127 | } |
| 195 | 128 | ||
| 196 | #[cfg(all(feature = "rt", feature = "_rp235x"))] | 129 | embassy_time_queue_driver::timer_queue_impl!( |
| 197 | #[interrupt] | 130 | static TIMER_QUEUE_DRIVER: GlobalTimerQueue |
| 198 | fn TIMER0_IRQ_1() { | 131 | = GlobalTimerQueue::new(|next_expiration| DRIVER.set_alarm(next_expiration)) |
| 199 | DRIVER.check_alarm(1) | 132 | ); |
| 200 | } | ||
| 201 | |||
| 202 | #[cfg(all(feature = "rt", feature = "_rp235x"))] | ||
| 203 | #[interrupt] | ||
| 204 | fn TIMER0_IRQ_2() { | ||
| 205 | DRIVER.check_alarm(2) | ||
| 206 | } | ||
| 207 | |||
| 208 | #[cfg(all(feature = "rt", feature = "_rp235x"))] | ||
| 209 | #[interrupt] | ||
| 210 | fn TIMER0_IRQ_3() { | ||
| 211 | DRIVER.check_alarm(3) | ||
| 212 | } | ||
diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index 8b4e7c929..82030f99f 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml | |||
| @@ -45,6 +45,7 @@ rustdoc-args = ["--cfg", "docsrs"] | |||
| 45 | embassy-sync = { version = "0.6.1", path = "../embassy-sync" } | 45 | embassy-sync = { version = "0.6.1", path = "../embassy-sync" } |
| 46 | embassy-time = { version = "0.3.2", path = "../embassy-time", optional = true } | 46 | embassy-time = { version = "0.3.2", path = "../embassy-time", optional = true } |
| 47 | embassy-time-driver = { version = "0.1", path = "../embassy-time-driver", optional = true } | 47 | embassy-time-driver = { version = "0.1", path = "../embassy-time-driver", optional = true } |
| 48 | embassy-time-queue-driver = { version = "0.1", path = "../embassy-time-queue-driver", optional = true } | ||
| 48 | embassy-futures = { version = "0.1.0", path = "../embassy-futures" } | 49 | embassy-futures = { version = "0.1.0", path = "../embassy-futures" } |
| 49 | embassy-hal-internal = {version = "0.2.0", path = "../embassy-hal-internal", features = ["cortex-m", "prio-bits-4"] } | 50 | embassy-hal-internal = {version = "0.2.0", path = "../embassy-hal-internal", features = ["cortex-m", "prio-bits-4"] } |
| 50 | embassy-embedded-hal = {version = "0.2.0", path = "../embassy-embedded-hal", default-features = false } | 51 | embassy-embedded-hal = {version = "0.2.0", path = "../embassy-embedded-hal", default-features = false } |
| @@ -125,6 +126,7 @@ defmt = [ | |||
| 125 | exti = [] | 126 | exti = [] |
| 126 | low-power = [ "dep:embassy-executor", "embassy-executor?/arch-cortex-m", "time" ] | 127 | low-power = [ "dep:embassy-executor", "embassy-executor?/arch-cortex-m", "time" ] |
| 127 | low-power-debug-with-sleep = [] | 128 | low-power-debug-with-sleep = [] |
| 129 | integrated-timers = ["dep:embassy-executor", "_time-driver"] | ||
| 128 | 130 | ||
| 129 | ## Automatically generate `memory.x` file using [`stm32-metapac`](https://docs.rs/stm32-metapac/) | 131 | ## Automatically generate `memory.x` file using [`stm32-metapac`](https://docs.rs/stm32-metapac/) |
| 130 | memory-x = ["stm32-metapac/memory-x"] | 132 | memory-x = ["stm32-metapac/memory-x"] |
| @@ -149,7 +151,7 @@ time = ["dep:embassy-time", "embassy-embedded-hal/time"] | |||
| 149 | 151 | ||
| 150 | # Features starting with `_` are for internal use only. They're not intended | 152 | # Features starting with `_` are for internal use only. They're not intended |
| 151 | # to be enabled by other crates, and are not covered by semver guarantees. | 153 | # to be enabled by other crates, and are not covered by semver guarantees. |
| 152 | _time-driver = ["dep:embassy-time-driver", "time"] | 154 | _time-driver = ["dep:embassy-time-driver", "time", "dep:embassy-time-queue-driver"] |
| 153 | 155 | ||
| 154 | ## Use any time driver | 156 | ## Use any time driver |
| 155 | time-driver-any = ["_time-driver"] | 157 | time-driver-any = ["_time-driver"] |
diff --git a/embassy-stm32/src/low_power.rs b/embassy-stm32/src/low_power.rs index 0b87bd95a..7734365f1 100644 --- a/embassy-stm32/src/low_power.rs +++ b/embassy-stm32/src/low_power.rs | |||
| @@ -256,9 +256,6 @@ impl Executor { | |||
| 256 | /// This function never returns. | 256 | /// This function never returns. |
| 257 | pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! { | 257 | pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! { |
| 258 | let executor = unsafe { EXECUTOR.as_mut().unwrap() }; | 258 | let executor = unsafe { EXECUTOR.as_mut().unwrap() }; |
| 259 | unsafe { | ||
| 260 | executor.inner.initialize(); | ||
| 261 | } | ||
| 262 | init(executor.inner.spawner()); | 259 | init(executor.inner.spawner()); |
| 263 | 260 | ||
| 264 | loop { | 261 | loop { |
diff --git a/embassy-stm32/src/time_driver.rs b/embassy-stm32/src/time_driver.rs index 00aa3cfa4..290f857ad 100644 --- a/embassy-stm32/src/time_driver.rs +++ b/embassy-stm32/src/time_driver.rs | |||
| @@ -1,13 +1,13 @@ | |||
| 1 | #![allow(non_snake_case)] | 1 | #![allow(non_snake_case)] |
| 2 | 2 | ||
| 3 | use core::cell::Cell; | 3 | use core::cell::Cell; |
| 4 | use core::sync::atomic::{compiler_fence, AtomicU32, AtomicU8, Ordering}; | 4 | use core::sync::atomic::{compiler_fence, AtomicU32, Ordering}; |
| 5 | use core::{mem, ptr}; | ||
| 6 | 5 | ||
| 7 | use critical_section::CriticalSection; | 6 | use critical_section::CriticalSection; |
| 8 | use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; | 7 | use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; |
| 9 | use embassy_sync::blocking_mutex::Mutex; | 8 | use embassy_sync::blocking_mutex::Mutex; |
| 10 | use embassy_time_driver::{AlarmHandle, Driver, TICK_HZ}; | 9 | use embassy_time_driver::{Driver, TICK_HZ}; |
| 10 | use embassy_time_queue_driver::GlobalTimerQueue; | ||
| 11 | use stm32_metapac::timer::{regs, TimGp16}; | 11 | use stm32_metapac::timer::{regs, TimGp16}; |
| 12 | 12 | ||
| 13 | use crate::interrupt::typelevel::Interrupt; | 13 | use crate::interrupt::typelevel::Interrupt; |
| @@ -24,18 +24,6 @@ use crate::{interrupt, peripherals}; | |||
| 24 | // additional CC capabilities to provide timer alarms to embassy-time. embassy-time requires AT LEAST | 24 | // additional CC capabilities to provide timer alarms to embassy-time. embassy-time requires AT LEAST |
| 25 | // one alarm to be allocatable, which means timers that only have CC1, such as TIM16/TIM17, are not | 25 | // one alarm to be allocatable, which means timers that only have CC1, such as TIM16/TIM17, are not |
| 26 | // candidates for use as an embassy-time driver provider. (a.k.a 1CH and 1CH_CMP are not, others are good.) | 26 | // candidates for use as an embassy-time driver provider. (a.k.a 1CH and 1CH_CMP are not, others are good.) |
| 27 | // | ||
| 28 | // The values of ALARM_COUNT below are not the TOTAL CC registers available, but rather the number | ||
| 29 | // available after reserving CC1 for regular time keeping. For example, TIM2 has four CC registers: | ||
| 30 | // CC1, CC2, CC3, and CC4, so it can provide ALARM_COUNT = 3. | ||
| 31 | |||
| 32 | cfg_if::cfg_if! { | ||
| 33 | if #[cfg(any(time_driver_tim9, time_driver_tim12, time_driver_tim15, time_driver_tim21, time_driver_tim22))] { | ||
| 34 | const ALARM_COUNT: usize = 1; | ||
| 35 | } else { | ||
| 36 | const ALARM_COUNT: usize = 3; | ||
| 37 | } | ||
| 38 | } | ||
| 39 | 27 | ||
| 40 | #[cfg(time_driver_tim1)] | 28 | #[cfg(time_driver_tim1)] |
| 41 | type T = peripherals::TIM1; | 29 | type T = peripherals::TIM1; |
| @@ -208,11 +196,6 @@ fn calc_now(period: u32, counter: u16) -> u64 { | |||
| 208 | 196 | ||
| 209 | struct AlarmState { | 197 | struct AlarmState { |
| 210 | timestamp: Cell<u64>, | 198 | timestamp: Cell<u64>, |
| 211 | |||
| 212 | // This is really a Option<(fn(*mut ()), *mut ())> | ||
| 213 | // but fn pointers aren't allowed in const yet | ||
| 214 | callback: Cell<*const ()>, | ||
| 215 | ctx: Cell<*mut ()>, | ||
| 216 | } | 199 | } |
| 217 | 200 | ||
| 218 | unsafe impl Send for AlarmState {} | 201 | unsafe impl Send for AlarmState {} |
| @@ -221,8 +204,6 @@ impl AlarmState { | |||
| 221 | const fn new() -> Self { | 204 | const fn new() -> Self { |
| 222 | Self { | 205 | Self { |
| 223 | timestamp: Cell::new(u64::MAX), | 206 | timestamp: Cell::new(u64::MAX), |
| 224 | callback: Cell::new(ptr::null()), | ||
| 225 | ctx: Cell::new(ptr::null_mut()), | ||
| 226 | } | 207 | } |
| 227 | } | 208 | } |
| 228 | } | 209 | } |
| @@ -230,17 +211,14 @@ impl AlarmState { | |||
| 230 | pub(crate) struct RtcDriver { | 211 | pub(crate) struct RtcDriver { |
| 231 | /// Number of 2^15 periods elapsed since boot. | 212 | /// Number of 2^15 periods elapsed since boot. |
| 232 | period: AtomicU32, | 213 | period: AtomicU32, |
| 233 | alarm_count: AtomicU8, | 214 | alarm: Mutex<CriticalSectionRawMutex, AlarmState>, |
| 234 | /// Timestamp at which to fire alarm. u64::MAX if no alarm is scheduled. | ||
| 235 | alarms: Mutex<CriticalSectionRawMutex, [AlarmState; ALARM_COUNT]>, | ||
| 236 | #[cfg(feature = "low-power")] | 215 | #[cfg(feature = "low-power")] |
| 237 | rtc: Mutex<CriticalSectionRawMutex, Cell<Option<&'static Rtc>>>, | 216 | rtc: Mutex<CriticalSectionRawMutex, Cell<Option<&'static Rtc>>>, |
| 238 | } | 217 | } |
| 239 | 218 | ||
| 240 | embassy_time_driver::time_driver_impl!(static DRIVER: RtcDriver = RtcDriver { | 219 | embassy_time_driver::time_driver_impl!(static DRIVER: RtcDriver = RtcDriver { |
| 241 | period: AtomicU32::new(0), | 220 | period: AtomicU32::new(0), |
| 242 | alarm_count: AtomicU8::new(0), | 221 | alarm: Mutex::const_new(CriticalSectionRawMutex::new(), AlarmState::new()), |
| 243 | alarms: Mutex::const_new(CriticalSectionRawMutex::new(), [const{AlarmState::new()}; ALARM_COUNT]), | ||
| 244 | #[cfg(feature = "low-power")] | 222 | #[cfg(feature = "low-power")] |
| 245 | rtc: Mutex::const_new(CriticalSectionRawMutex::new(), Cell::new(None)), | 223 | rtc: Mutex::const_new(CriticalSectionRawMutex::new(), Cell::new(None)), |
| 246 | }); | 224 | }); |
| @@ -289,7 +267,7 @@ impl RtcDriver { | |||
| 289 | let r = regs_gp16(); | 267 | let r = regs_gp16(); |
| 290 | 268 | ||
| 291 | // XXX: reduce the size of this critical section ? | 269 | // XXX: reduce the size of this critical section ? |
| 292 | critical_section::with(|cs| { | 270 | critical_section::with(|_cs| { |
| 293 | let sr = r.sr().read(); | 271 | let sr = r.sr().read(); |
| 294 | let dier = r.dier().read(); | 272 | let dier = r.dier().read(); |
| 295 | 273 | ||
| @@ -308,10 +286,9 @@ impl RtcDriver { | |||
| 308 | self.next_period(); | 286 | self.next_period(); |
| 309 | } | 287 | } |
| 310 | 288 | ||
| 311 | for n in 0..ALARM_COUNT { | 289 | let n = 0; |
| 312 | if sr.ccif(n + 1) && dier.ccie(n + 1) { | 290 | if sr.ccif(n + 1) && dier.ccie(n + 1) { |
| 313 | self.trigger_alarm(n, cs); | 291 | self.trigger_alarm(); |
| 314 | } | ||
| 315 | } | 292 | } |
| 316 | }) | 293 | }) |
| 317 | } | 294 | } |
| @@ -326,36 +303,20 @@ impl RtcDriver { | |||
| 326 | 303 | ||
| 327 | critical_section::with(move |cs| { | 304 | critical_section::with(move |cs| { |
| 328 | r.dier().modify(move |w| { | 305 | r.dier().modify(move |w| { |
| 329 | for n in 0..ALARM_COUNT { | 306 | let n = 0; |
| 330 | let alarm = &self.alarms.borrow(cs)[n]; | 307 | let alarm = self.alarm.borrow(cs); |
| 331 | let at = alarm.timestamp.get(); | 308 | let at = alarm.timestamp.get(); |
| 332 | 309 | ||
| 333 | if at < t + 0xc000 { | 310 | if at < t + 0xc000 { |
| 334 | // just enable it. `set_alarm` has already set the correct CCR val. | 311 | // just enable it. `set_alarm` has already set the correct CCR val. |
| 335 | w.set_ccie(n + 1, true); | 312 | w.set_ccie(n + 1, true); |
| 336 | } | ||
| 337 | } | 313 | } |
| 338 | }) | 314 | }) |
| 339 | }) | 315 | }) |
| 340 | } | 316 | } |
| 341 | 317 | ||
| 342 | fn get_alarm<'a>(&'a self, cs: CriticalSection<'a>, alarm: AlarmHandle) -> &'a AlarmState { | 318 | fn trigger_alarm(&self) { |
| 343 | // safety: we're allowed to assume the AlarmState is created by us, and | 319 | TIMER_QUEUE_DRIVER.dispatch(); |
| 344 | // we never create one that's out of bounds. | ||
| 345 | unsafe { self.alarms.borrow(cs).get_unchecked(alarm.id() as usize) } | ||
| 346 | } | ||
| 347 | |||
| 348 | fn trigger_alarm(&self, n: usize, cs: CriticalSection) { | ||
| 349 | let alarm = &self.alarms.borrow(cs)[n]; | ||
| 350 | alarm.timestamp.set(u64::MAX); | ||
| 351 | |||
| 352 | // Call after clearing alarm, so the callback can set another alarm. | ||
| 353 | |||
| 354 | // safety: | ||
| 355 | // - we can ignore the possibility of `f` being unset (null) because of the safety contract of `allocate_alarm`. | ||
| 356 | // - other than that we only store valid function pointers into alarm.callback | ||
| 357 | let f: fn(*mut ()) = unsafe { mem::transmute(alarm.callback.get()) }; | ||
| 358 | f(alarm.ctx.get()); | ||
| 359 | } | 320 | } |
| 360 | 321 | ||
| 361 | /* | 322 | /* |
| @@ -367,14 +328,7 @@ impl RtcDriver { | |||
| 367 | fn time_until_next_alarm(&self, cs: CriticalSection) -> embassy_time::Duration { | 328 | fn time_until_next_alarm(&self, cs: CriticalSection) -> embassy_time::Duration { |
| 368 | let now = self.now() + 32; | 329 | let now = self.now() + 32; |
| 369 | 330 | ||
| 370 | embassy_time::Duration::from_ticks( | 331 | embassy_time::Duration::from_ticks(self.alarm.borrow(cs).timestamp.get().saturating_sub(now)) |
| 371 | self.alarms | ||
| 372 | .borrow(cs) | ||
| 373 | .iter() | ||
| 374 | .map(|alarm: &AlarmState| alarm.timestamp.get().saturating_sub(now)) | ||
| 375 | .min() | ||
| 376 | .unwrap_or(u64::MAX), | ||
| 377 | ) | ||
| 378 | } | 332 | } |
| 379 | 333 | ||
| 380 | #[cfg(feature = "low-power")] | 334 | #[cfg(feature = "low-power")] |
| @@ -409,15 +363,12 @@ impl RtcDriver { | |||
| 409 | self.period.store(period, Ordering::SeqCst); | 363 | self.period.store(period, Ordering::SeqCst); |
| 410 | regs_gp16().cnt().write(|w| w.set_cnt(cnt as u16)); | 364 | regs_gp16().cnt().write(|w| w.set_cnt(cnt as u16)); |
| 411 | 365 | ||
| 412 | // Now, recompute all alarms | 366 | // Now, recompute alarm |
| 413 | for i in 0..self.alarm_count.load(Ordering::Relaxed) as usize { | 367 | let alarm = self.alarm.borrow(cs); |
| 414 | let alarm_handle = unsafe { AlarmHandle::new(i as u8) }; | ||
| 415 | let alarm = self.get_alarm(cs, alarm_handle); | ||
| 416 | 368 | ||
| 417 | if !self.set_alarm(alarm_handle, alarm.timestamp.get()) { | 369 | if !self.set_alarm(alarm.timestamp.get()) { |
| 418 | // If the alarm timestamp has passed, we need to trigger it | 370 | // If the alarm timestamp has passed, we need to trigger it |
| 419 | self.trigger_alarm(i, cs); | 371 | self.trigger_alarm(); |
| 420 | } | ||
| 421 | } | 372 | } |
| 422 | } | 373 | } |
| 423 | 374 | ||
| @@ -489,46 +440,13 @@ impl RtcDriver { | |||
| 489 | regs_gp16().cr1().modify(|w| w.set_cen(true)); | 440 | regs_gp16().cr1().modify(|w| w.set_cen(true)); |
| 490 | }) | 441 | }) |
| 491 | } | 442 | } |
| 492 | } | ||
| 493 | |||
| 494 | impl Driver for RtcDriver { | ||
| 495 | fn now(&self) -> u64 { | ||
| 496 | let r = regs_gp16(); | ||
| 497 | |||
| 498 | let period = self.period.load(Ordering::Relaxed); | ||
| 499 | compiler_fence(Ordering::Acquire); | ||
| 500 | let counter = r.cnt().read().cnt(); | ||
| 501 | calc_now(period, counter) | ||
| 502 | } | ||
| 503 | 443 | ||
| 504 | unsafe fn allocate_alarm(&self) -> Option<AlarmHandle> { | 444 | fn set_alarm(&self, timestamp: u64) -> bool { |
| 505 | critical_section::with(|_| { | ||
| 506 | let id = self.alarm_count.load(Ordering::Relaxed); | ||
| 507 | if id < ALARM_COUNT as u8 { | ||
| 508 | self.alarm_count.store(id + 1, Ordering::Relaxed); | ||
| 509 | Some(AlarmHandle::new(id)) | ||
| 510 | } else { | ||
| 511 | None | ||
| 512 | } | ||
| 513 | }) | ||
| 514 | } | ||
| 515 | |||
| 516 | fn set_alarm_callback(&self, alarm: AlarmHandle, callback: fn(*mut ()), ctx: *mut ()) { | ||
| 517 | critical_section::with(|cs| { | ||
| 518 | let alarm = self.get_alarm(cs, alarm); | ||
| 519 | |||
| 520 | alarm.callback.set(callback as *const ()); | ||
| 521 | alarm.ctx.set(ctx); | ||
| 522 | }) | ||
| 523 | } | ||
| 524 | |||
| 525 | fn set_alarm(&self, alarm: AlarmHandle, timestamp: u64) -> bool { | ||
| 526 | critical_section::with(|cs| { | 445 | critical_section::with(|cs| { |
| 527 | let r = regs_gp16(); | 446 | let r = regs_gp16(); |
| 528 | 447 | ||
| 529 | let n = alarm.id() as usize; | 448 | let n = 0; |
| 530 | let alarm = self.get_alarm(cs, alarm); | 449 | self.alarm.borrow(cs).timestamp.set(timestamp); |
| 531 | alarm.timestamp.set(timestamp); | ||
| 532 | 450 | ||
| 533 | let t = self.now(); | 451 | let t = self.now(); |
| 534 | if timestamp <= t { | 452 | if timestamp <= t { |
| @@ -536,7 +454,7 @@ impl Driver for RtcDriver { | |||
| 536 | // Disarm the alarm and return `false` to indicate that. | 454 | // Disarm the alarm and return `false` to indicate that. |
| 537 | r.dier().modify(|w| w.set_ccie(n + 1, false)); | 455 | r.dier().modify(|w| w.set_ccie(n + 1, false)); |
| 538 | 456 | ||
| 539 | alarm.timestamp.set(u64::MAX); | 457 | self.alarm.borrow(cs).timestamp.set(u64::MAX); |
| 540 | 458 | ||
| 541 | return false; | 459 | return false; |
| 542 | } | 460 | } |
| @@ -558,7 +476,7 @@ impl Driver for RtcDriver { | |||
| 558 | // It is the caller's responsibility to handle this ambiguity. | 476 | // It is the caller's responsibility to handle this ambiguity. |
| 559 | r.dier().modify(|w| w.set_ccie(n + 1, false)); | 477 | r.dier().modify(|w| w.set_ccie(n + 1, false)); |
| 560 | 478 | ||
| 561 | alarm.timestamp.set(u64::MAX); | 479 | self.alarm.borrow(cs).timestamp.set(u64::MAX); |
| 562 | 480 | ||
| 563 | return false; | 481 | return false; |
| 564 | } | 482 | } |
| @@ -569,6 +487,17 @@ impl Driver for RtcDriver { | |||
| 569 | } | 487 | } |
| 570 | } | 488 | } |
| 571 | 489 | ||
| 490 | impl Driver for RtcDriver { | ||
| 491 | fn now(&self) -> u64 { | ||
| 492 | let r = regs_gp16(); | ||
| 493 | |||
| 494 | let period = self.period.load(Ordering::Relaxed); | ||
| 495 | compiler_fence(Ordering::Acquire); | ||
| 496 | let counter = r.cnt().read().cnt(); | ||
| 497 | calc_now(period, counter) | ||
| 498 | } | ||
| 499 | } | ||
| 500 | |||
| 572 | #[cfg(feature = "low-power")] | 501 | #[cfg(feature = "low-power")] |
| 573 | pub(crate) fn get_driver() -> &'static RtcDriver { | 502 | pub(crate) fn get_driver() -> &'static RtcDriver { |
| 574 | &DRIVER | 503 | &DRIVER |
| @@ -577,3 +506,8 @@ pub(crate) fn get_driver() -> &'static RtcDriver { | |||
| 577 | pub(crate) fn init(cs: CriticalSection) { | 506 | pub(crate) fn init(cs: CriticalSection) { |
| 578 | DRIVER.init(cs) | 507 | DRIVER.init(cs) |
| 579 | } | 508 | } |
| 509 | |||
| 510 | embassy_time_queue_driver::timer_queue_impl!( | ||
| 511 | static TIMER_QUEUE_DRIVER: GlobalTimerQueue | ||
| 512 | = GlobalTimerQueue::new(|next_expiration| DRIVER.set_alarm(next_expiration)) | ||
| 513 | ); | ||
diff --git a/embassy-time-driver/src/lib.rs b/embassy-time-driver/src/lib.rs index 12f40b9b9..ffb363cd7 100644 --- a/embassy-time-driver/src/lib.rs +++ b/embassy-time-driver/src/lib.rs | |||
| @@ -21,8 +21,8 @@ | |||
| 21 | //! | 21 | //! |
| 22 | //! Instead of the usual "trait + generic params" approach, calls from embassy to the driver are done via `extern` functions. | 22 | //! Instead of the usual "trait + generic params" approach, calls from embassy to the driver are done via `extern` functions. |
| 23 | //! | 23 | //! |
| 24 | //! `embassy` internally defines the driver functions as `extern "Rust" { fn _embassy_time_now() -> u64; }` and calls them. | 24 | //! `embassy` internally defines the driver function as `extern "Rust" { fn _embassy_time_now() -> u64; }` and calls it. |
| 25 | //! The driver crate defines the functions as `#[no_mangle] fn _embassy_time_now() -> u64`. The linker will resolve the | 25 | //! The driver crate defines the function as `#[no_mangle] fn _embassy_time_now() -> u64`. The linker will resolve the |
| 26 | //! calls from the `embassy` crate to call into the driver crate. | 26 | //! calls from the `embassy` crate to call into the driver crate. |
| 27 | //! | 27 | //! |
| 28 | //! If there is none or multiple drivers in the crate tree, linking will fail. | 28 | //! If there is none or multiple drivers in the crate tree, linking will fail. |
| @@ -38,7 +38,7 @@ | |||
| 38 | //! # Example | 38 | //! # Example |
| 39 | //! | 39 | //! |
| 40 | //! ``` | 40 | //! ``` |
| 41 | //! use embassy_time_driver::{Driver, AlarmHandle}; | 41 | //! use embassy_time_driver::Driver; |
| 42 | //! | 42 | //! |
| 43 | //! struct MyDriver{} // not public! | 43 | //! struct MyDriver{} // not public! |
| 44 | //! | 44 | //! |
| @@ -46,15 +46,6 @@ | |||
| 46 | //! fn now(&self) -> u64 { | 46 | //! fn now(&self) -> u64 { |
| 47 | //! todo!() | 47 | //! todo!() |
| 48 | //! } | 48 | //! } |
| 49 | //! unsafe fn allocate_alarm(&self) -> Option<AlarmHandle> { | ||
| 50 | //! todo!() | ||
| 51 | //! } | ||
| 52 | //! fn set_alarm_callback(&self, alarm: AlarmHandle, callback: fn(*mut ()), ctx: *mut ()) { | ||
| 53 | //! todo!() | ||
| 54 | //! } | ||
| 55 | //! fn set_alarm(&self, alarm: AlarmHandle, timestamp: u64) -> bool { | ||
| 56 | //! todo!() | ||
| 57 | //! } | ||
| 58 | //! } | 49 | //! } |
| 59 | //! | 50 | //! |
| 60 | //! embassy_time_driver::time_driver_impl!(static DRIVER: MyDriver = MyDriver{}); | 51 | //! embassy_time_driver::time_driver_impl!(static DRIVER: MyDriver = MyDriver{}); |
| @@ -70,28 +61,6 @@ mod tick; | |||
| 70 | /// This value is specified by the [`tick-*` Cargo features](crate#tick-rate) | 61 | /// This value is specified by the [`tick-*` Cargo features](crate#tick-rate) |
| 71 | pub const TICK_HZ: u64 = tick::TICK_HZ; | 62 | pub const TICK_HZ: u64 = tick::TICK_HZ; |
| 72 | 63 | ||
| 73 | /// Alarm handle, assigned by the driver. | ||
| 74 | #[derive(Clone, Copy)] | ||
| 75 | pub struct AlarmHandle { | ||
| 76 | id: u8, | ||
| 77 | } | ||
| 78 | |||
| 79 | impl AlarmHandle { | ||
| 80 | /// Create an AlarmHandle | ||
| 81 | /// | ||
| 82 | /// Safety: May only be called by the current global Driver impl. | ||
| 83 | /// The impl is allowed to rely on the fact that all `AlarmHandle` instances | ||
| 84 | /// are created by itself in unsafe code (e.g. indexing operations) | ||
| 85 | pub unsafe fn new(id: u8) -> Self { | ||
| 86 | Self { id } | ||
| 87 | } | ||
| 88 | |||
| 89 | /// Get the ID of the AlarmHandle. | ||
| 90 | pub fn id(&self) -> u8 { | ||
| 91 | self.id | ||
| 92 | } | ||
| 93 | } | ||
| 94 | |||
| 95 | /// Time driver | 64 | /// Time driver |
| 96 | pub trait Driver: Send + Sync + 'static { | 65 | pub trait Driver: Send + Sync + 'static { |
| 97 | /// Return the current timestamp in ticks. | 66 | /// Return the current timestamp in ticks. |
| @@ -105,76 +74,10 @@ pub trait Driver: Send + Sync + 'static { | |||
| 105 | /// you MUST extend them to 64-bit, for example by counting overflows in software, | 74 | /// you MUST extend them to 64-bit, for example by counting overflows in software, |
| 106 | /// or chaining multiple timers together. | 75 | /// or chaining multiple timers together. |
| 107 | fn now(&self) -> u64; | 76 | fn now(&self) -> u64; |
| 108 | |||
| 109 | /// Try allocating an alarm handle. Returns None if no alarms left. | ||
| 110 | /// Initially the alarm has no callback set, and a null `ctx` pointer. | ||
| 111 | /// | ||
| 112 | /// The allocated alarm is a reusable resource and can be used multiple times. | ||
| 113 | /// Once the alarm has fired, it remains allocated and can be set again without needing | ||
| 114 | /// to be reallocated. | ||
| 115 | /// | ||
| 116 | /// # Safety | ||
| 117 | /// It is UB to make the alarm fire before setting a callback. | ||
| 118 | unsafe fn allocate_alarm(&self) -> Option<AlarmHandle>; | ||
| 119 | |||
| 120 | /// Set the callback function to be called when the alarm triggers. | ||
| 121 | /// The callback may be called from any context (interrupt or thread mode). | ||
| 122 | /// | ||
| 123 | /// The callback is maintained after the alarm has fired. Callers do not need | ||
| 124 | /// to set a callback again before setting another alarm, unless they want to | ||
| 125 | /// change the callback function or context. | ||
| 126 | fn set_alarm_callback(&self, alarm: AlarmHandle, callback: fn(*mut ()), ctx: *mut ()); | ||
| 127 | |||
| 128 | /// Set an alarm at the given timestamp. | ||
| 129 | /// | ||
| 130 | /// ## Behavior | ||
| 131 | /// | ||
| 132 | /// If `timestamp` is in the future, `set_alarm` schedules calling the callback function | ||
| 133 | /// at that time, and returns `true`. | ||
| 134 | /// | ||
| 135 | /// If `timestamp` is in the past, `set_alarm` has two allowed behaviors. Implementations can pick whether to: | ||
| 136 | /// | ||
| 137 | /// - Schedule calling the callback function "immediately", as if the requested timestamp was "now+epsilon" and return `true`, or | ||
| 138 | /// - Not schedule the callback, and return `false`. | ||
| 139 | /// | ||
| 140 | /// Callers must ensure to behave correctly with either behavior. | ||
| 141 | /// | ||
| 142 | /// When callback is called, it is guaranteed that `now()` will return a value greater than or equal to `timestamp`. | ||
| 143 | /// | ||
| 144 | /// ## Reentrancy | ||
| 145 | /// | ||
| 146 | /// Calling the callback from `set_alarm` synchronously is not allowed. If the implementation chooses the first option above, | ||
| 147 | /// it must still call the callback from another context (i.e. an interrupt handler or background thread), it's not allowed | ||
| 148 | /// to call it synchronously in the context `set_alarm` is running. | ||
| 149 | /// | ||
| 150 | /// The reason for the above is callers are explicitly permitted to do both of: | ||
| 151 | /// - Lock a mutex in the alarm callback. | ||
| 152 | /// - Call `set_alarm` while having that mutex locked. | ||
| 153 | /// | ||
| 154 | /// If `set_alarm` called the callback synchronously, it'd cause a deadlock or panic because it'd cause the | ||
| 155 | /// mutex to be locked twice reentrantly in the same context. | ||
| 156 | /// | ||
| 157 | /// ## Overwriting alarms | ||
| 158 | /// | ||
| 159 | /// Only one alarm can be active at a time for each `AlarmHandle`. This overwrites any previously-set alarm if any. | ||
| 160 | /// | ||
| 161 | /// ## Unsetting the alarm | ||
| 162 | /// | ||
| 163 | /// There is no `unset_alarm` API. Instead, callers can call `set_alarm` with `timestamp` set to `u64::MAX`. | ||
| 164 | /// | ||
| 165 | /// This allows for more efficient implementations, since they don't need to distinguish between the "alarm set" and | ||
| 166 | /// "alarm not set" cases, thanks to the fact "Alarm set for u64::MAX" is effectively equivalent for "alarm not set". | ||
| 167 | /// | ||
| 168 | /// This means implementations need to be careful to avoid timestamp overflows. The recommendation is to make `timestamp` | ||
| 169 | /// be in the same units as hardware ticks to avoid any conversions, which makes avoiding overflow easier. | ||
| 170 | fn set_alarm(&self, alarm: AlarmHandle, timestamp: u64) -> bool; | ||
| 171 | } | 77 | } |
| 172 | 78 | ||
| 173 | extern "Rust" { | 79 | extern "Rust" { |
| 174 | fn _embassy_time_now() -> u64; | 80 | fn _embassy_time_now() -> u64; |
| 175 | fn _embassy_time_allocate_alarm() -> Option<AlarmHandle>; | ||
| 176 | fn _embassy_time_set_alarm_callback(alarm: AlarmHandle, callback: fn(*mut ()), ctx: *mut ()); | ||
| 177 | fn _embassy_time_set_alarm(alarm: AlarmHandle, timestamp: u64) -> bool; | ||
| 178 | } | 81 | } |
| 179 | 82 | ||
| 180 | /// See [`Driver::now`] | 83 | /// See [`Driver::now`] |
| @@ -182,23 +85,6 @@ pub fn now() -> u64 { | |||
| 182 | unsafe { _embassy_time_now() } | 85 | unsafe { _embassy_time_now() } |
| 183 | } | 86 | } |
| 184 | 87 | ||
| 185 | /// See [`Driver::allocate_alarm`] | ||
| 186 | /// | ||
| 187 | /// Safety: it is UB to make the alarm fire before setting a callback. | ||
| 188 | pub unsafe fn allocate_alarm() -> Option<AlarmHandle> { | ||
| 189 | _embassy_time_allocate_alarm() | ||
| 190 | } | ||
| 191 | |||
| 192 | /// See [`Driver::set_alarm_callback`] | ||
| 193 | pub fn set_alarm_callback(alarm: AlarmHandle, callback: fn(*mut ()), ctx: *mut ()) { | ||
| 194 | unsafe { _embassy_time_set_alarm_callback(alarm, callback, ctx) } | ||
| 195 | } | ||
| 196 | |||
| 197 | /// See [`Driver::set_alarm`] | ||
| 198 | pub fn set_alarm(alarm: AlarmHandle, timestamp: u64) -> bool { | ||
| 199 | unsafe { _embassy_time_set_alarm(alarm, timestamp) } | ||
| 200 | } | ||
| 201 | |||
| 202 | /// Set the time Driver implementation. | 88 | /// Set the time Driver implementation. |
| 203 | /// | 89 | /// |
| 204 | /// See the module documentation for an example. | 90 | /// See the module documentation for an example. |
| @@ -211,20 +97,5 @@ macro_rules! time_driver_impl { | |||
| 211 | fn _embassy_time_now() -> u64 { | 97 | fn _embassy_time_now() -> u64 { |
| 212 | <$t as $crate::Driver>::now(&$name) | 98 | <$t as $crate::Driver>::now(&$name) |
| 213 | } | 99 | } |
| 214 | |||
| 215 | #[no_mangle] | ||
| 216 | unsafe fn _embassy_time_allocate_alarm() -> Option<$crate::AlarmHandle> { | ||
| 217 | <$t as $crate::Driver>::allocate_alarm(&$name) | ||
| 218 | } | ||
| 219 | |||
| 220 | #[no_mangle] | ||
| 221 | fn _embassy_time_set_alarm_callback(alarm: $crate::AlarmHandle, callback: fn(*mut ()), ctx: *mut ()) { | ||
| 222 | <$t as $crate::Driver>::set_alarm_callback(&$name, alarm, callback, ctx) | ||
| 223 | } | ||
| 224 | |||
| 225 | #[no_mangle] | ||
| 226 | fn _embassy_time_set_alarm(alarm: $crate::AlarmHandle, timestamp: u64) -> bool { | ||
| 227 | <$t as $crate::Driver>::set_alarm(&$name, alarm, timestamp) | ||
| 228 | } | ||
| 229 | }; | 100 | }; |
| 230 | } | 101 | } |
diff --git a/embassy-time-queue-driver/Cargo.toml b/embassy-time-queue-driver/Cargo.toml index 9ce9d79bb..599041a3f 100644 --- a/embassy-time-queue-driver/Cargo.toml +++ b/embassy-time-queue-driver/Cargo.toml | |||
| @@ -20,6 +20,39 @@ categories = [ | |||
| 20 | # This is especially common when mixing crates from crates.io and git. | 20 | # This is especially common when mixing crates from crates.io and git. |
| 21 | links = "embassy-time-queue" | 21 | links = "embassy-time-queue" |
| 22 | 22 | ||
| 23 | [dependencies] | ||
| 24 | critical-section = "1.2.0" | ||
| 25 | heapless = "0.8" | ||
| 26 | embassy-executor = { version = "0.6.3", path = "../embassy-executor", optional = true } | ||
| 27 | embassy-time-driver = { version = "0.1.0", path = "../embassy-time-driver" } | ||
| 28 | |||
| 29 | [features] | ||
| 30 | #! ### Generic Queue | ||
| 31 | |||
| 32 | ## Use the executor-integrated `embassy-time` timer queue. The timer items are stored inside | ||
| 33 | ## the task headers, so you do not need to set a capacity for the queue. | ||
| 34 | ## To use this you must have a time driver provided. | ||
| 35 | ## | ||
| 36 | ## If this feature is not enabled, a generic queue is available with a configurable capacity. | ||
| 37 | integrated-timers = ["embassy-executor/integrated-timers"] | ||
| 38 | |||
| 39 | #! The following features set how many timers are used for the generic queue. At most one | ||
| 40 | #! `generic-queue-*` feature can be enabled. If none is enabled, a default of 64 timers is used. | ||
| 41 | #! | ||
| 42 | #! When using embassy-time from libraries, you should *not* enable any `generic-queue-*` feature, to allow the | ||
| 43 | #! end user to pick. | ||
| 44 | |||
| 45 | ## Generic Queue with 8 timers | ||
| 46 | generic-queue-8 = [] | ||
| 47 | ## Generic Queue with 16 timers | ||
| 48 | generic-queue-16 = [] | ||
| 49 | ## Generic Queue with 32 timers | ||
| 50 | generic-queue-32 = [] | ||
| 51 | ## Generic Queue with 64 timers | ||
| 52 | generic-queue-64 = [] | ||
| 53 | ## Generic Queue with 128 timers | ||
| 54 | generic-queue-128 = [] | ||
| 55 | |||
| 23 | [package.metadata.embassy_docs] | 56 | [package.metadata.embassy_docs] |
| 24 | src_base = "https://github.com/embassy-rs/embassy/blob/embassy-time-queue-driver-v$VERSION/embassy-time-queue-driver/src/" | 57 | src_base = "https://github.com/embassy-rs/embassy/blob/embassy-time-queue-driver-v$VERSION/embassy-time-queue-driver/src/" |
| 25 | src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-time-queue-driver/src/" | 58 | src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-time-queue-driver/src/" |
diff --git a/embassy-time-queue-driver/src/lib.rs b/embassy-time-queue-driver/src/lib.rs index 50736e8c7..c5e989854 100644 --- a/embassy-time-queue-driver/src/lib.rs +++ b/embassy-time-queue-driver/src/lib.rs | |||
| @@ -6,7 +6,29 @@ | |||
| 6 | //! | 6 | //! |
| 7 | //! - Define a struct `MyTimerQueue` | 7 | //! - Define a struct `MyTimerQueue` |
| 8 | //! - Implement [`TimerQueue`] for it | 8 | //! - Implement [`TimerQueue`] for it |
| 9 | //! - Register it as the global timer queue with [`timer_queue_impl`](crate::timer_queue_impl). | 9 | //! - Register it as the global timer queue with [`timer_queue_impl`]. |
| 10 | //! - Ensure that you process the timer queue when `schedule_wake` is due. This usually involves | ||
| 11 | //! waking expired tasks, finding the next expiration time and setting an alarm. | ||
| 12 | //! | ||
| 13 | //! If a single global timer queue is sufficient for you, you can use the | ||
| 14 | //! [`GlobalTimerQueue`] type, which is a wrapper around a global timer queue | ||
| 15 | //! protected by a critical section. | ||
| 16 | //! | ||
| 17 | //! ``` | ||
| 18 | //! use embassy_time_queue_driver::GlobalTimerQueue; | ||
| 19 | //! embassy_time_queue_driver::timer_queue_impl!( | ||
| 20 | //! static TIMER_QUEUE_DRIVER: GlobalTimerQueue | ||
| 21 | //! = GlobalTimerQueue::new(|next_expiration| todo!("Set an alarm")) | ||
| 22 | //! ); | ||
| 23 | //! ``` | ||
| 24 | //! | ||
| 25 | //! You can also use the `queue_generic` or the `embassy_executor::raw::timer_queue` modules to | ||
| 26 | //! implement your own timer queue. These modules contain queue implementations which you can wrap | ||
| 27 | //! and tailor to your needs. | ||
| 28 | //! | ||
| 29 | //! If you are providing an embassy-executor implementation besides a timer queue, you can choose to | ||
| 30 | //! expose the `integrated-timers` feature in your implementation. This feature stores timer items | ||
| 31 | //! in the tasks themselves, so you don't need a fixed-size queue or dynamic memory allocation. | ||
| 10 | //! | 32 | //! |
| 11 | //! ## Example | 33 | //! ## Example |
| 12 | //! | 34 | //! |
| @@ -14,7 +36,7 @@ | |||
| 14 | //! use core::task::Waker; | 36 | //! use core::task::Waker; |
| 15 | //! | 37 | //! |
| 16 | //! use embassy_time::Instant; | 38 | //! use embassy_time::Instant; |
| 17 | //! use embassy_time::queue::{TimerQueue}; | 39 | //! use embassy_time::queue::TimerQueue; |
| 18 | //! | 40 | //! |
| 19 | //! struct MyTimerQueue{}; // not public! | 41 | //! struct MyTimerQueue{}; // not public! |
| 20 | //! | 42 | //! |
| @@ -26,11 +48,18 @@ | |||
| 26 | //! | 48 | //! |
| 27 | //! embassy_time_queue_driver::timer_queue_impl!(static QUEUE: MyTimerQueue = MyTimerQueue{}); | 49 | //! embassy_time_queue_driver::timer_queue_impl!(static QUEUE: MyTimerQueue = MyTimerQueue{}); |
| 28 | //! ``` | 50 | //! ``` |
| 51 | |||
| 52 | pub mod queue_generic; | ||
| 53 | |||
| 54 | use core::cell::RefCell; | ||
| 29 | use core::task::Waker; | 55 | use core::task::Waker; |
| 30 | 56 | ||
| 57 | use critical_section::Mutex; | ||
| 58 | |||
| 31 | /// Timer queue | 59 | /// Timer queue |
| 32 | pub trait TimerQueue { | 60 | pub trait TimerQueue { |
| 33 | /// Schedules a waker in the queue to be awoken at moment `at`. | 61 | /// Schedules a waker in the queue to be awoken at moment `at`. |
| 62 | /// | ||
| 34 | /// If this moment is in the past, the waker might be awoken immediately. | 63 | /// If this moment is in the past, the waker might be awoken immediately. |
| 35 | fn schedule_wake(&'static self, at: u64, waker: &Waker); | 64 | fn schedule_wake(&'static self, at: u64, waker: &Waker); |
| 36 | } | 65 | } |
| @@ -58,3 +87,106 @@ macro_rules! timer_queue_impl { | |||
| 58 | } | 87 | } |
| 59 | }; | 88 | }; |
| 60 | } | 89 | } |
| 90 | |||
| 91 | #[cfg(feature = "integrated-timers")] | ||
| 92 | type InnerQueue = embassy_executor::raw::timer_queue::TimerQueue; | ||
| 93 | |||
| 94 | #[cfg(not(feature = "integrated-timers"))] | ||
| 95 | type InnerQueue = queue_generic::Queue; | ||
| 96 | |||
| 97 | /// A timer queue implementation that can be used as a global timer queue. | ||
| 98 | /// | ||
| 99 | /// This implementation is not thread-safe, and should be protected by a mutex of some sort. | ||
| 100 | pub struct GenericTimerQueue<F: Fn(u64) -> bool> { | ||
| 101 | queue: InnerQueue, | ||
| 102 | set_alarm: F, | ||
| 103 | } | ||
| 104 | |||
| 105 | impl<F: Fn(u64) -> bool> GenericTimerQueue<F> { | ||
| 106 | /// Creates a new timer queue. | ||
| 107 | /// | ||
| 108 | /// `set_alarm` is a function that should set the next alarm time. The function should | ||
| 109 | /// return `true` if the alarm was set, and `false` if the alarm was in the past. | ||
| 110 | pub const fn new(set_alarm: F) -> Self { | ||
| 111 | Self { | ||
| 112 | queue: InnerQueue::new(), | ||
| 113 | set_alarm, | ||
| 114 | } | ||
| 115 | } | ||
| 116 | |||
| 117 | /// Schedules a task to run at a specific time, and returns whether any changes were made. | ||
| 118 | pub fn schedule_wake(&mut self, at: u64, waker: &core::task::Waker) { | ||
| 119 | #[cfg(feature = "integrated-timers")] | ||
| 120 | let waker = embassy_executor::raw::task_from_waker(waker); | ||
| 121 | |||
| 122 | if self.queue.schedule_wake(at, waker) { | ||
| 123 | self.dispatch() | ||
| 124 | } | ||
| 125 | } | ||
| 126 | |||
| 127 | /// Dequeues expired timers and returns the next alarm time. | ||
| 128 | pub fn next_expiration(&mut self, now: u64) -> u64 { | ||
| 129 | self.queue.next_expiration(now) | ||
| 130 | } | ||
| 131 | |||
| 132 | /// Handle the alarm. | ||
| 133 | /// | ||
| 134 | /// Call this function when the next alarm is due. | ||
| 135 | pub fn dispatch(&mut self) { | ||
| 136 | let mut next_expiration = self.next_expiration(embassy_time_driver::now()); | ||
| 137 | |||
| 138 | while !(self.set_alarm)(next_expiration) { | ||
| 139 | // next_expiration is in the past, dequeue and find a new expiration | ||
| 140 | next_expiration = self.next_expiration(next_expiration); | ||
| 141 | } | ||
| 142 | } | ||
| 143 | } | ||
| 144 | |||
| 145 | /// A [`GenericTimerQueue`] protected by a critical section. Directly useable as a [`TimerQueue`]. | ||
| 146 | pub struct GlobalTimerQueue { | ||
| 147 | inner: Mutex<RefCell<GenericTimerQueue<fn(u64) -> bool>>>, | ||
| 148 | } | ||
| 149 | |||
| 150 | impl GlobalTimerQueue { | ||
| 151 | /// Creates a new timer queue. | ||
| 152 | /// | ||
| 153 | /// `set_alarm` is a function that should set the next alarm time. The function should | ||
| 154 | /// return `true` if the alarm was set, and `false` if the alarm was in the past. | ||
| 155 | pub const fn new(set_alarm: fn(u64) -> bool) -> Self { | ||
| 156 | Self { | ||
| 157 | inner: Mutex::new(RefCell::new(GenericTimerQueue::new(set_alarm))), | ||
| 158 | } | ||
| 159 | } | ||
| 160 | |||
| 161 | /// Schedules a task to run at a specific time, and returns whether any changes were made. | ||
| 162 | pub fn schedule_wake(&self, at: u64, waker: &core::task::Waker) { | ||
| 163 | critical_section::with(|cs| { | ||
| 164 | let mut inner = self.inner.borrow_ref_mut(cs); | ||
| 165 | inner.schedule_wake(at, waker); | ||
| 166 | }); | ||
| 167 | } | ||
| 168 | |||
| 169 | /// Dequeues expired timers and returns the next alarm time. | ||
| 170 | pub fn next_expiration(&self, now: u64) -> u64 { | ||
| 171 | critical_section::with(|cs| { | ||
| 172 | let mut inner = self.inner.borrow_ref_mut(cs); | ||
| 173 | inner.next_expiration(now) | ||
| 174 | }) | ||
| 175 | } | ||
| 176 | |||
| 177 | /// Handle the alarm. | ||
| 178 | /// | ||
| 179 | /// Call this function when the next alarm is due. | ||
| 180 | pub fn dispatch(&self) { | ||
| 181 | critical_section::with(|cs| { | ||
| 182 | let mut inner = self.inner.borrow_ref_mut(cs); | ||
| 183 | inner.dispatch() | ||
| 184 | }) | ||
| 185 | } | ||
| 186 | } | ||
| 187 | |||
| 188 | impl TimerQueue for GlobalTimerQueue { | ||
| 189 | fn schedule_wake(&'static self, at: u64, waker: &Waker) { | ||
| 190 | GlobalTimerQueue::schedule_wake(self, at, waker) | ||
| 191 | } | ||
| 192 | } | ||
diff --git a/embassy-time-queue-driver/src/queue_generic.rs b/embassy-time-queue-driver/src/queue_generic.rs new file mode 100644 index 000000000..232035bc6 --- /dev/null +++ b/embassy-time-queue-driver/src/queue_generic.rs | |||
| @@ -0,0 +1,146 @@ | |||
| 1 | //! Generic timer queue implementations. | ||
| 2 | //! | ||
| 3 | //! Time queue drivers may use this to simplify their implementation. | ||
| 4 | |||
| 5 | use core::cmp::{min, Ordering}; | ||
| 6 | use core::task::Waker; | ||
| 7 | |||
| 8 | use heapless::Vec; | ||
| 9 | |||
| 10 | #[derive(Debug)] | ||
| 11 | struct Timer { | ||
| 12 | at: u64, | ||
| 13 | waker: Waker, | ||
| 14 | } | ||
| 15 | |||
| 16 | impl PartialEq for Timer { | ||
| 17 | fn eq(&self, other: &Self) -> bool { | ||
| 18 | self.at == other.at | ||
| 19 | } | ||
| 20 | } | ||
| 21 | |||
| 22 | impl Eq for Timer {} | ||
| 23 | |||
| 24 | impl PartialOrd for Timer { | ||
| 25 | fn partial_cmp(&self, other: &Self) -> Option<Ordering> { | ||
| 26 | self.at.partial_cmp(&other.at) | ||
| 27 | } | ||
| 28 | } | ||
| 29 | |||
| 30 | impl Ord for Timer { | ||
| 31 | fn cmp(&self, other: &Self) -> Ordering { | ||
| 32 | self.at.cmp(&other.at) | ||
| 33 | } | ||
| 34 | } | ||
| 35 | |||
| 36 | /// A timer queue with a pre-determined capacity. | ||
| 37 | pub struct ConstGenericQueue<const QUEUE_SIZE: usize> { | ||
| 38 | queue: Vec<Timer, QUEUE_SIZE>, | ||
| 39 | } | ||
| 40 | |||
| 41 | impl<const QUEUE_SIZE: usize> ConstGenericQueue<QUEUE_SIZE> { | ||
| 42 | /// Creates a new timer queue. | ||
| 43 | pub const fn new() -> Self { | ||
| 44 | Self { queue: Vec::new() } | ||
| 45 | } | ||
| 46 | |||
| 47 | /// Schedules a task to run at a specific time, and returns whether any changes were made. | ||
| 48 | /// | ||
| 49 | /// If this function returns `true`, the called should find the next expiration time and set | ||
| 50 | /// a new alarm for that time. | ||
| 51 | pub fn schedule_wake(&mut self, at: u64, waker: &Waker) -> bool { | ||
| 52 | self.queue | ||
| 53 | .iter_mut() | ||
| 54 | .find(|timer| timer.waker.will_wake(waker)) | ||
| 55 | .map(|timer| { | ||
| 56 | if timer.at > at { | ||
| 57 | timer.at = at; | ||
| 58 | true | ||
| 59 | } else { | ||
| 60 | false | ||
| 61 | } | ||
| 62 | }) | ||
| 63 | .unwrap_or_else(|| { | ||
| 64 | let mut timer = Timer { | ||
| 65 | waker: waker.clone(), | ||
| 66 | at, | ||
| 67 | }; | ||
| 68 | |||
| 69 | loop { | ||
| 70 | match self.queue.push(timer) { | ||
| 71 | Ok(()) => break, | ||
| 72 | Err(e) => timer = e, | ||
| 73 | } | ||
| 74 | |||
| 75 | self.queue.pop().unwrap().waker.wake(); | ||
| 76 | } | ||
| 77 | |||
| 78 | true | ||
| 79 | }) | ||
| 80 | } | ||
| 81 | |||
| 82 | /// Dequeues expired timers and returns the next alarm time. | ||
| 83 | pub fn next_expiration(&mut self, now: u64) -> u64 { | ||
| 84 | let mut next_alarm = u64::MAX; | ||
| 85 | |||
| 86 | let mut i = 0; | ||
| 87 | while i < self.queue.len() { | ||
| 88 | let timer = &self.queue[i]; | ||
| 89 | if timer.at <= now { | ||
| 90 | let timer = self.queue.swap_remove(i); | ||
| 91 | timer.waker.wake(); | ||
| 92 | } else { | ||
| 93 | next_alarm = min(next_alarm, timer.at); | ||
| 94 | i += 1; | ||
| 95 | } | ||
| 96 | } | ||
| 97 | |||
| 98 | next_alarm | ||
| 99 | } | ||
| 100 | } | ||
| 101 | |||
| 102 | #[cfg(feature = "generic-queue-8")] | ||
| 103 | const QUEUE_SIZE: usize = 8; | ||
| 104 | #[cfg(feature = "generic-queue-16")] | ||
| 105 | const QUEUE_SIZE: usize = 16; | ||
| 106 | #[cfg(feature = "generic-queue-32")] | ||
| 107 | const QUEUE_SIZE: usize = 32; | ||
| 108 | #[cfg(feature = "generic-queue-64")] | ||
| 109 | const QUEUE_SIZE: usize = 64; | ||
| 110 | #[cfg(feature = "generic-queue-128")] | ||
| 111 | const QUEUE_SIZE: usize = 128; | ||
| 112 | #[cfg(not(any( | ||
| 113 | feature = "generic-queue-8", | ||
| 114 | feature = "generic-queue-16", | ||
| 115 | feature = "generic-queue-32", | ||
| 116 | feature = "generic-queue-64", | ||
| 117 | feature = "generic-queue-128" | ||
| 118 | )))] | ||
| 119 | const QUEUE_SIZE: usize = 64; | ||
| 120 | |||
| 121 | /// A timer queue with a pre-determined capacity. | ||
| 122 | pub struct Queue { | ||
| 123 | queue: ConstGenericQueue<QUEUE_SIZE>, | ||
| 124 | } | ||
| 125 | |||
| 126 | impl Queue { | ||
| 127 | /// Creates a new timer queue. | ||
| 128 | pub const fn new() -> Self { | ||
| 129 | Self { | ||
| 130 | queue: ConstGenericQueue::new(), | ||
| 131 | } | ||
| 132 | } | ||
| 133 | |||
| 134 | /// Schedules a task to run at a specific time, and returns whether any changes were made. | ||
| 135 | /// | ||
| 136 | /// If this function returns `true`, the called should find the next expiration time and set | ||
| 137 | /// a new alarm for that time. | ||
| 138 | pub fn schedule_wake(&mut self, at: u64, waker: &Waker) -> bool { | ||
| 139 | self.queue.schedule_wake(at, waker) | ||
| 140 | } | ||
| 141 | |||
| 142 | /// Dequeues expired timers and returns the next alarm time. | ||
| 143 | pub fn next_expiration(&mut self, now: u64) -> u64 { | ||
| 144 | self.queue.next_expiration(now) | ||
| 145 | } | ||
| 146 | } | ||
diff --git a/embassy-time/Cargo.toml b/embassy-time/Cargo.toml index 8c7de9840..e3074119f 100644 --- a/embassy-time/Cargo.toml +++ b/embassy-time/Cargo.toml | |||
| @@ -42,29 +42,6 @@ defmt-timestamp-uptime-tus = ["defmt"] | |||
| 42 | ## Create a `MockDriver` that can be manually advanced for testing purposes. | 42 | ## Create a `MockDriver` that can be manually advanced for testing purposes. |
| 43 | mock-driver = ["tick-hz-1_000_000"] | 43 | mock-driver = ["tick-hz-1_000_000"] |
| 44 | 44 | ||
| 45 | #! ### Generic Queue | ||
| 46 | |||
| 47 | ## Create a global, generic queue that can be used with any executor. | ||
| 48 | ## To use this you must have a time driver provided. | ||
| 49 | generic-queue = [] | ||
| 50 | |||
| 51 | #! The following features set how many timers are used for the generic queue. At most one | ||
| 52 | #! `generic-queue-*` feature can be enabled. If none is enabled, a default of 64 timers is used. | ||
| 53 | #! | ||
| 54 | #! When using embassy-time from libraries, you should *not* enable any `generic-queue-*` feature, to allow the | ||
| 55 | #! end user to pick. | ||
| 56 | |||
| 57 | ## Generic Queue with 8 timers | ||
| 58 | generic-queue-8 = ["generic-queue"] | ||
| 59 | ## Generic Queue with 16 timers | ||
| 60 | generic-queue-16 = ["generic-queue"] | ||
| 61 | ## Generic Queue with 32 timers | ||
| 62 | generic-queue-32 = ["generic-queue"] | ||
| 63 | ## Generic Queue with 64 timers | ||
| 64 | generic-queue-64 = ["generic-queue"] | ||
| 65 | ## Generic Queue with 128 timers | ||
| 66 | generic-queue-128 = ["generic-queue"] | ||
| 67 | |||
| 68 | #! ### Tick Rate | 45 | #! ### Tick Rate |
| 69 | #! | 46 | #! |
| 70 | #! At most 1 `tick-*` feature can be enabled. If none is enabled, a default of 1MHz is used. | 47 | #! At most 1 `tick-*` feature can be enabled. If none is enabled, a default of 1MHz is used. |
| @@ -419,7 +396,6 @@ embedded-hal-async = { version = "1.0" } | |||
| 419 | futures-util = { version = "0.3.17", default-features = false } | 396 | futures-util = { version = "0.3.17", default-features = false } |
| 420 | critical-section = "1.1" | 397 | critical-section = "1.1" |
| 421 | cfg-if = "1.0.0" | 398 | cfg-if = "1.0.0" |
| 422 | heapless = "0.8" | ||
| 423 | 399 | ||
| 424 | document-features = "0.2.7" | 400 | document-features = "0.2.7" |
| 425 | 401 | ||
diff --git a/embassy-time/src/driver_mock.rs b/embassy-time/src/driver_mock.rs index 8587f9172..829eb0437 100644 --- a/embassy-time/src/driver_mock.rs +++ b/embassy-time/src/driver_mock.rs | |||
| @@ -1,7 +1,7 @@ | |||
| 1 | use core::cell::RefCell; | 1 | use core::cell::RefCell; |
| 2 | 2 | ||
| 3 | use critical_section::Mutex as CsMutex; | 3 | use critical_section::Mutex as CsMutex; |
| 4 | use embassy_time_driver::{AlarmHandle, Driver}; | 4 | use embassy_time_driver::Driver; |
| 5 | 5 | ||
| 6 | use crate::{Duration, Instant}; | 6 | use crate::{Duration, Instant}; |
| 7 | 7 | ||
| @@ -60,15 +60,13 @@ impl MockDriver { | |||
| 60 | 60 | ||
| 61 | let now = inner.now.as_ticks(); | 61 | let now = inner.now.as_ticks(); |
| 62 | 62 | ||
| 63 | inner | 63 | if inner.alarm.timestamp <= now { |
| 64 | .alarm | 64 | inner.alarm.timestamp = u64::MAX; |
| 65 | .as_mut() | ||
| 66 | .filter(|alarm| alarm.timestamp <= now) | ||
| 67 | .map(|alarm| { | ||
| 68 | alarm.timestamp = u64::MAX; | ||
| 69 | 65 | ||
| 70 | (alarm.callback, alarm.ctx) | 66 | Some((inner.alarm.callback, inner.alarm.ctx)) |
| 71 | }) | 67 | } else { |
| 68 | None | ||
| 69 | } | ||
| 72 | }) | 70 | }) |
| 73 | }; | 71 | }; |
| 74 | 72 | ||
| @@ -76,68 +74,48 @@ impl MockDriver { | |||
| 76 | (callback)(ctx); | 74 | (callback)(ctx); |
| 77 | } | 75 | } |
| 78 | } | 76 | } |
| 79 | } | ||
| 80 | |||
| 81 | impl Driver for MockDriver { | ||
| 82 | fn now(&self) -> u64 { | ||
| 83 | critical_section::with(|cs| self.0.borrow_ref(cs).now).as_ticks() | ||
| 84 | } | ||
| 85 | |||
| 86 | unsafe fn allocate_alarm(&self) -> Option<AlarmHandle> { | ||
| 87 | critical_section::with(|cs| { | ||
| 88 | let mut inner = self.0.borrow_ref_mut(cs); | ||
| 89 | |||
| 90 | if inner.alarm.is_some() { | ||
| 91 | None | ||
| 92 | } else { | ||
| 93 | inner.alarm.replace(AlarmState::new()); | ||
| 94 | |||
| 95 | Some(AlarmHandle::new(0)) | ||
| 96 | } | ||
| 97 | }) | ||
| 98 | } | ||
| 99 | 77 | ||
| 100 | fn set_alarm_callback(&self, _alarm: AlarmHandle, callback: fn(*mut ()), ctx: *mut ()) { | 78 | /// Configures a callback to be called when the alarm fires. |
| 79 | pub fn set_alarm_callback(&self, callback: fn(*mut ()), ctx: *mut ()) { | ||
| 101 | critical_section::with(|cs| { | 80 | critical_section::with(|cs| { |
| 102 | let mut inner = self.0.borrow_ref_mut(cs); | 81 | let mut inner = self.0.borrow_ref_mut(cs); |
| 103 | 82 | ||
| 104 | let Some(alarm) = inner.alarm.as_mut() else { | 83 | inner.alarm.callback = callback; |
| 105 | panic!("Alarm not allocated"); | 84 | inner.alarm.ctx = ctx; |
| 106 | }; | ||
| 107 | |||
| 108 | alarm.callback = callback; | ||
| 109 | alarm.ctx = ctx; | ||
| 110 | }); | 85 | }); |
| 111 | } | 86 | } |
| 112 | 87 | ||
| 113 | fn set_alarm(&self, _alarm: AlarmHandle, timestamp: u64) -> bool { | 88 | /// Sets the alarm to fire at the specified timestamp. |
| 89 | pub fn set_alarm(&self, timestamp: u64) -> bool { | ||
| 114 | critical_section::with(|cs| { | 90 | critical_section::with(|cs| { |
| 115 | let mut inner = self.0.borrow_ref_mut(cs); | 91 | let mut inner = self.0.borrow_ref_mut(cs); |
| 116 | 92 | ||
| 117 | if timestamp <= inner.now.as_ticks() { | 93 | if timestamp <= inner.now.as_ticks() { |
| 118 | false | 94 | false |
| 119 | } else { | 95 | } else { |
| 120 | let Some(alarm) = inner.alarm.as_mut() else { | 96 | inner.alarm.timestamp = timestamp; |
| 121 | panic!("Alarm not allocated"); | ||
| 122 | }; | ||
| 123 | |||
| 124 | alarm.timestamp = timestamp; | ||
| 125 | true | 97 | true |
| 126 | } | 98 | } |
| 127 | }) | 99 | }) |
| 128 | } | 100 | } |
| 129 | } | 101 | } |
| 130 | 102 | ||
| 103 | impl Driver for MockDriver { | ||
| 104 | fn now(&self) -> u64 { | ||
| 105 | critical_section::with(|cs| self.0.borrow_ref(cs).now).as_ticks() | ||
| 106 | } | ||
| 107 | } | ||
| 108 | |||
| 131 | struct InnerMockDriver { | 109 | struct InnerMockDriver { |
| 132 | now: Instant, | 110 | now: Instant, |
| 133 | alarm: Option<AlarmState>, | 111 | alarm: AlarmState, |
| 134 | } | 112 | } |
| 135 | 113 | ||
| 136 | impl InnerMockDriver { | 114 | impl InnerMockDriver { |
| 137 | const fn new() -> Self { | 115 | const fn new() -> Self { |
| 138 | Self { | 116 | Self { |
| 139 | now: Instant::from_ticks(0), | 117 | now: Instant::from_ticks(0), |
| 140 | alarm: None, | 118 | alarm: AlarmState::new(), |
| 141 | } | 119 | } |
| 142 | } | 120 | } |
| 143 | } | 121 | } |
| @@ -189,8 +167,7 @@ mod tests { | |||
| 189 | setup(); | 167 | setup(); |
| 190 | 168 | ||
| 191 | let driver = MockDriver::get(); | 169 | let driver = MockDriver::get(); |
| 192 | let alarm = unsafe { AlarmHandle::new(0) }; | 170 | assert_eq!(false, driver.set_alarm(driver.now())); |
| 193 | assert_eq!(false, driver.set_alarm(alarm, driver.now())); | ||
| 194 | } | 171 | } |
| 195 | 172 | ||
| 196 | #[test] | 173 | #[test] |
| @@ -199,23 +176,11 @@ mod tests { | |||
| 199 | setup(); | 176 | setup(); |
| 200 | 177 | ||
| 201 | let driver = MockDriver::get(); | 178 | let driver = MockDriver::get(); |
| 202 | let alarm = unsafe { driver.allocate_alarm() }.expect("No alarms available"); | ||
| 203 | static mut CALLBACK_CALLED: bool = false; | 179 | static mut CALLBACK_CALLED: bool = false; |
| 204 | let ctx = &mut () as *mut (); | 180 | driver.set_alarm_callback(|_| unsafe { CALLBACK_CALLED = true }, core::ptr::null_mut()); |
| 205 | driver.set_alarm_callback(alarm, |_| unsafe { CALLBACK_CALLED = true }, ctx); | 181 | driver.set_alarm(driver.now() + 1); |
| 206 | driver.set_alarm(alarm, driver.now() + 1); | ||
| 207 | assert_eq!(false, unsafe { CALLBACK_CALLED }); | 182 | assert_eq!(false, unsafe { CALLBACK_CALLED }); |
| 208 | driver.advance(Duration::from_secs(1)); | 183 | driver.advance(Duration::from_secs(1)); |
| 209 | assert_eq!(true, unsafe { CALLBACK_CALLED }); | 184 | assert_eq!(true, unsafe { CALLBACK_CALLED }); |
| 210 | } | 185 | } |
| 211 | |||
| 212 | #[test] | ||
| 213 | #[serial] | ||
| 214 | fn test_allocate_alarm() { | ||
| 215 | setup(); | ||
| 216 | |||
| 217 | let driver = MockDriver::get(); | ||
| 218 | assert!(unsafe { driver.allocate_alarm() }.is_some()); | ||
| 219 | assert!(unsafe { driver.allocate_alarm() }.is_none()); | ||
| 220 | } | ||
| 221 | } | 186 | } |
diff --git a/embassy-time/src/driver_std.rs b/embassy-time/src/driver_std.rs index cbef7aae1..45467f09b 100644 --- a/embassy-time/src/driver_std.rs +++ b/embassy-time/src/driver_std.rs | |||
| @@ -1,53 +1,38 @@ | |||
| 1 | use core::sync::atomic::{AtomicU8, Ordering}; | ||
| 2 | use std::cell::{RefCell, UnsafeCell}; | 1 | use std::cell::{RefCell, UnsafeCell}; |
| 3 | use std::mem::MaybeUninit; | 2 | use std::mem::MaybeUninit; |
| 4 | use std::sync::{Condvar, Mutex, Once}; | 3 | use std::sync::{Condvar, Mutex, Once}; |
| 5 | use std::time::{Duration as StdDuration, Instant as StdInstant}; | 4 | use std::time::{Duration as StdDuration, Instant as StdInstant}; |
| 6 | use std::{mem, ptr, thread}; | 5 | use std::{ptr, thread}; |
| 7 | 6 | ||
| 8 | use critical_section::Mutex as CsMutex; | 7 | use critical_section::Mutex as CsMutex; |
| 9 | use embassy_time_driver::{AlarmHandle, Driver}; | 8 | use embassy_time_driver::Driver; |
| 10 | 9 | use embassy_time_queue_driver::GlobalTimerQueue; | |
| 11 | const ALARM_COUNT: usize = 4; | ||
| 12 | 10 | ||
| 13 | struct AlarmState { | 11 | struct AlarmState { |
| 14 | timestamp: u64, | 12 | timestamp: u64, |
| 15 | |||
| 16 | // This is really a Option<(fn(*mut ()), *mut ())> | ||
| 17 | // but fn pointers aren't allowed in const yet | ||
| 18 | callback: *const (), | ||
| 19 | ctx: *mut (), | ||
| 20 | } | 13 | } |
| 21 | 14 | ||
| 22 | unsafe impl Send for AlarmState {} | 15 | unsafe impl Send for AlarmState {} |
| 23 | 16 | ||
| 24 | impl AlarmState { | 17 | impl AlarmState { |
| 25 | const fn new() -> Self { | 18 | const fn new() -> Self { |
| 26 | Self { | 19 | Self { timestamp: u64::MAX } |
| 27 | timestamp: u64::MAX, | ||
| 28 | callback: ptr::null(), | ||
| 29 | ctx: ptr::null_mut(), | ||
| 30 | } | ||
| 31 | } | 20 | } |
| 32 | } | 21 | } |
| 33 | 22 | ||
| 34 | struct TimeDriver { | 23 | struct TimeDriver { |
| 35 | alarm_count: AtomicU8, | ||
| 36 | |||
| 37 | once: Once, | 24 | once: Once, |
| 38 | // The STD Driver implementation requires the alarms' mutex to be reentrant, which the STD Mutex isn't | 25 | // The STD Driver implementation requires the alarm's mutex to be reentrant, which the STD Mutex isn't |
| 39 | // Fortunately, mutexes based on the `critical-section` crate are reentrant, because the critical sections | 26 | // Fortunately, mutexes based on the `critical-section` crate are reentrant, because the critical sections |
| 40 | // themselves are reentrant | 27 | // themselves are reentrant |
| 41 | alarms: UninitCell<CsMutex<RefCell<[AlarmState; ALARM_COUNT]>>>, | 28 | alarm: UninitCell<CsMutex<RefCell<AlarmState>>>, |
| 42 | zero_instant: UninitCell<StdInstant>, | 29 | zero_instant: UninitCell<StdInstant>, |
| 43 | signaler: UninitCell<Signaler>, | 30 | signaler: UninitCell<Signaler>, |
| 44 | } | 31 | } |
| 45 | 32 | ||
| 46 | embassy_time_driver::time_driver_impl!(static DRIVER: TimeDriver = TimeDriver { | 33 | embassy_time_driver::time_driver_impl!(static DRIVER: TimeDriver = TimeDriver { |
| 47 | alarm_count: AtomicU8::new(0), | ||
| 48 | |||
| 49 | once: Once::new(), | 34 | once: Once::new(), |
| 50 | alarms: UninitCell::uninit(), | 35 | alarm: UninitCell::uninit(), |
| 51 | zero_instant: UninitCell::uninit(), | 36 | zero_instant: UninitCell::uninit(), |
| 52 | signaler: UninitCell::uninit(), | 37 | signaler: UninitCell::uninit(), |
| 53 | }); | 38 | }); |
| @@ -55,8 +40,8 @@ embassy_time_driver::time_driver_impl!(static DRIVER: TimeDriver = TimeDriver { | |||
| 55 | impl TimeDriver { | 40 | impl TimeDriver { |
| 56 | fn init(&self) { | 41 | fn init(&self) { |
| 57 | self.once.call_once(|| unsafe { | 42 | self.once.call_once(|| unsafe { |
| 58 | self.alarms | 43 | self.alarm |
| 59 | .write(CsMutex::new(RefCell::new([const { AlarmState::new() }; ALARM_COUNT]))); | 44 | .write(CsMutex::new(RefCell::new(const { AlarmState::new() }))); |
| 60 | self.zero_instant.write(StdInstant::now()); | 45 | self.zero_instant.write(StdInstant::now()); |
| 61 | self.signaler.write(Signaler::new()); | 46 | self.signaler.write(Signaler::new()); |
| 62 | 47 | ||
| @@ -70,36 +55,13 @@ impl TimeDriver { | |||
| 70 | let now = DRIVER.now(); | 55 | let now = DRIVER.now(); |
| 71 | 56 | ||
| 72 | let next_alarm = critical_section::with(|cs| { | 57 | let next_alarm = critical_section::with(|cs| { |
| 73 | let alarms = unsafe { DRIVER.alarms.as_ref() }.borrow(cs); | 58 | let mut alarm = unsafe { DRIVER.alarm.as_ref() }.borrow_ref_mut(cs); |
| 74 | loop { | 59 | if alarm.timestamp <= now { |
| 75 | let pending = alarms | 60 | alarm.timestamp = u64::MAX; |
| 76 | .borrow_mut() | ||
| 77 | .iter_mut() | ||
| 78 | .find(|alarm| alarm.timestamp <= now) | ||
| 79 | .map(|alarm| { | ||
| 80 | alarm.timestamp = u64::MAX; | ||
| 81 | |||
| 82 | (alarm.callback, alarm.ctx) | ||
| 83 | }); | ||
| 84 | |||
| 85 | if let Some((callback, ctx)) = pending { | ||
| 86 | // safety: | ||
| 87 | // - we can ignore the possiblity of `f` being unset (null) because of the safety contract of `allocate_alarm`. | ||
| 88 | // - other than that we only store valid function pointers into alarm.callback | ||
| 89 | let f: fn(*mut ()) = unsafe { mem::transmute(callback) }; | ||
| 90 | f(ctx); | ||
| 91 | } else { | ||
| 92 | // No alarm due | ||
| 93 | break; | ||
| 94 | } | ||
| 95 | } | ||
| 96 | 61 | ||
| 97 | alarms | 62 | TIMER_QUEUE_DRIVER.dispatch(); |
| 98 | .borrow() | 63 | } |
| 99 | .iter() | 64 | alarm.timestamp |
| 100 | .map(|alarm| alarm.timestamp) | ||
| 101 | .min() | ||
| 102 | .unwrap_or(u64::MAX) | ||
| 103 | }); | 65 | }); |
| 104 | 66 | ||
| 105 | // Ensure we don't overflow | 67 | // Ensure we don't overflow |
| @@ -110,46 +72,11 @@ impl TimeDriver { | |||
| 110 | unsafe { DRIVER.signaler.as_ref() }.wait_until(until); | 72 | unsafe { DRIVER.signaler.as_ref() }.wait_until(until); |
| 111 | } | 73 | } |
| 112 | } | 74 | } |
| 113 | } | ||
| 114 | |||
| 115 | impl Driver for TimeDriver { | ||
| 116 | fn now(&self) -> u64 { | ||
| 117 | self.init(); | ||
| 118 | |||
| 119 | let zero = unsafe { self.zero_instant.read() }; | ||
| 120 | StdInstant::now().duration_since(zero).as_micros() as u64 | ||
| 121 | } | ||
| 122 | |||
| 123 | unsafe fn allocate_alarm(&self) -> Option<AlarmHandle> { | ||
| 124 | let id = self.alarm_count.fetch_update(Ordering::AcqRel, Ordering::Acquire, |x| { | ||
| 125 | if x < ALARM_COUNT as u8 { | ||
| 126 | Some(x + 1) | ||
| 127 | } else { | ||
| 128 | None | ||
| 129 | } | ||
| 130 | }); | ||
| 131 | |||
| 132 | match id { | ||
| 133 | Ok(id) => Some(AlarmHandle::new(id)), | ||
| 134 | Err(_) => None, | ||
| 135 | } | ||
| 136 | } | ||
| 137 | |||
| 138 | fn set_alarm_callback(&self, alarm: AlarmHandle, callback: fn(*mut ()), ctx: *mut ()) { | ||
| 139 | self.init(); | ||
| 140 | critical_section::with(|cs| { | ||
| 141 | let mut alarms = unsafe { self.alarms.as_ref() }.borrow_ref_mut(cs); | ||
| 142 | let alarm = &mut alarms[alarm.id() as usize]; | ||
| 143 | alarm.callback = callback as *const (); | ||
| 144 | alarm.ctx = ctx; | ||
| 145 | }); | ||
| 146 | } | ||
| 147 | 75 | ||
| 148 | fn set_alarm(&self, alarm: AlarmHandle, timestamp: u64) -> bool { | 76 | fn set_alarm(&self, timestamp: u64) -> bool { |
| 149 | self.init(); | 77 | self.init(); |
| 150 | critical_section::with(|cs| { | 78 | critical_section::with(|cs| { |
| 151 | let mut alarms = unsafe { self.alarms.as_ref() }.borrow_ref_mut(cs); | 79 | let mut alarm = unsafe { self.alarm.as_ref() }.borrow_ref_mut(cs); |
| 152 | let alarm = &mut alarms[alarm.id() as usize]; | ||
| 153 | alarm.timestamp = timestamp; | 80 | alarm.timestamp = timestamp; |
| 154 | unsafe { self.signaler.as_ref() }.signal(); | 81 | unsafe { self.signaler.as_ref() }.signal(); |
| 155 | }); | 82 | }); |
| @@ -158,6 +85,15 @@ impl Driver for TimeDriver { | |||
| 158 | } | 85 | } |
| 159 | } | 86 | } |
| 160 | 87 | ||
| 88 | impl Driver for TimeDriver { | ||
| 89 | fn now(&self) -> u64 { | ||
| 90 | self.init(); | ||
| 91 | |||
| 92 | let zero = unsafe { self.zero_instant.read() }; | ||
| 93 | StdInstant::now().duration_since(zero).as_micros() as u64 | ||
| 94 | } | ||
| 95 | } | ||
| 96 | |||
| 161 | struct Signaler { | 97 | struct Signaler { |
| 162 | mutex: Mutex<bool>, | 98 | mutex: Mutex<bool>, |
| 163 | condvar: Condvar, | 99 | condvar: Condvar, |
| @@ -228,3 +164,8 @@ impl<T: Copy> UninitCell<T> { | |||
| 228 | ptr::read(self.as_mut_ptr()) | 164 | ptr::read(self.as_mut_ptr()) |
| 229 | } | 165 | } |
| 230 | } | 166 | } |
| 167 | |||
| 168 | embassy_time_queue_driver::timer_queue_impl!( | ||
| 169 | static TIMER_QUEUE_DRIVER: GlobalTimerQueue | ||
| 170 | = GlobalTimerQueue::new(|next_expiration| DRIVER.set_alarm(next_expiration)) | ||
| 171 | ); | ||
diff --git a/embassy-time/src/driver_wasm.rs b/embassy-time/src/driver_wasm.rs index d65629e49..dcc935fde 100644 --- a/embassy-time/src/driver_wasm.rs +++ b/embassy-time/src/driver_wasm.rs | |||
| @@ -1,28 +1,22 @@ | |||
| 1 | use core::sync::atomic::{AtomicU8, Ordering}; | ||
| 2 | use std::cell::UnsafeCell; | 1 | use std::cell::UnsafeCell; |
| 3 | use std::mem::MaybeUninit; | 2 | use std::mem::MaybeUninit; |
| 4 | use std::ptr; | 3 | use std::ptr; |
| 5 | use std::sync::{Mutex, Once}; | 4 | use std::sync::{Mutex, Once}; |
| 6 | 5 | ||
| 7 | use embassy_time_driver::{AlarmHandle, Driver}; | 6 | use embassy_time_driver::Driver; |
| 7 | use embassy_time_queue_driver::GlobalTimerQueue; | ||
| 8 | use wasm_bindgen::prelude::*; | 8 | use wasm_bindgen::prelude::*; |
| 9 | use wasm_timer::Instant as StdInstant; | 9 | use wasm_timer::Instant as StdInstant; |
| 10 | 10 | ||
| 11 | const ALARM_COUNT: usize = 4; | ||
| 12 | |||
| 13 | struct AlarmState { | 11 | struct AlarmState { |
| 14 | token: Option<f64>, | 12 | token: Option<f64>, |
| 15 | closure: Option<Closure<dyn FnMut() + 'static>>, | ||
| 16 | } | 13 | } |
| 17 | 14 | ||
| 18 | unsafe impl Send for AlarmState {} | 15 | unsafe impl Send for AlarmState {} |
| 19 | 16 | ||
| 20 | impl AlarmState { | 17 | impl AlarmState { |
| 21 | const fn new() -> Self { | 18 | const fn new() -> Self { |
| 22 | Self { | 19 | Self { token: None } |
| 23 | token: None, | ||
| 24 | closure: None, | ||
| 25 | } | ||
| 26 | } | 20 | } |
| 27 | } | 21 | } |
| 28 | 22 | ||
| @@ -33,66 +27,32 @@ extern "C" { | |||
| 33 | } | 27 | } |
| 34 | 28 | ||
| 35 | struct TimeDriver { | 29 | struct TimeDriver { |
| 36 | alarm_count: AtomicU8, | ||
| 37 | |||
| 38 | once: Once, | 30 | once: Once, |
| 39 | alarms: UninitCell<Mutex<[AlarmState; ALARM_COUNT]>>, | 31 | alarm: UninitCell<Mutex<AlarmState>>, |
| 40 | zero_instant: UninitCell<StdInstant>, | 32 | zero_instant: UninitCell<StdInstant>, |
| 33 | closure: UninitCell<Closure<dyn FnMut()>>, | ||
| 41 | } | 34 | } |
| 42 | 35 | ||
| 43 | embassy_time_driver::time_driver_impl!(static DRIVER: TimeDriver = TimeDriver { | 36 | embassy_time_driver::time_driver_impl!(static DRIVER: TimeDriver = TimeDriver { |
| 44 | alarm_count: AtomicU8::new(0), | ||
| 45 | once: Once::new(), | 37 | once: Once::new(), |
| 46 | alarms: UninitCell::uninit(), | 38 | alarm: UninitCell::uninit(), |
| 47 | zero_instant: UninitCell::uninit(), | 39 | zero_instant: UninitCell::uninit(), |
| 40 | closure: UninitCell::uninit() | ||
| 48 | }); | 41 | }); |
| 49 | 42 | ||
| 50 | impl TimeDriver { | 43 | impl TimeDriver { |
| 51 | fn init(&self) { | 44 | fn init(&self) { |
| 52 | self.once.call_once(|| unsafe { | 45 | self.once.call_once(|| unsafe { |
| 53 | self.alarms | 46 | self.alarm.write(Mutex::new(const { AlarmState::new() })); |
| 54 | .write(Mutex::new([const { AlarmState::new() }; ALARM_COUNT])); | ||
| 55 | self.zero_instant.write(StdInstant::now()); | 47 | self.zero_instant.write(StdInstant::now()); |
| 48 | self.closure | ||
| 49 | .write(Closure::new(Box::new(|| TIMER_QUEUE_DRIVER.dispatch()))); | ||
| 56 | }); | 50 | }); |
| 57 | } | 51 | } |
| 58 | } | ||
| 59 | |||
| 60 | impl Driver for TimeDriver { | ||
| 61 | fn now(&self) -> u64 { | ||
| 62 | self.init(); | ||
| 63 | |||
| 64 | let zero = unsafe { self.zero_instant.read() }; | ||
| 65 | StdInstant::now().duration_since(zero).as_micros() as u64 | ||
| 66 | } | ||
| 67 | |||
| 68 | unsafe fn allocate_alarm(&self) -> Option<AlarmHandle> { | ||
| 69 | let id = self.alarm_count.fetch_update(Ordering::AcqRel, Ordering::Acquire, |x| { | ||
| 70 | if x < ALARM_COUNT as u8 { | ||
| 71 | Some(x + 1) | ||
| 72 | } else { | ||
| 73 | None | ||
| 74 | } | ||
| 75 | }); | ||
| 76 | |||
| 77 | match id { | ||
| 78 | Ok(id) => Some(AlarmHandle::new(id)), | ||
| 79 | Err(_) => None, | ||
| 80 | } | ||
| 81 | } | ||
| 82 | |||
| 83 | fn set_alarm_callback(&self, alarm: AlarmHandle, callback: fn(*mut ()), ctx: *mut ()) { | ||
| 84 | self.init(); | ||
| 85 | let mut alarms = unsafe { self.alarms.as_ref() }.lock().unwrap(); | ||
| 86 | let alarm = &mut alarms[alarm.id() as usize]; | ||
| 87 | alarm.closure.replace(Closure::new(move || { | ||
| 88 | callback(ctx); | ||
| 89 | })); | ||
| 90 | } | ||
| 91 | 52 | ||
| 92 | fn set_alarm(&self, alarm: AlarmHandle, timestamp: u64) -> bool { | 53 | fn set_alarm(&self, timestamp: u64) -> bool { |
| 93 | self.init(); | 54 | self.init(); |
| 94 | let mut alarms = unsafe { self.alarms.as_ref() }.lock().unwrap(); | 55 | let mut alarm = unsafe { self.alarm.as_ref() }.lock().unwrap(); |
| 95 | let alarm = &mut alarms[alarm.id() as usize]; | ||
| 96 | if let Some(token) = alarm.token { | 56 | if let Some(token) = alarm.token { |
| 97 | clearTimeout(token); | 57 | clearTimeout(token); |
| 98 | } | 58 | } |
| @@ -102,13 +62,22 @@ impl Driver for TimeDriver { | |||
| 102 | false | 62 | false |
| 103 | } else { | 63 | } else { |
| 104 | let timeout = (timestamp - now) as u32; | 64 | let timeout = (timestamp - now) as u32; |
| 105 | alarm.token = Some(setTimeout(alarm.closure.as_ref().unwrap(), timeout / 1000)); | 65 | alarm.token = Some(setTimeout(unsafe { self.closure.as_ref() }, timeout / 1000)); |
| 106 | 66 | ||
| 107 | true | 67 | true |
| 108 | } | 68 | } |
| 109 | } | 69 | } |
| 110 | } | 70 | } |
| 111 | 71 | ||
| 72 | impl Driver for TimeDriver { | ||
| 73 | fn now(&self) -> u64 { | ||
| 74 | self.init(); | ||
| 75 | |||
| 76 | let zero = unsafe { self.zero_instant.read() }; | ||
| 77 | StdInstant::now().duration_since(zero).as_micros() as u64 | ||
| 78 | } | ||
| 79 | } | ||
| 80 | |||
| 112 | pub(crate) struct UninitCell<T>(MaybeUninit<UnsafeCell<T>>); | 81 | pub(crate) struct UninitCell<T>(MaybeUninit<UnsafeCell<T>>); |
| 113 | unsafe impl<T> Send for UninitCell<T> {} | 82 | unsafe impl<T> Send for UninitCell<T> {} |
| 114 | unsafe impl<T> Sync for UninitCell<T> {} | 83 | unsafe impl<T> Sync for UninitCell<T> {} |
| @@ -139,3 +108,8 @@ impl<T: Copy> UninitCell<T> { | |||
| 139 | ptr::read(self.as_mut_ptr()) | 108 | ptr::read(self.as_mut_ptr()) |
| 140 | } | 109 | } |
| 141 | } | 110 | } |
| 111 | |||
| 112 | embassy_time_queue_driver::timer_queue_impl!( | ||
| 113 | static TIMER_QUEUE_DRIVER: GlobalTimerQueue | ||
| 114 | = GlobalTimerQueue::new(|next_expiration| DRIVER.set_alarm(next_expiration)) | ||
| 115 | ); | ||
diff --git a/embassy-time/src/lib.rs b/embassy-time/src/lib.rs index 8d0648ce5..80a359413 100644 --- a/embassy-time/src/lib.rs +++ b/embassy-time/src/lib.rs | |||
| @@ -25,8 +25,6 @@ pub use driver_mock::MockDriver; | |||
| 25 | mod driver_std; | 25 | mod driver_std; |
| 26 | #[cfg(feature = "wasm")] | 26 | #[cfg(feature = "wasm")] |
| 27 | mod driver_wasm; | 27 | mod driver_wasm; |
| 28 | #[cfg(feature = "generic-queue")] | ||
| 29 | mod queue_generic; | ||
| 30 | 28 | ||
| 31 | pub use delay::{block_for, Delay}; | 29 | pub use delay::{block_for, Delay}; |
| 32 | pub use duration::Duration; | 30 | pub use duration::Duration; |
diff --git a/embassy-time/src/queue_generic.rs b/embassy-time/src/queue_generic.rs deleted file mode 100644 index 0068edae8..000000000 --- a/embassy-time/src/queue_generic.rs +++ /dev/null | |||
| @@ -1,346 +0,0 @@ | |||
| 1 | use core::cell::RefCell; | ||
| 2 | use core::cmp::{min, Ordering}; | ||
| 3 | use core::task::Waker; | ||
| 4 | |||
| 5 | use critical_section::Mutex; | ||
| 6 | use embassy_time_driver::{allocate_alarm, set_alarm, set_alarm_callback, AlarmHandle}; | ||
| 7 | use embassy_time_queue_driver::TimerQueue; | ||
| 8 | use heapless::Vec; | ||
| 9 | |||
| 10 | use crate::Instant; | ||
| 11 | |||
| 12 | #[cfg(feature = "generic-queue-8")] | ||
| 13 | const QUEUE_SIZE: usize = 8; | ||
| 14 | #[cfg(feature = "generic-queue-16")] | ||
| 15 | const QUEUE_SIZE: usize = 16; | ||
| 16 | #[cfg(feature = "generic-queue-32")] | ||
| 17 | const QUEUE_SIZE: usize = 32; | ||
| 18 | #[cfg(feature = "generic-queue-64")] | ||
| 19 | const QUEUE_SIZE: usize = 64; | ||
| 20 | #[cfg(feature = "generic-queue-128")] | ||
| 21 | const QUEUE_SIZE: usize = 128; | ||
| 22 | #[cfg(not(any( | ||
| 23 | feature = "generic-queue-8", | ||
| 24 | feature = "generic-queue-16", | ||
| 25 | feature = "generic-queue-32", | ||
| 26 | feature = "generic-queue-64", | ||
| 27 | feature = "generic-queue-128" | ||
| 28 | )))] | ||
| 29 | const QUEUE_SIZE: usize = 64; | ||
| 30 | |||
| 31 | #[derive(Debug)] | ||
| 32 | struct Timer { | ||
| 33 | at: Instant, | ||
| 34 | waker: Waker, | ||
| 35 | } | ||
| 36 | |||
| 37 | impl PartialEq for Timer { | ||
| 38 | fn eq(&self, other: &Self) -> bool { | ||
| 39 | self.at == other.at | ||
| 40 | } | ||
| 41 | } | ||
| 42 | |||
| 43 | impl Eq for Timer {} | ||
| 44 | |||
| 45 | impl PartialOrd for Timer { | ||
| 46 | fn partial_cmp(&self, other: &Self) -> Option<Ordering> { | ||
| 47 | self.at.partial_cmp(&other.at) | ||
| 48 | } | ||
| 49 | } | ||
| 50 | |||
| 51 | impl Ord for Timer { | ||
| 52 | fn cmp(&self, other: &Self) -> Ordering { | ||
| 53 | self.at.cmp(&other.at) | ||
| 54 | } | ||
| 55 | } | ||
| 56 | |||
| 57 | struct InnerQueue { | ||
| 58 | queue: Vec<Timer, QUEUE_SIZE>, | ||
| 59 | alarm: AlarmHandle, | ||
| 60 | } | ||
| 61 | |||
| 62 | impl InnerQueue { | ||
| 63 | fn schedule_wake(&mut self, at: Instant, waker: &Waker) { | ||
| 64 | self.queue | ||
| 65 | .iter_mut() | ||
| 66 | .find(|timer| timer.waker.will_wake(waker)) | ||
| 67 | .map(|timer| { | ||
| 68 | timer.at = min(timer.at, at); | ||
| 69 | }) | ||
| 70 | .unwrap_or_else(|| { | ||
| 71 | let mut timer = Timer { | ||
| 72 | waker: waker.clone(), | ||
| 73 | at, | ||
| 74 | }; | ||
| 75 | |||
| 76 | loop { | ||
| 77 | match self.queue.push(timer) { | ||
| 78 | Ok(()) => break, | ||
| 79 | Err(e) => timer = e, | ||
| 80 | } | ||
| 81 | |||
| 82 | self.queue.pop().unwrap().waker.wake(); | ||
| 83 | } | ||
| 84 | }); | ||
| 85 | |||
| 86 | // Don't wait for the alarm callback to trigger and directly | ||
| 87 | // dispatch all timers that are already due | ||
| 88 | // | ||
| 89 | // Then update the alarm if necessary | ||
| 90 | self.dispatch(); | ||
| 91 | } | ||
| 92 | |||
| 93 | fn dispatch(&mut self) { | ||
| 94 | loop { | ||
| 95 | let now = Instant::now(); | ||
| 96 | |||
| 97 | let mut next_alarm = Instant::MAX; | ||
| 98 | |||
| 99 | let mut i = 0; | ||
| 100 | while i < self.queue.len() { | ||
| 101 | let timer = &self.queue[i]; | ||
| 102 | if timer.at <= now { | ||
| 103 | let timer = self.queue.swap_remove(i); | ||
| 104 | timer.waker.wake(); | ||
| 105 | } else { | ||
| 106 | next_alarm = min(next_alarm, timer.at); | ||
| 107 | i += 1; | ||
| 108 | } | ||
| 109 | } | ||
| 110 | |||
| 111 | if self.update_alarm(next_alarm) { | ||
| 112 | break; | ||
| 113 | } | ||
| 114 | } | ||
| 115 | } | ||
| 116 | |||
| 117 | fn update_alarm(&mut self, next_alarm: Instant) -> bool { | ||
| 118 | if next_alarm == Instant::MAX { | ||
| 119 | true | ||
| 120 | } else { | ||
| 121 | set_alarm(self.alarm, next_alarm.as_ticks()) | ||
| 122 | } | ||
| 123 | } | ||
| 124 | |||
| 125 | fn handle_alarm(&mut self) { | ||
| 126 | self.dispatch(); | ||
| 127 | } | ||
| 128 | } | ||
| 129 | |||
| 130 | struct Queue { | ||
| 131 | inner: Mutex<RefCell<Option<InnerQueue>>>, | ||
| 132 | } | ||
| 133 | |||
| 134 | impl Queue { | ||
| 135 | const fn new() -> Self { | ||
| 136 | Self { | ||
| 137 | inner: Mutex::new(RefCell::new(None)), | ||
| 138 | } | ||
| 139 | } | ||
| 140 | |||
| 141 | fn schedule_wake(&'static self, at: Instant, waker: &Waker) { | ||
| 142 | critical_section::with(|cs| { | ||
| 143 | let mut inner = self.inner.borrow_ref_mut(cs); | ||
| 144 | |||
| 145 | inner | ||
| 146 | .get_or_insert_with(|| { | ||
| 147 | let handle = unsafe { allocate_alarm() }.unwrap(); | ||
| 148 | set_alarm_callback(handle, Self::handle_alarm_callback, self as *const _ as _); | ||
| 149 | InnerQueue { | ||
| 150 | queue: Vec::new(), | ||
| 151 | alarm: handle, | ||
| 152 | } | ||
| 153 | }) | ||
| 154 | .schedule_wake(at, waker) | ||
| 155 | }); | ||
| 156 | } | ||
| 157 | |||
| 158 | fn handle_alarm(&self) { | ||
| 159 | critical_section::with(|cs| self.inner.borrow_ref_mut(cs).as_mut().unwrap().handle_alarm()) | ||
| 160 | } | ||
| 161 | |||
| 162 | fn handle_alarm_callback(ctx: *mut ()) { | ||
| 163 | unsafe { (ctx as *const Self).as_ref().unwrap() }.handle_alarm(); | ||
| 164 | } | ||
| 165 | } | ||
| 166 | |||
| 167 | impl TimerQueue for Queue { | ||
| 168 | fn schedule_wake(&'static self, at: u64, waker: &Waker) { | ||
| 169 | Queue::schedule_wake(self, Instant::from_ticks(at), waker); | ||
| 170 | } | ||
| 171 | } | ||
| 172 | |||
| 173 | embassy_time_queue_driver::timer_queue_impl!(static QUEUE: Queue = Queue::new()); | ||
| 174 | |||
| 175 | #[cfg(test)] | ||
| 176 | #[cfg(feature = "mock-driver")] | ||
| 177 | mod tests { | ||
| 178 | use core::sync::atomic::{AtomicBool, Ordering}; | ||
| 179 | use core::task::Waker; | ||
| 180 | use std::sync::Arc; | ||
| 181 | use std::task::Wake; | ||
| 182 | |||
| 183 | use serial_test::serial; | ||
| 184 | |||
| 185 | use crate::driver_mock::MockDriver; | ||
| 186 | use crate::queue_generic::QUEUE; | ||
| 187 | use crate::{Duration, Instant}; | ||
| 188 | |||
| 189 | struct TestWaker { | ||
| 190 | pub awoken: AtomicBool, | ||
| 191 | } | ||
| 192 | |||
| 193 | impl Wake for TestWaker { | ||
| 194 | fn wake(self: Arc<Self>) { | ||
| 195 | self.awoken.store(true, Ordering::Relaxed); | ||
| 196 | } | ||
| 197 | |||
| 198 | fn wake_by_ref(self: &Arc<Self>) { | ||
| 199 | self.awoken.store(true, Ordering::Relaxed); | ||
| 200 | } | ||
| 201 | } | ||
| 202 | |||
| 203 | fn test_waker() -> (Arc<TestWaker>, Waker) { | ||
| 204 | let arc = Arc::new(TestWaker { | ||
| 205 | awoken: AtomicBool::new(false), | ||
| 206 | }); | ||
| 207 | let waker = Waker::from(arc.clone()); | ||
| 208 | |||
| 209 | (arc, waker) | ||
| 210 | } | ||
| 211 | |||
| 212 | fn setup() { | ||
| 213 | MockDriver::get().reset(); | ||
| 214 | critical_section::with(|cs| *QUEUE.inner.borrow_ref_mut(cs) = None); | ||
| 215 | } | ||
| 216 | |||
| 217 | fn queue_len() -> usize { | ||
| 218 | critical_section::with(|cs| { | ||
| 219 | QUEUE | ||
| 220 | .inner | ||
| 221 | .borrow_ref(cs) | ||
| 222 | .as_ref() | ||
| 223 | .map(|inner| inner.queue.iter().count()) | ||
| 224 | .unwrap_or(0) | ||
| 225 | }) | ||
| 226 | } | ||
| 227 | |||
| 228 | #[test] | ||
| 229 | #[serial] | ||
| 230 | fn test_schedule() { | ||
| 231 | setup(); | ||
| 232 | |||
| 233 | assert_eq!(queue_len(), 0); | ||
| 234 | |||
| 235 | let (flag, waker) = test_waker(); | ||
| 236 | |||
| 237 | QUEUE.schedule_wake(Instant::from_secs(1), &waker); | ||
| 238 | |||
| 239 | assert!(!flag.awoken.load(Ordering::Relaxed)); | ||
| 240 | assert_eq!(queue_len(), 1); | ||
| 241 | } | ||
| 242 | |||
| 243 | #[test] | ||
| 244 | #[serial] | ||
| 245 | fn test_schedule_same() { | ||
| 246 | setup(); | ||
| 247 | |||
| 248 | let (_flag, waker) = test_waker(); | ||
| 249 | |||
| 250 | QUEUE.schedule_wake(Instant::from_secs(1), &waker); | ||
| 251 | |||
| 252 | assert_eq!(queue_len(), 1); | ||
| 253 | |||
| 254 | QUEUE.schedule_wake(Instant::from_secs(1), &waker); | ||
| 255 | |||
| 256 | assert_eq!(queue_len(), 1); | ||
| 257 | |||
| 258 | QUEUE.schedule_wake(Instant::from_secs(100), &waker); | ||
| 259 | |||
| 260 | assert_eq!(queue_len(), 1); | ||
| 261 | |||
| 262 | let (_flag2, waker2) = test_waker(); | ||
| 263 | |||
| 264 | QUEUE.schedule_wake(Instant::from_secs(100), &waker2); | ||
| 265 | |||
| 266 | assert_eq!(queue_len(), 2); | ||
| 267 | } | ||
| 268 | |||
| 269 | #[test] | ||
| 270 | #[serial] | ||
| 271 | fn test_trigger() { | ||
| 272 | setup(); | ||
| 273 | |||
| 274 | let (flag, waker) = test_waker(); | ||
| 275 | |||
| 276 | QUEUE.schedule_wake(Instant::from_secs(100), &waker); | ||
| 277 | |||
| 278 | assert!(!flag.awoken.load(Ordering::Relaxed)); | ||
| 279 | |||
| 280 | MockDriver::get().advance(Duration::from_secs(99)); | ||
| 281 | |||
| 282 | assert!(!flag.awoken.load(Ordering::Relaxed)); | ||
| 283 | |||
| 284 | assert_eq!(queue_len(), 1); | ||
| 285 | |||
| 286 | MockDriver::get().advance(Duration::from_secs(1)); | ||
| 287 | |||
| 288 | assert!(flag.awoken.load(Ordering::Relaxed)); | ||
| 289 | |||
| 290 | assert_eq!(queue_len(), 0); | ||
| 291 | } | ||
| 292 | |||
| 293 | #[test] | ||
| 294 | #[serial] | ||
| 295 | fn test_immediate_trigger() { | ||
| 296 | setup(); | ||
| 297 | |||
| 298 | let (flag, waker) = test_waker(); | ||
| 299 | |||
| 300 | QUEUE.schedule_wake(Instant::from_secs(100), &waker); | ||
| 301 | |||
| 302 | MockDriver::get().advance(Duration::from_secs(50)); | ||
| 303 | |||
| 304 | let (flag2, waker2) = test_waker(); | ||
| 305 | |||
| 306 | QUEUE.schedule_wake(Instant::from_secs(40), &waker2); | ||
| 307 | |||
| 308 | assert!(!flag.awoken.load(Ordering::Relaxed)); | ||
| 309 | assert!(flag2.awoken.load(Ordering::Relaxed)); | ||
| 310 | assert_eq!(queue_len(), 1); | ||
| 311 | } | ||
| 312 | |||
| 313 | #[test] | ||
| 314 | #[serial] | ||
| 315 | fn test_queue_overflow() { | ||
| 316 | setup(); | ||
| 317 | |||
| 318 | for i in 1..super::QUEUE_SIZE { | ||
| 319 | let (flag, waker) = test_waker(); | ||
| 320 | |||
| 321 | QUEUE.schedule_wake(Instant::from_secs(310), &waker); | ||
| 322 | |||
| 323 | assert_eq!(queue_len(), i); | ||
| 324 | assert!(!flag.awoken.load(Ordering::Relaxed)); | ||
| 325 | } | ||
| 326 | |||
| 327 | let (flag, waker) = test_waker(); | ||
| 328 | |||
| 329 | QUEUE.schedule_wake(Instant::from_secs(300), &waker); | ||
| 330 | |||
| 331 | assert_eq!(queue_len(), super::QUEUE_SIZE); | ||
| 332 | assert!(!flag.awoken.load(Ordering::Relaxed)); | ||
| 333 | |||
| 334 | let (flag2, waker2) = test_waker(); | ||
| 335 | |||
| 336 | QUEUE.schedule_wake(Instant::from_secs(305), &waker2); | ||
| 337 | |||
| 338 | assert_eq!(queue_len(), super::QUEUE_SIZE); | ||
| 339 | assert!(flag.awoken.load(Ordering::Relaxed)); | ||
| 340 | |||
| 341 | let (_flag3, waker3) = test_waker(); | ||
| 342 | QUEUE.schedule_wake(Instant::from_secs(320), &waker3); | ||
| 343 | assert_eq!(queue_len(), super::QUEUE_SIZE); | ||
| 344 | assert!(flag2.awoken.load(Ordering::Relaxed)); | ||
| 345 | } | ||
| 346 | } | ||
diff --git a/examples/nrf52840-rtic/Cargo.toml b/examples/nrf52840-rtic/Cargo.toml index 290b2fdb1..326355dd6 100644 --- a/examples/nrf52840-rtic/Cargo.toml +++ b/examples/nrf52840-rtic/Cargo.toml | |||
| @@ -9,7 +9,8 @@ rtic = { version = "2", features = ["thumbv7-backend"] } | |||
| 9 | 9 | ||
| 10 | embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } | 10 | embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } |
| 11 | embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["defmt"] } | 11 | embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["defmt"] } |
| 12 | embassy-time = { version = "0.3.2", path = "../../embassy-time", features = [ "defmt", "defmt-timestamp-uptime", "generic-queue"] } | 12 | embassy-time = { version = "0.3.2", path = "../../embassy-time", features = [ "defmt", "defmt-timestamp-uptime"] } |
| 13 | embassy-time-queue-driver = { version = "0.1.0", path = "../../embassy-time-queue-driver" } | ||
| 13 | embassy-nrf = { version = "0.2.0", path = "../../embassy-nrf", features = [ "defmt", "nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } | 14 | embassy-nrf = { version = "0.2.0", path = "../../embassy-nrf", features = [ "defmt", "nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } |
| 14 | 15 | ||
| 15 | defmt = "0.3" | 16 | defmt = "0.3" |
