| 1 | #![no_std ] |
| 2 | #![doc = include_str!("../README.md" )] |
| 3 | #![warn (missing_docs)] |
| 4 | |
| 5 | //! ## Implementing a driver |
| 6 | //! |
| 7 | //! - Define a struct `MyDriver` |
| 8 | //! - Implement [`Driver`] for it |
| 9 | //! - Register it as the global driver with [`time_driver_impl`](crate::time_driver_impl). |
| 10 | //! |
| 11 | //! If your driver has a single set tick rate, enable the corresponding [`tick-hz-*`](crate#tick-rate) feature, |
| 12 | //! which will prevent users from needing to configure it themselves (or selecting an incorrect configuration). |
| 13 | //! |
| 14 | //! If your driver supports a small number of set tick rates, expose your own cargo features and have each one |
| 15 | //! enable the corresponding `embassy-time-driver/tick-*`. |
| 16 | //! |
| 17 | //! Otherwise, don’t enable any `tick-hz-*` feature to let the user configure the tick rate themselves by |
| 18 | //! enabling a feature on `embassy-time`. |
| 19 | //! |
| 20 | //! ### Example |
| 21 | //! |
| 22 | //! ``` |
| 23 | //! use core::task::Waker; |
| 24 | //! |
| 25 | //! use embassy_time_driver::Driver; |
| 26 | //! |
| 27 | //! struct MyDriver{} // not public! |
| 28 | //! |
| 29 | //! impl Driver for MyDriver { |
| 30 | //! fn now(&self) -> u64 { |
| 31 | //! todo!() |
| 32 | //! } |
| 33 | //! |
| 34 | //! fn schedule_wake(&self, at: u64, waker: &Waker) { |
| 35 | //! todo!() |
| 36 | //! } |
| 37 | //! } |
| 38 | //! |
| 39 | //! embassy_time_driver::time_driver_impl!(static DRIVER: MyDriver = MyDriver{}); |
| 40 | //! ``` |
| 41 | //! |
| 42 | //! ## Implementing the timer queue |
| 43 | //! |
| 44 | //! The simplest (but suboptimal) way to implement a timer queue is to define a single queue in the |
| 45 | //! time driver. Declare a field protected by an appropriate mutex (e.g. `critical_section::Mutex`). |
| 46 | //! |
| 47 | //! Then, you'll need to adapt the `schedule_wake` method to use this queue. |
| 48 | //! |
| 49 | //! Note that if you are using multiple queues, you will need to ensure that a single timer |
| 50 | //! queue item is only ever enqueued into a single queue at a time. |
| 51 | //! |
| 52 | //! ``` |
| 53 | //! use core::cell::RefCell; |
| 54 | //! use core::task::Waker; |
| 55 | //! |
| 56 | //! use critical_section::{CriticalSection, Mutex}; |
| 57 | //! use embassy_time_queue_utils::Queue; |
| 58 | //! use embassy_time_driver::Driver; |
| 59 | //! |
| 60 | //! struct MyDriver { |
| 61 | //! queue: Mutex<RefCell<Queue>>, |
| 62 | //! } |
| 63 | //! |
| 64 | //! impl MyDriver { |
| 65 | //! fn set_alarm(&self, cs: &CriticalSection, at: u64) -> bool { |
| 66 | //! todo!() |
| 67 | //! } |
| 68 | //! } |
| 69 | //! |
| 70 | //! impl Driver for MyDriver { |
| 71 | //! fn now(&self) -> u64 { todo!() } |
| 72 | //! |
| 73 | //! fn schedule_wake(&self, at: u64, waker: &Waker) { |
| 74 | //! critical_section::with(|cs| { |
| 75 | //! let mut queue = self.queue.borrow(cs).borrow_mut(); |
| 76 | //! if queue.schedule_wake(at, waker) { |
| 77 | //! let mut next = queue.next_expiration(self.now()); |
| 78 | //! while !self.set_alarm(&cs, next) { |
| 79 | //! next = queue.next_expiration(self.now()); |
| 80 | //! } |
| 81 | //! } |
| 82 | //! }); |
| 83 | //! } |
| 84 | //! } |
| 85 | //! ``` |
| 86 | //! |
| 87 | //! # Linkage details |
| 88 | //! |
| 89 | //! Instead of the usual "trait + generic params" approach, calls from embassy to the driver are done via `extern` functions. |
| 90 | //! |
| 91 | //! `embassy` internally defines the driver function as `extern "Rust" { fn _embassy_time_now() -> u64; }` and calls it. |
| 92 | //! The driver crate defines the function as `#[no_mangle] fn _embassy_time_now() -> u64`. The linker will resolve the |
| 93 | //! calls from the `embassy` crate to call into the driver crate. |
| 94 | //! |
| 95 | //! If there is none or multiple drivers in the crate tree, linking will fail. |
| 96 | //! |
| 97 | //! This method has a few key advantages for something as foundational as timekeeping: |
| 98 | //! |
| 99 | //! - The time driver is available everywhere easily, without having to thread the implementation |
| 100 | //! through generic parameters. This is especially helpful for libraries. |
| 101 | //! - It means comparing `Instant`s will always make sense: if there were multiple drivers |
| 102 | //! active, one could compare an `Instant` from driver A to an `Instant` from driver B, which |
| 103 | //! would yield incorrect results. |
| 104 | |
| 105 | //! ## Feature flags |
| 106 | #![doc = document_features::document_features!(feature_label = r#"<span class="stab portability"><code>{feature}</code></span>"# )] |
| 107 | |
| 108 | use core::task::Waker; |
| 109 | |
| 110 | mod tick; |
| 111 | |
| 112 | /// Ticks per second of the global timebase. |
| 113 | /// |
| 114 | /// This value is specified by the [`tick-*` Cargo features](crate#tick-rate) |
| 115 | pub const TICK_HZ: u64 = tick::TICK_HZ; |
| 116 | |
| 117 | /// Time driver |
| 118 | pub trait Driver: Send + Sync + 'static { |
| 119 | /// Return the current timestamp in ticks. |
| 120 | /// |
| 121 | /// Implementations MUST ensure that: |
| 122 | /// - This is guaranteed to be monotonic, i.e. a call to now() will always return |
| 123 | /// a greater or equal value than earlier calls. Time can't "roll backwards". |
| 124 | /// - It "never" overflows. It must not overflow in a sufficiently long time frame, say |
| 125 | /// in 10_000 years (Human civilization is likely to already have self-destructed |
| 126 | /// 10_000 years from now.). This means if your hardware only has 16bit/32bit timers |
| 127 | /// you MUST extend them to 64-bit, for example by counting overflows in software, |
| 128 | /// or chaining multiple timers together. |
| 129 | fn now(&self) -> u64; |
| 130 | |
| 131 | /// Schedules a waker to be awoken at moment `at`. |
| 132 | /// If this moment is in the past, the waker might be awoken immediately. |
| 133 | fn schedule_wake(&self, at: u64, waker: &Waker); |
| 134 | } |
| 135 | |
| 136 | unsafeextern "Rust" { |
| 137 | unsafefn _embassy_time_now() -> u64; |
| 138 | unsafefn _embassy_time_schedule_wake(at: u64, waker: &Waker); |
| 139 | } |
| 140 | |
| 141 | /// See [`Driver::now`] |
| 142 | pub fn now() -> u64 { |
| 143 | unsafe { _embassy_time_now() } |
| 144 | } |
| 145 | |
| 146 | /// Schedule the given waker to be woken at `at`. |
| 147 | pub fn schedule_wake(at: u64, waker: &Waker) { |
| 148 | unsafe { _embassy_time_schedule_wake(at, waker) } |
| 149 | } |
| 150 | |
| 151 | /// Set the time Driver implementation. |
| 152 | /// |
| 153 | /// See the module documentation for an example. |
| 154 | #[macro_export ] |
| 155 | macro_rules! time_driver_impl { |
| 156 | (static $name:ident: $t: ty = $val:expr) => { |
| 157 | static $name: $t = $val; |
| 158 | |
| 159 | #[no_mangle] |
| 160 | fn _embassy_time_now() -> u64 { |
| 161 | <$t as $crate::Driver>::now(&$name) |
| 162 | } |
| 163 | |
| 164 | #[no_mangle] |
| 165 | fn _embassy_time_schedule_wake(at: u64, waker: &core::task::Waker) { |
| 166 | <$t as $crate::Driver>::schedule_wake(&$name, at, waker); |
| 167 | } |
| 168 | }; |
| 169 | } |
| 170 | |