diff options
| author | Dario Nieuwenhuis <[email protected]> | 2022-07-29 21:58:35 +0200 |
|---|---|---|
| committer | Dario Nieuwenhuis <[email protected]> | 2022-07-29 23:40:36 +0200 |
| commit | a0f1b0ee01d461607660d2d56b5b1bdc57e0d3fb (patch) | |
| tree | e60fc8f8db8ec07e55d655c1a830b07f4db0b7d2 /embassy-executor/src/time/driver.rs | |
| parent | 8745d646f0976791b7098456aa61adb983fb1c18 (diff) | |
Split embassy crate into embassy-executor, embassy-util.
Diffstat (limited to 'embassy-executor/src/time/driver.rs')
| -rw-r--r-- | embassy-executor/src/time/driver.rs | 170 |
1 files changed, 170 insertions, 0 deletions
diff --git a/embassy-executor/src/time/driver.rs b/embassy-executor/src/time/driver.rs new file mode 100644 index 000000000..48e2f1c7d --- /dev/null +++ b/embassy-executor/src/time/driver.rs | |||
| @@ -0,0 +1,170 @@ | |||
| 1 | //! Time driver interface | ||
| 2 | //! | ||
| 3 | //! This module defines the interface a driver needs to implement to power the `embassy_executor::time` module. | ||
| 4 | //! | ||
| 5 | //! # Implementing a driver | ||
| 6 | //! | ||
| 7 | //! - Define a struct `MyDriver` | ||
| 8 | //! - Implement [`Driver`] for it | ||
| 9 | //! - Register it as the global driver with [`time_driver_impl`]. | ||
| 10 | //! - Enable the Cargo features `embassy-executor/time` and one of `embassy-executor/time-tick-*` corresponding to the | ||
| 11 | //! tick rate of your driver. | ||
| 12 | //! | ||
| 13 | //! If you wish to make the tick rate configurable by the end user, you should do so by exposing your own | ||
| 14 | //! Cargo features and having each enable the corresponding `embassy-executor/time-tick-*`. | ||
| 15 | //! | ||
| 16 | //! # Linkage details | ||
| 17 | //! | ||
| 18 | //! Instead of the usual "trait + generic params" approach, calls from embassy to the driver are done via `extern` functions. | ||
| 19 | //! | ||
| 20 | //! `embassy` internally defines the driver functions as `extern "Rust" { fn _embassy_time_now() -> u64; }` and calls them. | ||
| 21 | //! The driver crate defines the functions as `#[no_mangle] fn _embassy_time_now() -> u64`. The linker will resolve the | ||
| 22 | //! calls from the `embassy` crate to call into the driver crate. | ||
| 23 | //! | ||
| 24 | //! If there is none or multiple drivers in the crate tree, linking will fail. | ||
| 25 | //! | ||
| 26 | //! This method has a few key advantages for something as foundational as timekeeping: | ||
| 27 | //! | ||
| 28 | //! - The time driver is available everywhere easily, without having to thread the implementation | ||
| 29 | //! through generic parameters. This is especially helpful for libraries. | ||
| 30 | //! - It means comparing `Instant`s will always make sense: if there were multiple drivers | ||
| 31 | //! active, one could compare an `Instant` from driver A to an `Instant` from driver B, which | ||
| 32 | //! would yield incorrect results. | ||
| 33 | //! | ||
| 34 | //! # Example | ||
| 35 | //! | ||
| 36 | //! ``` | ||
| 37 | //! use embassy_executor::time::driver::{Driver, AlarmHandle}; | ||
| 38 | //! | ||
| 39 | //! struct MyDriver{}; // not public! | ||
| 40 | //! embassy_executor::time_driver_impl!(static DRIVER: MyDriver = MyDriver{}); | ||
| 41 | //! | ||
| 42 | //! impl Driver for MyDriver { | ||
| 43 | //! fn now(&self) -> u64 { | ||
| 44 | //! todo!() | ||
| 45 | //! } | ||
| 46 | //! unsafe fn allocate_alarm(&self) -> Option<AlarmHandle> { | ||
| 47 | //! todo!() | ||
| 48 | //! } | ||
| 49 | //! fn set_alarm_callback(&self, alarm: AlarmHandle, callback: fn(*mut ()), ctx: *mut ()) { | ||
| 50 | //! todo!() | ||
| 51 | //! } | ||
| 52 | //! fn set_alarm(&self, alarm: AlarmHandle, timestamp: u64) { | ||
| 53 | //! todo!() | ||
| 54 | //! } | ||
| 55 | //! } | ||
| 56 | //! ``` | ||
| 57 | |||
| 58 | /// Alarm handle, assigned by the driver. | ||
| 59 | #[derive(Clone, Copy)] | ||
| 60 | pub struct AlarmHandle { | ||
| 61 | id: u8, | ||
| 62 | } | ||
| 63 | |||
| 64 | impl AlarmHandle { | ||
| 65 | /// Create an AlarmHandle | ||
| 66 | /// | ||
| 67 | /// Safety: May only be called by the current global Driver impl. | ||
| 68 | /// The impl is allowed to rely on the fact that all `AlarmHandle` instances | ||
| 69 | /// are created by itself in unsafe code (e.g. indexing operations) | ||
| 70 | pub unsafe fn new(id: u8) -> Self { | ||
| 71 | Self { id } | ||
| 72 | } | ||
| 73 | |||
| 74 | /// Get the ID of the AlarmHandle. | ||
| 75 | pub fn id(&self) -> u8 { | ||
| 76 | self.id | ||
| 77 | } | ||
| 78 | } | ||
| 79 | |||
| 80 | /// Time driver | ||
| 81 | pub trait Driver: Send + Sync + 'static { | ||
| 82 | /// Return the current timestamp in ticks. | ||
| 83 | /// | ||
| 84 | /// Implementations MUST ensure that: | ||
| 85 | /// - This is guaranteed to be monotonic, i.e. a call to now() will always return | ||
| 86 | /// a greater or equal value than earler calls. Time can't "roll backwards". | ||
| 87 | /// - It "never" overflows. It must not overflow in a sufficiently long time frame, say | ||
| 88 | /// in 10_000 years (Human civilization is likely to already have self-destructed | ||
| 89 | /// 10_000 years from now.). This means if your hardware only has 16bit/32bit timers | ||
| 90 | /// you MUST extend them to 64-bit, for example by counting overflows in software, | ||
| 91 | /// or chaining multiple timers together. | ||
| 92 | fn now(&self) -> u64; | ||
| 93 | |||
| 94 | /// Try allocating an alarm handle. Returns None if no alarms left. | ||
| 95 | /// Initially the alarm has no callback set, and a null `ctx` pointer. | ||
| 96 | /// | ||
| 97 | /// # Safety | ||
| 98 | /// It is UB to make the alarm fire before setting a callback. | ||
| 99 | unsafe fn allocate_alarm(&self) -> Option<AlarmHandle>; | ||
| 100 | |||
| 101 | /// Sets the callback function to be called when the alarm triggers. | ||
| 102 | /// The callback may be called from any context (interrupt or thread mode). | ||
| 103 | fn set_alarm_callback(&self, alarm: AlarmHandle, callback: fn(*mut ()), ctx: *mut ()); | ||
| 104 | |||
| 105 | /// Sets an alarm at the given timestamp. When the current timestamp reaches the alarm | ||
| 106 | /// timestamp, the provided callback function will be called. | ||
| 107 | /// | ||
| 108 | /// If `timestamp` is already in the past, the alarm callback must be immediately fired. | ||
| 109 | /// In this case, it is allowed (but not mandatory) to call the alarm callback synchronously from `set_alarm`. | ||
| 110 | /// | ||
| 111 | /// When callback is called, it is guaranteed that now() will return a value greater or equal than timestamp. | ||
| 112 | /// | ||
| 113 | /// Only one alarm can be active at a time for each AlarmHandle. This overwrites any previously-set alarm if any. | ||
| 114 | fn set_alarm(&self, alarm: AlarmHandle, timestamp: u64); | ||
| 115 | } | ||
| 116 | |||
| 117 | extern "Rust" { | ||
| 118 | fn _embassy_time_now() -> u64; | ||
| 119 | fn _embassy_time_allocate_alarm() -> Option<AlarmHandle>; | ||
| 120 | fn _embassy_time_set_alarm_callback(alarm: AlarmHandle, callback: fn(*mut ()), ctx: *mut ()); | ||
| 121 | fn _embassy_time_set_alarm(alarm: AlarmHandle, timestamp: u64); | ||
| 122 | } | ||
| 123 | |||
| 124 | pub(crate) fn now() -> u64 { | ||
| 125 | unsafe { _embassy_time_now() } | ||
| 126 | } | ||
| 127 | /// Safety: it is UB to make the alarm fire before setting a callback. | ||
| 128 | pub(crate) unsafe fn allocate_alarm() -> Option<AlarmHandle> { | ||
| 129 | _embassy_time_allocate_alarm() | ||
| 130 | } | ||
| 131 | pub(crate) fn set_alarm_callback(alarm: AlarmHandle, callback: fn(*mut ()), ctx: *mut ()) { | ||
| 132 | unsafe { _embassy_time_set_alarm_callback(alarm, callback, ctx) } | ||
| 133 | } | ||
| 134 | pub(crate) fn set_alarm(alarm: AlarmHandle, timestamp: u64) { | ||
| 135 | unsafe { _embassy_time_set_alarm(alarm, timestamp) } | ||
| 136 | } | ||
| 137 | |||
| 138 | /// Set the time Driver implementation. | ||
| 139 | /// | ||
| 140 | /// See the module documentation for an example. | ||
| 141 | #[macro_export] | ||
| 142 | macro_rules! time_driver_impl { | ||
| 143 | (static $name:ident: $t: ty = $val:expr) => { | ||
| 144 | static $name: $t = $val; | ||
| 145 | |||
| 146 | #[no_mangle] | ||
| 147 | fn _embassy_time_now() -> u64 { | ||
| 148 | <$t as $crate::time::driver::Driver>::now(&$name) | ||
| 149 | } | ||
| 150 | |||
| 151 | #[no_mangle] | ||
| 152 | unsafe fn _embassy_time_allocate_alarm() -> Option<$crate::time::driver::AlarmHandle> { | ||
| 153 | <$t as $crate::time::driver::Driver>::allocate_alarm(&$name) | ||
| 154 | } | ||
| 155 | |||
| 156 | #[no_mangle] | ||
| 157 | fn _embassy_time_set_alarm_callback( | ||
| 158 | alarm: $crate::time::driver::AlarmHandle, | ||
| 159 | callback: fn(*mut ()), | ||
| 160 | ctx: *mut (), | ||
| 161 | ) { | ||
| 162 | <$t as $crate::time::driver::Driver>::set_alarm_callback(&$name, alarm, callback, ctx) | ||
| 163 | } | ||
| 164 | |||
| 165 | #[no_mangle] | ||
| 166 | fn _embassy_time_set_alarm(alarm: $crate::time::driver::AlarmHandle, timestamp: u64) { | ||
| 167 | <$t as $crate::time::driver::Driver>::set_alarm(&$name, alarm, timestamp) | ||
| 168 | } | ||
| 169 | }; | ||
| 170 | } | ||
