Skip to content

Commit 5d970f3

Browse files
committed
zephyr: embassy: Decouple embassy-time and Zephyr time
Don't require that the two time bases be the same. This allows applications to work using the default embassy-time base of 1Mhz. There is a performance cost to the conversion (which depends on the exact ratios). If the time bases are the same (which would be common for an application built for a single target), then no conversion is needed. Signed-off-by: David Brown <[email protected]>
1 parent 61d1158 commit 5d970f3

File tree

1 file changed

+53
-7
lines changed

1 file changed

+53
-7
lines changed

zephyr/src/embassy/time_driver.rs

+53-7
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,18 @@ use embassy_time_queue_utils::Queue;
1414
use crate::raw::{k_timeout_t, k_timer, k_timer_init, k_timer_start};
1515
use crate::sys::K_FOREVER;
1616

17+
/// The time base configured into Zephyr.
18+
pub const ZEPHYR_TICK_HZ: u64 = crate::time::SYS_FREQUENCY as u64;
19+
20+
/// The configured Embassy time tick rate.
21+
pub const EMBASSY_TICK_HZ: u64 = embassy_time_driver::TICK_HZ;
22+
23+
/// When the zephyr and embassy rates differ, use this intermediate type. This can be selected by
24+
/// feature. At the worst case, with Embassy's tick at 1Mhz, and Zephyr's at 50k, it is a little
25+
/// over 11 years. Higher of either will reduce that further. But, 128-bit arithmetic is fairly
26+
/// inefficient.
27+
type InterTime = u128;
28+
1729
embassy_time_driver::time_driver_impl!(static DRIVER: ZephyrTimeDriver = ZephyrTimeDriver {
1830
queue: Mutex::new(RefCell::new(Queue::new())),
1931
timer: Mutex::new(RefCell::new(unsafe { mem::zeroed() })),
@@ -63,20 +75,54 @@ impl ZTimer {
6375
}
6476
}
6577

78+
/// Convert from a zephyr tick count, to an embassy tick count.
79+
///
80+
/// This is done using an intermediate type defined above.
81+
/// This conversion truncates.
82+
fn zephyr_to_embassy(ticks: u64) -> u64 {
83+
if ZEPHYR_TICK_HZ == EMBASSY_TICK_HZ {
84+
// This should happen at compile time.
85+
return ticks;
86+
}
87+
88+
// Otherwise do the intermediate conversion.
89+
let prod = (ticks as InterTime) * (EMBASSY_TICK_HZ as InterTime);
90+
(prod / (ZEPHYR_TICK_HZ as InterTime)) as u64
91+
}
92+
93+
/// Convert from an embassy tick count to a zephyr.
94+
///
95+
/// This conversion use ceil so that values are always large enough.
96+
fn embassy_to_zephyr(ticks: u64) -> u64 {
97+
if ZEPHYR_TICK_HZ == EMBASSY_TICK_HZ {
98+
return ticks;
99+
}
100+
101+
let prod = (ticks as InterTime) * (ZEPHYR_TICK_HZ as InterTime);
102+
prod.div_ceil(EMBASSY_TICK_HZ as InterTime) as u64
103+
}
104+
105+
fn zephyr_now() -> u64 {
106+
crate::time::now().ticks()
107+
}
108+
66109
impl Driver for ZephyrTimeDriver {
67110
fn now(&self) -> u64 {
68-
crate::time::now().ticks()
111+
zephyr_to_embassy(zephyr_now())
69112
}
70113

71114
fn schedule_wake(&self, at: u64, waker: &core::task::Waker) {
72115
critical_section::with(|cs| {
73116
let mut queue = self.queue.borrow(cs).borrow_mut();
74117
let mut timer = self.timer.borrow(cs).borrow_mut();
75118

119+
// All times below are in Zephyr units.
120+
let at = embassy_to_zephyr(at);
121+
76122
if queue.schedule_wake(at, waker) {
77-
let mut next = queue.next_expiration(self.now());
78-
while !timer.set_alarm(next, self.now()) {
79-
next = queue.next_expiration(self.now());
123+
let mut next = queue.next_expiration(zephyr_now());
124+
while !timer.set_alarm(next, zephyr_now()) {
125+
next = queue.next_expiration(zephyr_now());
80126
}
81127
}
82128
})
@@ -89,9 +135,9 @@ impl ZephyrTimeDriver {
89135
let mut queue = self.queue.borrow(cs).borrow_mut();
90136
let mut timer = self.timer.borrow(cs).borrow_mut();
91137

92-
let mut next = queue.next_expiration(self.now());
93-
while !timer.set_alarm(next, self.now()) {
94-
next = queue.next_expiration(self.now());
138+
let mut next = queue.next_expiration(zephyr_now());
139+
while !timer.set_alarm(next, zephyr_now()) {
140+
next = queue.next_expiration(zephyr_now());
95141
}
96142
})
97143
}

0 commit comments

Comments
 (0)