From 70e2c052056e31f06ac67d69fda503f07cae46d0 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Wed, 7 May 2025 08:33:03 -0700 Subject: [PATCH 1/4] Rename rtc.rs to time_driver.rs We will add another time driver for user selection. --- embassy-imxrt/src/lib.rs | 4 ++-- embassy-imxrt/src/{rtc.rs => time_driver.rs} | 0 2 files changed, 2 insertions(+), 2 deletions(-) rename embassy-imxrt/src/{rtc.rs => time_driver.rs} (100%) diff --git a/embassy-imxrt/src/lib.rs b/embassy-imxrt/src/lib.rs index 5fbf3244b..fdf07b433 100644 --- a/embassy-imxrt/src/lib.rs +++ b/embassy-imxrt/src/lib.rs @@ -22,7 +22,7 @@ pub mod gpio; pub mod iopctl; #[cfg(feature = "_time-driver")] -pub mod rtc; +pub mod time_driver; // This mod MUST go last, so that it sees all the `impl_foo!' macros #[cfg_attr(feature = "mimxrt633s", path = "chips/mimxrt633s.rs")] @@ -137,7 +137,7 @@ pub fn init(config: config::Config) -> Peripherals { // init RTC time driver #[cfg(feature = "_time-driver")] - rtc::init(config.time_interrupt_priority); + time_driver::init(config.time_interrupt_priority); peripherals } diff --git a/embassy-imxrt/src/rtc.rs b/embassy-imxrt/src/time_driver.rs similarity index 100% rename from embassy-imxrt/src/rtc.rs rename to embassy-imxrt/src/time_driver.rs From 64ce271af526a9311b0c1c251b19b5ce79032551 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Wed, 7 May 2025 10:39:25 -0700 Subject: [PATCH 2/4] clocks: split clock and reset operations Some peripherals need to control clock without touching the reset. Signed-off-by: Felipe Balbi --- embassy-imxrt/src/clocks.rs | 30 ++++++++++++++++++++++-------- 1 file changed, 22 insertions(+), 8 deletions(-) diff --git a/embassy-imxrt/src/clocks.rs b/embassy-imxrt/src/clocks.rs index 1d36fb142..5be5f3925 100644 --- a/embassy-imxrt/src/clocks.rs +++ b/embassy-imxrt/src/clocks.rs @@ -1561,7 +1561,8 @@ pub(crate) unsafe fn init(config: ClockConfig) -> Result<(), ClockError> { ///Trait to expose perph clocks trait SealedSysconPeripheral { - fn enable_and_reset_perph_clock(); + fn enable_perph_clock(); + fn reset_perph(); fn disable_perph_clock(); } @@ -1574,7 +1575,18 @@ pub trait SysconPeripheral: SealedSysconPeripheral + 'static {} /// /// Peripheral must not be in use. pub fn enable_and_reset() { - T::enable_and_reset_perph_clock(); + T::enable_perph_clock(); + T::reset_perph(); +} + +/// Enables peripheral `T`. +pub fn enable() { + T::enable_perph_clock(); +} + +/// Reset peripheral `T`. +pub fn reset() { + T::reset_perph(); } /// Disables peripheral `T`. @@ -1588,15 +1600,21 @@ pub fn disable() { macro_rules! impl_perph_clk { ($peripheral:ident, $clkctl:ident, $clkreg:ident, $rstctl:ident, $rstreg:ident, $bit:expr) => { impl SealedSysconPeripheral for crate::peripherals::$peripheral { - fn enable_and_reset_perph_clock() { + fn enable_perph_clock() { // SAFETY: unsafe needed to take pointers to Rstctl1 and Clkctl1 let cc1 = unsafe { pac::$clkctl::steal() }; - let rc1 = unsafe { pac::$rstctl::steal() }; paste! { // SAFETY: unsafe due to the use of bits() cc1.[<$clkreg _set>]().write(|w| unsafe { w.bits(1 << $bit) }); + } + } + fn reset_perph() { + // SAFETY: unsafe needed to take pointers to Rstctl1 and Clkctl1 + let rc1 = unsafe { pac::$rstctl::steal() }; + + paste! { // SAFETY: unsafe due to the use of bits() rc1.[<$rstreg _clr>]().write(|w| unsafe { w.bits(1 << $bit) }); } @@ -1605,12 +1623,8 @@ macro_rules! impl_perph_clk { fn disable_perph_clock() { // SAFETY: unsafe needed to take pointers to Rstctl1 and Clkctl1 let cc1 = unsafe { pac::$clkctl::steal() }; - let rc1 = unsafe { pac::$rstctl::steal() }; paste! { - // SAFETY: unsafe due to the use of bits() - rc1.[<$rstreg _set>]().write(|w| unsafe { w.bits(1 << $bit) }); - // SAFETY: unsafe due to the use of bits() cc1.[<$clkreg _clr>]().write(|w| unsafe { w.bits(1 << $bit) }); } From 297ff3d03229bedb2582c171be23b488ecc4e520 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Wed, 7 May 2025 12:56:58 -0700 Subject: [PATCH 3/4] clocks: remove defmt messages Whenever any of the defmt-timestamp-uptime* features is enabled, defmt will insert code that reads the timestamp in order to embed it into the format string. This means that we *must* have a functional time driver by the time the very first defmt message is printed. Because clocks.rs is the part of the code setting up clocks that may, indeed, be required by the chosen clock driver, it cannot contain any defmt messages, otherwise it will trigger a read to a function that does not yet exist. Signed-off-by: Felipe Balbi --- embassy-imxrt/src/clocks.rs | 42 +------------------------------------ 1 file changed, 1 insertion(+), 41 deletions(-) diff --git a/embassy-imxrt/src/clocks.rs b/embassy-imxrt/src/clocks.rs index 5be5f3925..39c3e6238 100644 --- a/embassy-imxrt/src/clocks.rs +++ b/embassy-imxrt/src/clocks.rs @@ -1,8 +1,6 @@ //! Clock configuration for the `RT6xx` use core::sync::atomic::{AtomicU32, AtomicU8, Ordering}; -#[cfg(feature = "defmt")] -use defmt; use paste::paste; use crate::pac; @@ -503,7 +501,6 @@ impl ConfigurableClock for LposcConfig { } } } else { - error!("failed to convert desired clock rate, {:#}, to LPOSC Freq", freq); Err(ClockError::InvalidFrequency) } } @@ -549,7 +546,6 @@ impl ConfigurableClock for FfroConfig { Ok(()) } fn get_clock_rate(&self) -> Result { - trace!("getting ffro clock rate"); Ok(self.freq.load(Ordering::Relaxed)) } fn set_clock_rate(&mut self, _div: u8, _mult: u8, freq: u32) -> Result<(), ClockError> { @@ -616,7 +612,6 @@ impl ConfigurableClock for SfroConfig { fn set_clock_rate(&mut self, _div: u8, _mult: u8, freq: u32) -> Result<(), ClockError> { if self.state == State::Enabled { if freq == SFRO_FREQ { - trace!("Sfro frequency is already set at 16MHz"); Ok(()) } else { Err(ClockError::InvalidFrequency) @@ -677,7 +672,6 @@ impl MultiSourceClock for MainPllClkConfig { } MainPllClkSrc::SFRO => { if !clock_src_config.is_enabled() { - error!("Can't set SFRO as source for MainPll as it's not enabled"); return Err(ClockError::ClockNotEnabled); } // check if desired frequency is a valid multiple of 16m SFRO clock @@ -703,7 +697,6 @@ impl ConfigurableClock for MainPllClkConfig { } fn disable(&self) -> Result<(), ClockError> { if self.is_enabled() { - error!("Attempting to reset the Main Pll Clock, should be resetting its source"); Err(ClockError::ClockNotSupported) } else { Err(ClockError::ClockNotEnabled) @@ -719,7 +712,6 @@ impl ConfigurableClock for MainPllClkConfig { } fn set_clock_rate(&mut self, div: u8, mult: u8, freq: u32) -> Result<(), ClockError> { if self.is_enabled() { - trace!("attempting to set main pll clock rate"); // SAFETY: unsafe needed to take pointers to Sysctl0 and Clkctl0 let clkctl0 = unsafe { crate::pac::Clkctl0::steal() }; let sysctl0 = unsafe { crate::pac::Sysctl0::steal() }; @@ -741,15 +733,12 @@ impl ConfigurableClock for MainPllClkConfig { base_rate = r; } MainPllClkSrc::FFRO => { - trace!("found FFRO as source, wait a bit"); delay_loop_clocks(1000, desired_freq); match clkctl0.ffroctl0().read().trim_range().is_ffro_48mhz() { true => base_rate = Into::into(FfroFreq::Ffro48m), false => base_rate = Into::into(FfroFreq::Ffro60m), } - trace!("found ffro rate to be: {:#}", base_rate); if div == 2 { - trace!("dividing FFRO rate by 2"); clkctl0.syspll0clksel().write(|w| w.sel().ffro_div_2()); delay_loop_clocks(150, desired_freq); base_rate /= 2; @@ -763,10 +752,8 @@ impl ConfigurableClock for MainPllClkConfig { } }; base_rate *= u32::from(mult); - trace!("calculated base rate at: {:#}", base_rate); if base_rate != freq { // make sure to power syspll back up before returning the error - error!("invalid frequency found, powering syspll back up before returning error. Check div and mult"); // Clear System PLL reset clkctl0.syspll0ctl0().write(|w| w.reset().normal()); // Power up SYSPLL @@ -775,13 +762,11 @@ impl ConfigurableClock for MainPllClkConfig { .write(|w| w.syspllana_pd().clr_pdruncfg0().syspllldo_pd().clr_pdruncfg0()); return Err(ClockError::InvalidFrequency); } - trace!("setting default num and denom"); // SAFETY: unsafe needed to write the bits for the num and demon fields clkctl0.syspll0num().write(|w| unsafe { w.num().bits(0b0) }); clkctl0.syspll0denom().write(|w| unsafe { w.denom().bits(0b1) }); delay_loop_clocks(30, desired_freq); self.mult.store(mult, Ordering::Relaxed); - trace!("setting self.mult as: {:#}", mult); match mult { 16 => { clkctl0.syspll0ctl0().modify(|_r, w| w.mult().div_16()); @@ -803,7 +788,6 @@ impl ConfigurableClock for MainPllClkConfig { } _ => return Err(ClockError::InvalidMult), } - trace!("clear syspll reset"); // Clear System PLL reset clkctl0.syspll0ctl0().modify(|_r, w| w.reset().normal()); // Power up SYSPLL @@ -819,7 +803,6 @@ impl ConfigurableClock for MainPllClkConfig { clkctl0.syspll0ctl0().modify(|_, w| w.holdringoff_ena().dsiable()); delay_loop_clocks(15, desired_freq); - trace!("setting new PFD0 bits"); // gate the output and clear bits. // SAFETY: unsafe needed to write the bits for pfd0 clkctl0 @@ -833,7 +816,6 @@ impl ConfigurableClock for MainPllClkConfig { .modify(|_r, w| unsafe { w.pfd0_clkgate().not_gated().pfd0().bits(0x12) }); // wait for ready bit to be set delay_loop_clocks(50, desired_freq); - trace!("waiting for mainpll clock to be ready"); while clkctl0.syspll0pfd().read().pfd0_clkrdy().bit_is_clear() {} // clear by writing a 1 clkctl0.syspll0pfd().modify(|_, w| w.pfd0_clkrdy().set_bit()); @@ -854,11 +836,9 @@ impl ConfigurableClock for MainPllClkConfig { impl MainPllClkConfig { /// Calculate the mult value of a desired frequency, return error if invalid pub(self) fn calc_mult(rate: u32, base_freq: u32) -> Result { - trace!("calculating mult for {:#} / {:#}", rate, base_freq); const VALIDMULTS: [u8; 6] = [16, 17, 20, 22, 27, 33]; if rate > base_freq && rate % base_freq == 0 { let mult = (rate / base_freq) as u8; - trace!("verifying that calculated mult {:#} is a valid one", mult); if VALIDMULTS.into_iter().any(|i| i == mult) { Ok(mult) } else { @@ -1112,7 +1092,6 @@ impl ConfigurableClock for MainClkConfig { Ok(()) } fn disable(&self) -> Result<(), ClockError> { - error!("Attempting to reset the main clock, should NOT happen during runtime"); Err(ClockError::ClockNotSupported) } fn get_clock_rate(&self) -> Result { @@ -1120,7 +1099,6 @@ impl ConfigurableClock for MainClkConfig { Ok(rate) } fn set_clock_rate(&mut self, _div: u8, _mult: u8, _freq: u32) -> Result<(), ClockError> { - error!("The multi-source set_clock_rate_and_source method should be used instead of set_clock_rate"); Err(ClockError::ClockNotSupported) } fn is_enabled(&self) -> bool { @@ -1145,7 +1123,6 @@ impl ConfigurableClock for ClkInConfig { } } fn set_clock_rate(&mut self, _div: u8, _mult: u8, freq: u32) -> Result<(), ClockError> { - trace!("Setting value of clk in config, this won't change the clock itself"); self.freq.as_ref().unwrap().store(freq, Ordering::Relaxed); Ok(()) } @@ -1188,7 +1165,6 @@ impl ConfigurableClock for RtcClkConfig { Ok(()) } fn disable(&self) -> Result<(), ClockError> { - error!("Resetting the RTC clock, this should NOT happen during runtime"); Err(ClockError::ClockNotSupported) } fn set_clock_rate(&mut self, _div: u8, _mult: u8, freq: u32) -> Result<(), ClockError> { @@ -1199,7 +1175,6 @@ impl ConfigurableClock for RtcClkConfig { match r { RtcFreq::Default1Hz => { if rtc.ctrl().read().rtc_en().is_enable() { - trace!("Attempting to enable an already enabled clock, RTC 1Hz"); } else { rtc.ctrl().modify(|_r, w| w.rtc_en().enable()); } @@ -1207,7 +1182,6 @@ impl ConfigurableClock for RtcClkConfig { } RtcFreq::HighResolution1khz => { if rtc.ctrl().read().rtc1khz_en().is_enable() { - trace!("Attempting to enable an already enabled clock, RTC 1Hz"); } else { rtc.ctrl().modify(|_r, w| w.rtc1khz_en().enable()); } @@ -1215,7 +1189,6 @@ impl ConfigurableClock for RtcClkConfig { } RtcFreq::SubSecond32kHz => { if rtc.ctrl().read().rtc_subsec_ena().is_enable() { - trace!("Attempting to enable an already enabled clock, RTC 1Hz"); } else { rtc.ctrl().modify(|_r, w| w.rtc_subsec_ena().enable()); } @@ -1245,18 +1218,12 @@ impl ConfigurableClock for RtcClkConfig { impl SysClkConfig { /// Updates the system core clock frequency, SW concept used for systick - fn update_sys_core_clock(&self) { - trace!( - "System core clock has been updated to {:?}, this involves no HW reg writes", - self.sysclkfreq.load(Ordering::Relaxed) - ); - } + fn update_sys_core_clock(&self) {} } impl ConfigurableClock for SysOscConfig { fn enable_and_reset(&self) -> Result<(), ClockError> { if self.state == State::Enabled { - trace!("SysOsc was already enabled"); return Ok(()); } @@ -1498,32 +1465,26 @@ impl ClockOutConfig { /// Using the config, enables all desired clocks to desired clock rates fn init_clock_hw(config: ClockConfig) -> Result<(), ClockError> { if let Err(e) = config.rtc.enable_and_reset() { - error!("couldn't Power on OSC for RTC, result: {:?}", e); return Err(e); } if let Err(e) = config.lposc.enable_and_reset() { - error!("couldn't Power on LPOSC, result: {:?}", e); return Err(e); } if let Err(e) = config.ffro.enable_and_reset() { - error!("couldn't Power on FFRO, result: {:?}", e); return Err(e); } if let Err(e) = config.sfro.enable_and_reset() { - error!("couldn't Power on SFRO, result: {:?}", e); return Err(e); } if let Err(e) = config.sys_osc.enable_and_reset() { - error!("Couldn't enable sys oscillator {:?}", e); return Err(e); } if let Err(e) = config.main_pll_clk.enable_and_reset() { - error!("Couldn't enable main pll clock {:?}", e); return Err(e); } @@ -1542,7 +1503,6 @@ fn init_clock_hw(config: ClockConfig) -> Result<(), ClockError> { init_syscpuahb_clk(); if let Err(e) = config.main_clk.enable_and_reset() { - error!("Couldn't enable main clock {:?}", e); return Err(e); } From 42c62ba8999df08ad34d566f30f0a7199dbae083 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Wed, 7 May 2025 10:40:43 -0700 Subject: [PATCH 4/4] Add OS Event timer support Allow for the use of the OS Event timer as a time source. Signed-off-by: Felipe Balbi --- embassy-imxrt/Cargo.toml | 9 +- embassy-imxrt/src/lib.rs | 4 +- embassy-imxrt/src/time_driver.rs | 206 +++++++++++++++++++++++++++---- examples/mimxrt6/Cargo.toml | 4 +- 4 files changed, 189 insertions(+), 34 deletions(-) diff --git a/embassy-imxrt/Cargo.toml b/embassy-imxrt/Cargo.toml index d58de6353..f16002a8d 100644 --- a/embassy-imxrt/Cargo.toml +++ b/embassy-imxrt/Cargo.toml @@ -12,7 +12,7 @@ documentation = "https://docs.embassy.dev/embassy-imxrt" [package.metadata.embassy_docs] src_base = "https://github.com/embassy-rs/embassy/blob/embassy-imxrt-v$VERSION/embassy-imxrt/src/" src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-imxrt/src/" -features = ["defmt", "unstable-pac", "time", "time-driver"] +features = ["defmt", "unstable-pac", "time", "time-driver-os-timer"] flavors = [ { regex_feature = "mimxrt6.*", target = "thumbv8m.main-none-eabihf" } ] @@ -37,9 +37,12 @@ defmt = ["dep:defmt", "embassy-hal-internal/defmt", "embassy-sync/defmt", "mimxr time = ["dep:embassy-time", "embassy-embedded-hal/time"] ## Enable custom embassy time-driver implementation, using 32KHz RTC -time-driver-rtc = ["_time-driver"] +time-driver-rtc = ["_time-driver", "embassy-time-driver?/tick-hz-1_000"] -_time-driver = ["dep:embassy-time-driver", "embassy-time-driver?/tick-hz-1_000", "dep:embassy-time-queue-utils", "embassy-embedded-hal/time"] +## Enable custom embassy time-driver implementation, using 1MHz OS Timer +time-driver-os-timer = ["_time-driver", "embassy-time-driver?/tick-hz-1_000_000"] + +_time-driver = ["dep:embassy-time-driver", "dep:embassy-time-queue-utils", "embassy-embedded-hal/time"] ## Reexport the PAC for the currently enabled chip at `embassy_imxrt::pac` (unstable) unstable-pac = [] diff --git a/embassy-imxrt/src/lib.rs b/embassy-imxrt/src/lib.rs index fdf07b433..ad9f81e88 100644 --- a/embassy-imxrt/src/lib.rs +++ b/embassy-imxrt/src/lib.rs @@ -132,12 +132,10 @@ pub fn init(config: config::Config) -> Peripherals { error!("unable to initialize Clocks for reason: {:?}", e); // Panic here? } - gpio::init(); } - - // init RTC time driver #[cfg(feature = "_time-driver")] time_driver::init(config.time_interrupt_priority); + gpio::init(); peripherals } diff --git a/embassy-imxrt/src/time_driver.rs b/embassy-imxrt/src/time_driver.rs index 56a8f7397..c68679d3e 100644 --- a/embassy-imxrt/src/time_driver.rs +++ b/embassy-imxrt/src/time_driver.rs @@ -1,5 +1,6 @@ -//! RTC Time Driver. +//! Time Driver. use core::cell::{Cell, RefCell}; +#[cfg(feature = "time-driver-rtc")] use core::sync::atomic::{compiler_fence, AtomicU32, Ordering}; use critical_section::CriticalSection; @@ -8,27 +9,11 @@ use embassy_sync::blocking_mutex::Mutex; use embassy_time_driver::Driver; use embassy_time_queue_utils::Queue; +#[cfg(feature = "time-driver-os-timer")] +use crate::clocks::enable; use crate::interrupt::InterruptExt; use crate::{interrupt, pac}; -fn rtc() -> &'static pac::rtc::RegisterBlock { - unsafe { &*pac::Rtc::ptr() } -} - -/// Calculate the timestamp from the period count and the tick count. -/// -/// To get `now()`, `period` is read first, then `counter` is read. If the counter value matches -/// the expected range for the `period` parity, we're done. If it doesn't, this means that -/// a new period start has raced us between reading `period` and `counter`, so we assume the `counter` value -/// corresponds to the next period. -/// -/// the 1kHz RTC counter is 16 bits and RTC doesn't have separate compare channels, -/// so using a 32 bit GPREG0-2 as counter, compare, and int_en -/// `period` is a 32bit integer, gpreg 'counter' is 31 bits plus the parity bit for overflow detection -fn calc_now(period: u32, counter: u32) -> u64 { - ((period as u64) << 31) + ((counter ^ ((period & 1) << 31)) as u64) -} - struct AlarmState { timestamp: Cell, } @@ -43,6 +28,34 @@ impl AlarmState { } } +#[cfg(feature = "time-driver-rtc")] +fn rtc() -> &'static pac::rtc::RegisterBlock { + unsafe { &*pac::Rtc::ptr() } +} + +/// Calculate the timestamp from the period count and the tick count. +/// +/// To get `now()`, `period` is read first, then `counter` is read. If the counter value matches +/// the expected range for the `period` parity, we're done. If it doesn't, this means that +/// a new period start has raced us between reading `period` and `counter`, so we assume the `counter` value +/// corresponds to the next period. +/// +/// the 1kHz RTC counter is 16 bits and RTC doesn't have separate compare channels, +/// so using a 32 bit GPREG0-2 as counter, compare, and int_en +/// `period` is a 32bit integer, gpreg 'counter' is 31 bits plus the parity bit for overflow detection +#[cfg(feature = "time-driver-rtc")] +fn calc_now(period: u32, counter: u32) -> u64 { + ((period as u64) << 31) + ((counter ^ ((period & 1) << 31)) as u64) +} + +#[cfg(feature = "time-driver-rtc")] +embassy_time_driver::time_driver_impl!(static DRIVER: Rtc = Rtc { + period: AtomicU32::new(0), + alarms: Mutex::const_new(CriticalSectionRawMutex::new(), AlarmState::new()), + queue: Mutex::new(RefCell::new(Queue::new())), +}); + +#[cfg(feature = "time-driver-rtc")] struct Rtc { /// Number of 2^31 periods elapsed since boot. period: AtomicU32, @@ -51,12 +64,7 @@ struct Rtc { queue: Mutex>, } -embassy_time_driver::time_driver_impl!(static DRIVER: Rtc = Rtc { - period: AtomicU32::new(0), - alarms: Mutex::const_new(CriticalSectionRawMutex::new(), AlarmState::new()), - queue: Mutex::new(RefCell::new(Queue::new())), -}); - +#[cfg(feature = "time-driver-rtc")] impl Rtc { /// Access the GPREG0 register to use it as a 31-bit counter. #[inline] @@ -219,6 +227,7 @@ impl Rtc { } } +#[cfg(feature = "time-driver-rtc")] impl Driver for Rtc { fn now(&self) -> u64 { // `period` MUST be read before `counter`, see comment at the top for details. @@ -242,13 +251,158 @@ impl Driver for Rtc { } } -#[cfg(feature = "rt")] +#[cfg(all(feature = "rt", feature = "time-driver-rtc"))] #[allow(non_snake_case)] #[interrupt] fn RTC() { DRIVER.on_interrupt() } +#[cfg(feature = "time-driver-os-timer")] +fn os() -> &'static pac::ostimer0::RegisterBlock { + unsafe { &*pac::Ostimer0::ptr() } +} + +/// Convert gray to decimal +/// +/// Os Event provides a 64-bit timestamp gray-encoded. All we have to +/// do here is read both 32-bit halves of the register and convert +/// from gray to regular binary. +#[cfg(feature = "time-driver-os-timer")] +fn gray_to_dec(gray: u64) -> u64 { + let mut dec = gray; + + dec ^= dec >> 1; + dec ^= dec >> 2; + dec ^= dec >> 4; + dec ^= dec >> 8; + dec ^= dec >> 16; + dec ^= dec >> 32; + + dec +} + +/// Convert decimal to gray +/// +/// Before writing match value to the target register, we must convert +/// it back into gray code. +#[cfg(feature = "time-driver-os-timer")] +fn dec_to_gray(dec: u64) -> u64 { + let gray = dec; + gray ^ (gray >> 1) +} + +#[cfg(feature = "time-driver-os-timer")] +embassy_time_driver::time_driver_impl!(static DRIVER: OsTimer = OsTimer { + alarms: Mutex::const_new(CriticalSectionRawMutex::new(), AlarmState::new()), + queue: Mutex::new(RefCell::new(Queue::new())), +}); + +#[cfg(feature = "time-driver-os-timer")] +struct OsTimer { + /// Timestamp at which to fire alarm. u64::MAX if no alarm is scheduled. + alarms: Mutex, + queue: Mutex>, +} + +#[cfg(feature = "time-driver-os-timer")] +impl OsTimer { + fn init(&'static self, irq_prio: crate::interrupt::Priority) { + // init alarms + critical_section::with(|cs| { + let alarm = DRIVER.alarms.borrow(cs); + alarm.timestamp.set(u64::MAX); + }); + + // Enable clocks. Documentation advises AGAINST resetting this + // peripheral. + enable::(); + + interrupt::OS_EVENT.disable(); + + // Make sure interrupt is masked + os().osevent_ctrl().modify(|_, w| w.ostimer_intena().clear_bit()); + + // Default to the end of time + os().match_l().write(|w| unsafe { w.bits(0xffff_ffff) }); + os().match_h().write(|w| unsafe { w.bits(0xffff_ffff) }); + + interrupt::OS_EVENT.unpend(); + interrupt::OS_EVENT.set_priority(irq_prio); + unsafe { interrupt::OS_EVENT.enable() }; + } + + fn set_alarm(&self, cs: CriticalSection, timestamp: u64) -> bool { + let alarm = self.alarms.borrow(cs); + alarm.timestamp.set(timestamp); + + let t = self.now(); + if timestamp <= t { + os().osevent_ctrl().modify(|_, w| w.ostimer_intena().clear_bit()); + alarm.timestamp.set(u64::MAX); + return false; + } + + let gray_timestamp = dec_to_gray(timestamp); + + os().match_l() + .write(|w| unsafe { w.bits(gray_timestamp as u32 & 0xffff_ffff) }); + os().match_h() + .write(|w| unsafe { w.bits((gray_timestamp >> 32) as u32) }); + os().osevent_ctrl().modify(|_, w| w.ostimer_intena().set_bit()); + + true + } + + #[cfg(feature = "rt")] + fn trigger_alarm(&self, cs: CriticalSection) { + let mut next = self.queue.borrow(cs).borrow_mut().next_expiration(self.now()); + while !self.set_alarm(cs, next) { + next = self.queue.borrow(cs).borrow_mut().next_expiration(self.now()); + } + } + + #[cfg(feature = "rt")] + fn on_interrupt(&self) { + critical_section::with(|cs| { + if os().osevent_ctrl().read().ostimer_intrflag().bit_is_set() { + os().osevent_ctrl().modify(|_, w| w.ostimer_intena().clear_bit()); + self.trigger_alarm(cs); + } + }); + } +} + +#[cfg(feature = "time-driver-os-timer")] +impl Driver for OsTimer { + fn now(&self) -> u64 { + let mut t = os().evtimerh().read().bits() as u64; + t <<= 32; + t |= os().evtimerl().read().bits() as u64; + gray_to_dec(t) + } + + fn schedule_wake(&self, at: u64, waker: &core::task::Waker) { + critical_section::with(|cs| { + let mut queue = self.queue.borrow(cs).borrow_mut(); + + if queue.schedule_wake(at, waker) { + let mut next = queue.next_expiration(self.now()); + while !self.set_alarm(cs, next) { + next = queue.next_expiration(self.now()); + } + } + }) + } +} + +#[cfg(all(feature = "rt", feature = "time-driver-os-timer"))] +#[allow(non_snake_case)] +#[interrupt] +fn OS_EVENT() { + DRIVER.on_interrupt() +} + pub(crate) fn init(irq_prio: crate::interrupt::Priority) { DRIVER.init(irq_prio) } diff --git a/examples/mimxrt6/Cargo.toml b/examples/mimxrt6/Cargo.toml index 8fc510c47..b0c56f003 100644 --- a/examples/mimxrt6/Cargo.toml +++ b/examples/mimxrt6/Cargo.toml @@ -12,8 +12,8 @@ defmt-rtt = "1.0" embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] } embassy-futures = { version = "0.1.1", path = "../../embassy-futures" } -embassy-imxrt = { version = "0.1.0", path = "../../embassy-imxrt", features = ["defmt", "mimxrt685s", "unstable-pac", "time", "time-driver-rtc"] } -embassy-time = { version = "0.4", path = "../../embassy-time" } +embassy-imxrt = { version = "0.1.0", path = "../../embassy-imxrt", features = ["defmt", "mimxrt685s", "unstable-pac", "time", "time-driver-os-timer"] } +embassy-time = { version = "0.4", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } embassy-sync = { version = "0.6.2", path = "../../embassy-sync", features = ["defmt"] } embedded-hal-1 = { package = "embedded-hal", version = "1.0" } embedded-hal-async = "1.0.0"