time: replace dyn clock/alarm with a global Driver trait
This commit is contained in:
@ -109,18 +109,13 @@ pub struct Executor {
|
||||
}
|
||||
|
||||
impl Executor {
|
||||
pub const fn new() -> Self {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
inner: raw::Executor::new(|_| cortex_m::asm::sev(), ptr::null_mut()),
|
||||
not_send: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "time")]
|
||||
pub fn set_alarm(&mut self, alarm: &'static dyn crate::time::Alarm) {
|
||||
self.inner.set_alarm(alarm);
|
||||
}
|
||||
|
||||
/// Runs the executor.
|
||||
///
|
||||
/// This function never returns.
|
||||
@ -161,11 +156,6 @@ impl<I: Interrupt> InterruptExecutor<I> {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "time")]
|
||||
pub fn set_alarm(&mut self, alarm: &'static dyn crate::time::Alarm) {
|
||||
self.inner.set_alarm(alarm);
|
||||
}
|
||||
|
||||
/// Start the executor.
|
||||
///
|
||||
/// `init` is called in the interrupt context, then the interrupt is
|
||||
|
@ -15,7 +15,9 @@ use super::SpawnToken;
|
||||
#[cfg(feature = "time")]
|
||||
use super::timer_queue::{TimerQueue, TimerQueueItem};
|
||||
#[cfg(feature = "time")]
|
||||
use crate::time::{Alarm, Instant};
|
||||
use crate::time::driver::{self, AlarmHandle};
|
||||
#[cfg(feature = "time")]
|
||||
use crate::time::Instant;
|
||||
|
||||
/// Task is spawned (has a future)
|
||||
pub(crate) const STATE_SPAWNED: u32 = 1 << 0;
|
||||
@ -169,11 +171,16 @@ pub struct Executor {
|
||||
#[cfg(feature = "time")]
|
||||
timer_queue: TimerQueue,
|
||||
#[cfg(feature = "time")]
|
||||
alarm: Option<&'static dyn Alarm>,
|
||||
alarm: AlarmHandle,
|
||||
}
|
||||
|
||||
impl Executor {
|
||||
pub const fn new(signal_fn: fn(*mut ()), signal_ctx: *mut ()) -> Self {
|
||||
pub fn new(signal_fn: fn(*mut ()), signal_ctx: *mut ()) -> Self {
|
||||
#[cfg(feature = "time")]
|
||||
let alarm = unsafe { unwrap!(driver::allocate_alarm()) };
|
||||
#[cfg(feature = "time")]
|
||||
driver::set_alarm_callback(alarm, signal_fn, signal_ctx);
|
||||
|
||||
Self {
|
||||
run_queue: RunQueue::new(),
|
||||
signal_fn,
|
||||
@ -182,15 +189,10 @@ impl Executor {
|
||||
#[cfg(feature = "time")]
|
||||
timer_queue: TimerQueue::new(),
|
||||
#[cfg(feature = "time")]
|
||||
alarm: None,
|
||||
alarm,
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "time")]
|
||||
pub fn set_alarm(&mut self, alarm: &'static dyn Alarm) {
|
||||
self.alarm = Some(alarm);
|
||||
}
|
||||
|
||||
pub fn set_signal_ctx(&mut self, signal_ctx: *mut ()) {
|
||||
self.signal_ctx = signal_ctx;
|
||||
}
|
||||
@ -209,11 +211,9 @@ impl Executor {
|
||||
|
||||
pub unsafe fn run_queued(&'static self) {
|
||||
#[cfg(feature = "time")]
|
||||
if self.alarm.is_some() {
|
||||
self.timer_queue.dequeue_expired(Instant::now(), |p| {
|
||||
p.as_ref().enqueue();
|
||||
});
|
||||
}
|
||||
self.timer_queue.dequeue_expired(Instant::now(), |p| {
|
||||
p.as_ref().enqueue();
|
||||
});
|
||||
|
||||
self.run_queue.dequeue_all(|p| {
|
||||
let task = p.as_ref();
|
||||
@ -239,13 +239,12 @@ impl Executor {
|
||||
self.timer_queue.update(p);
|
||||
});
|
||||
|
||||
// If this is in the past, set_alarm will immediately trigger the alarm,
|
||||
// which will make the wfe immediately return so we do another loop iteration.
|
||||
#[cfg(feature = "time")]
|
||||
if let Some(alarm) = self.alarm {
|
||||
{
|
||||
// If this is in the past, set_alarm will immediately trigger the alarm,
|
||||
// which will make the wfe immediately return so we do another loop iteration.
|
||||
let next_expiration = self.timer_queue.next_expiration();
|
||||
alarm.set_callback(self.signal_fn, self.signal_ctx);
|
||||
alarm.set(next_expiration.as_ticks());
|
||||
driver::set_alarm(self.alarm, next_expiration.as_ticks());
|
||||
}
|
||||
}
|
||||
|
||||
|
118
embassy/src/time/driver.rs
Normal file
118
embassy/src/time/driver.rs
Normal file
@ -0,0 +1,118 @@
|
||||
/// Alarm handle, assigned by the driver.
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct AlarmHandle {
|
||||
id: u8,
|
||||
}
|
||||
|
||||
impl AlarmHandle {
|
||||
/// Create an AlarmHandle
|
||||
///
|
||||
/// Safety: May only be called by the current global Driver impl.
|
||||
/// The impl is allowed to rely on the fact that all `AlarmHandle` instances
|
||||
/// are created by itself in unsafe code (e.g. indexing operations)
|
||||
pub unsafe fn new(id: u8) -> Self {
|
||||
Self { id }
|
||||
}
|
||||
|
||||
/// Get the ID of the AlarmHandle.
|
||||
pub fn id(&self) -> u8 {
|
||||
self.id
|
||||
}
|
||||
}
|
||||
|
||||
/// Time driver
|
||||
pub trait Driver {
|
||||
/// Return the current timestamp in ticks.
|
||||
/// This is guaranteed to be monotonic, i.e. a call to now() will always return
|
||||
/// a greater or equal value than earler calls.
|
||||
fn now() -> u64;
|
||||
|
||||
/// Try allocating an alarm handle. Returns None if no alarms left.
|
||||
/// Initially the alarm has no callback set, and a null `ctx` pointer.
|
||||
///
|
||||
/// # Safety
|
||||
/// It is UB to make the alarm fire before setting a callback.
|
||||
unsafe fn allocate_alarm() -> Option<AlarmHandle>;
|
||||
|
||||
/// Sets the callback function to be called when the alarm triggers.
|
||||
/// The callback may be called from any context (interrupt or thread mode).
|
||||
fn set_alarm_callback(alarm: AlarmHandle, callback: fn(*mut ()), ctx: *mut ());
|
||||
|
||||
/// Sets an alarm at the given timestamp. When the current timestamp reaches that
|
||||
/// timestamp, the provided callback funcion will be called.
|
||||
///
|
||||
/// When callback is called, it is guaranteed that now() will return a value greater or equal than timestamp.
|
||||
///
|
||||
/// Only one alarm can be active at a time. This overwrites any previously-set alarm if any.
|
||||
fn set_alarm(alarm: AlarmHandle, timestamp: u64);
|
||||
}
|
||||
|
||||
extern "Rust" {
|
||||
fn _embassy_time_now() -> u64;
|
||||
fn _embassy_time_allocate_alarm() -> Option<AlarmHandle>;
|
||||
fn _embassy_time_set_alarm_callback(alarm: AlarmHandle, callback: fn(*mut ()), ctx: *mut ());
|
||||
fn _embassy_time_set_alarm(alarm: AlarmHandle, timestamp: u64);
|
||||
}
|
||||
|
||||
pub(crate) fn now() -> u64 {
|
||||
unsafe { _embassy_time_now() }
|
||||
}
|
||||
/// Safety: it is UB to make the alarm fire before setting a callback.
|
||||
pub(crate) unsafe fn allocate_alarm() -> Option<AlarmHandle> {
|
||||
_embassy_time_allocate_alarm()
|
||||
}
|
||||
pub(crate) fn set_alarm_callback(alarm: AlarmHandle, callback: fn(*mut ()), ctx: *mut ()) {
|
||||
unsafe { _embassy_time_set_alarm_callback(alarm, callback, ctx) }
|
||||
}
|
||||
pub(crate) fn set_alarm(alarm: AlarmHandle, timestamp: u64) {
|
||||
unsafe { _embassy_time_set_alarm(alarm, timestamp) }
|
||||
}
|
||||
|
||||
/// Set the time Driver implementation.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// struct MyDriver;
|
||||
/// embassy::time_driver_impl!(MyDriver);
|
||||
///
|
||||
/// unsafe impl embassy::time::driver::Driver for MyDriver {
|
||||
/// fn now() -> u64 {
|
||||
/// todo!()
|
||||
/// }
|
||||
/// unsafe fn allocate_alarm() -> Option<AlarmHandle> {
|
||||
/// todo!()
|
||||
/// }
|
||||
/// fn set_alarm_callback(alarm: AlarmHandle, callback: fn(*mut ()), ctx: *mut ()) {
|
||||
/// todo!()
|
||||
/// }
|
||||
/// fn set_alarm(alarm: AlarmHandle, timestamp: u64) {
|
||||
/// todo!()
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
#[macro_export]
|
||||
macro_rules! time_driver_impl {
|
||||
($t: ty) => {
|
||||
#[no_mangle]
|
||||
fn _embassy_time_now() -> u64 {
|
||||
<$t as $crate::time::driver::Driver>::now()
|
||||
}
|
||||
#[no_mangle]
|
||||
unsafe fn _embassy_time_allocate_alarm() -> Option<AlarmHandle> {
|
||||
<$t as $crate::time::driver::Driver>::allocate_alarm()
|
||||
}
|
||||
#[no_mangle]
|
||||
fn _embassy_time_set_alarm_callback(
|
||||
alarm: AlarmHandle,
|
||||
callback: fn(*mut ()),
|
||||
ctx: *mut (),
|
||||
) {
|
||||
<$t as $crate::time::driver::Driver>::set_alarm_callback(alarm, callback, ctx)
|
||||
}
|
||||
#[no_mangle]
|
||||
fn _embassy_time_set_alarm(alarm: AlarmHandle, timestamp: u64) {
|
||||
<$t as $crate::time::driver::Driver>::set_alarm(alarm, timestamp)
|
||||
}
|
||||
};
|
||||
}
|
@ -1,8 +1,7 @@
|
||||
use core::fmt;
|
||||
use core::ops::{Add, AddAssign, Sub, SubAssign};
|
||||
|
||||
use super::TICKS_PER_SECOND;
|
||||
use super::{now, Duration};
|
||||
use super::{driver, Duration, TICKS_PER_SECOND};
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
@ -17,7 +16,9 @@ impl Instant {
|
||||
|
||||
/// Returns an Instant representing the current time.
|
||||
pub fn now() -> Instant {
|
||||
Instant { ticks: now() }
|
||||
Instant {
|
||||
ticks: driver::now(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Instant as clock ticks since MCU start.
|
||||
|
@ -1,17 +1,15 @@
|
||||
//! Time abstractions
|
||||
//! To use these abstractions, first call `set_clock` with an instance of an [Clock](trait.Clock.html).
|
||||
//!
|
||||
|
||||
mod delay;
|
||||
pub mod driver;
|
||||
mod duration;
|
||||
mod instant;
|
||||
mod timer;
|
||||
mod traits;
|
||||
|
||||
pub use delay::{block_for, Delay};
|
||||
pub use duration::Duration;
|
||||
pub use instant::Instant;
|
||||
pub use timer::{with_timeout, Ticker, TimeoutError, Timer};
|
||||
pub use traits::*;
|
||||
|
||||
#[cfg(feature = "time-tick-1000hz")]
|
||||
pub const TICKS_PER_SECOND: u64 = 1_000;
|
||||
@ -21,19 +19,3 @@ pub const TICKS_PER_SECOND: u64 = 32_768;
|
||||
|
||||
#[cfg(feature = "time-tick-1mhz")]
|
||||
pub const TICKS_PER_SECOND: u64 = 1_000_000;
|
||||
|
||||
static mut CLOCK: Option<&'static dyn Clock> = None;
|
||||
|
||||
/// Sets the clock used for the timing abstractions
|
||||
///
|
||||
/// Safety: Sets a mutable global.
|
||||
pub unsafe fn set_clock(clock: &'static dyn Clock) {
|
||||
CLOCK = Some(clock);
|
||||
}
|
||||
|
||||
/// Return the current timestamp in ticks.
|
||||
/// This is guaranteed to be monotonic, i.e. a call to now() will always return
|
||||
/// a greater or equal value than earler calls.
|
||||
pub(crate) fn now() -> u64 {
|
||||
unsafe { unwrap!(CLOCK, "No clock set").now() }
|
||||
}
|
||||
|
@ -1,32 +0,0 @@
|
||||
/// Monotonic clock
|
||||
pub trait Clock {
|
||||
/// Return the current timestamp in ticks.
|
||||
/// This is guaranteed to be monotonic, i.e. a call to now() will always return
|
||||
/// a greater or equal value than earler calls.
|
||||
fn now(&self) -> u64;
|
||||
}
|
||||
|
||||
impl<T: Clock + ?Sized> Clock for &T {
|
||||
fn now(&self) -> u64 {
|
||||
T::now(self)
|
||||
}
|
||||
}
|
||||
|
||||
/// Trait to register a callback at a given timestamp.
|
||||
pub trait Alarm {
|
||||
/// Sets the callback function to be called when the alarm triggers.
|
||||
/// The callback may be called from any context (interrupt or thread mode).
|
||||
fn set_callback(&self, callback: fn(*mut ()), ctx: *mut ());
|
||||
|
||||
/// Sets an alarm at the given timestamp. When the clock reaches that
|
||||
/// timestamp, the provided callback funcion will be called.
|
||||
///
|
||||
/// When callback is called, it is guaranteed that now() will return a value greater or equal than timestamp.
|
||||
///
|
||||
/// Only one alarm can be active at a time. This overwrites any previously-set alarm if any.
|
||||
fn set(&self, timestamp: u64);
|
||||
|
||||
/// Clears the previously-set alarm.
|
||||
/// If no alarm was set, this is a noop.
|
||||
fn clear(&self);
|
||||
}
|
Reference in New Issue
Block a user