diff --git a/Cargo.toml b/Cargo.toml index 4972078..e4e7b9f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,14 +21,16 @@ andes-riscv = { version = "0.1.1" } riscv = { version = "0.11", features = ["critical-section-single-hart"] } embedded-hal = { version = "1.0.0" } -embassy-time-driver = { version = "0.1.0", features = [ +embassy-time-driver = { version = "0.2.0", features = [ "tick-hz-1_000_000", ], optional = true } +embassy-time-queue-utils = { version = "0.1.0", optional = true } embassy-sync = { version = "0.6.1" } embassy-futures = { version = "0.1.1" } embassy-hal-internal = { version = "0.2.0", default-features = false } -embassy-time = { version = "0.3.2", optional = true } +embassy-time = { version = "0.4.0", optional = true } embassy-usb-driver = { version = "0.1.0" } +portable-atomic = "1" critical-section = "1.1.3" static_assertions = "1" @@ -61,7 +63,7 @@ default = ["rt", "embassy", "time"] rt = ["dep:hpm-riscv-rt", "hpm-metapac/rt"] defmt = ["dep:defmt", "embassy-usb-driver/defmt"] time = ["dep:embassy-time"] -embassy = ["dep:embassy-time-driver"] +embassy = ["dep:embassy-time-driver", "dep:embassy-time-queue-utils"] usb-pin-reuse-hpm5300 = [] mcan = ["dep:mcan"] diff --git a/examples/hpm5300evk/Cargo.toml b/examples/hpm5300evk/Cargo.toml index 9e3639e..c79308d 100644 --- a/examples/hpm5300evk/Cargo.toml +++ b/examples/hpm5300evk/Cargo.toml @@ -24,19 +24,18 @@ embedded-graphics = "0.8.1" embedded-hal = "1.0.0" embedded-hal-async = "1.0.0" embedded-io = "0.6.1" -embedded-hal-bus = "0.2.0" +embedded-hal-bus = "0.3.0" -embassy-time = { version = "0.3.0", features = ["tick-hz-1_000_000"] } -embassy-executor = { version = "0.6.3", features = [ +embassy-time = { version = "0.4.0", features = ["tick-hz-1_000_000"] } +embassy-executor = { version = "0.7.0", features = [ "nightly", - "integrated-timers", "arch-riscv32", "executor-thread", ] } -embassy-embedded-hal = "0.2.0" +embassy-embedded-hal = "0.3.0" embassy-sync = "0.6.1" -embassy-usb = { version = "0.3.0", features = [ +embassy-usb = { version = "0.4.0", features = [ "defmt", "max-handler-count-8", "max-interface-count-8", diff --git a/examples/hpm6200evk/Cargo.toml b/examples/hpm6200evk/Cargo.toml index 6788254..55cbcc6 100644 --- a/examples/hpm6200evk/Cargo.toml +++ b/examples/hpm6200evk/Cargo.toml @@ -14,21 +14,20 @@ assign-resources = "0.4.1" heapless = "0.8.0" # embassy dependencies -embassy-time = { version = "0.3.0", features = ["tick-hz-1_000_000"] } -embassy-executor = { version = "0.6.3", features = [ +embassy-time = { version = "0.4.0", features = ["tick-hz-1_000_000"] } +embassy-executor = { version = "0.7.0", features = [ # "nightly", - "integrated-timers", "arch-riscv32", "executor-thread", ] } -embassy-embedded-hal = "0.2.0" +embassy-embedded-hal = "0.3.0" embassy-sync = "0.6.1" # embedded-hal ecosystem embedded-hal = "1.0.0" embedded-hal-async = "1.0.0" embedded-io = "0.6.1" -embedded-hal-bus = "0.2.0" +embedded-hal-bus = "0.3.0" riscv = "0.12.1" andes-riscv = "0.1.2" diff --git a/examples/hpm6300evk/Cargo.toml b/examples/hpm6300evk/Cargo.toml index 2627880..c1e4c45 100644 --- a/examples/hpm6300evk/Cargo.toml +++ b/examples/hpm6300evk/Cargo.toml @@ -16,6 +16,11 @@ embedded-hal = "1.0.0" panic-halt = "1.0.0" riscv = { version = "0.11.1", features = ["critical-section-single-hart"] } andes-riscv = "0.1.2" +embassy-executor = { version = "0.7.0", features = [ + "nightly", + "arch-riscv32", + "executor-thread", +] } [profile.release] strip = false # symbols are not flashed to the microcontroller, so don't strip them. diff --git a/examples/hpm6750evkmini/Cargo.toml b/examples/hpm6750evkmini/Cargo.toml index 9f4a200..6af0f7b 100644 --- a/examples/hpm6750evkmini/Cargo.toml +++ b/examples/hpm6750evkmini/Cargo.toml @@ -14,13 +14,13 @@ edition = "2021" # defmt = "0.3.8" # defmt-rtt = "0.4.1" # no defmt -hpm-hal = { path = "../..", features = [ +hpm-hal = { path = "../../", features = [ "rt", "embassy", "hpm6750", "chrono", "time", -], no-default-features = true } +], default-features = false } embedded-hal = "1.0.0" @@ -29,10 +29,9 @@ embedded-hal = "1.0.0" panic-halt = "1.0.0" riscv = { version = "0.11.1", features = ["critical-section-single-hart"] } riscv-semihosting = "0.1.0" -embassy-time = { version = "0.3.0", features = ["tick-hz-1_000_000"] } -embassy-executor = { version = "0.6.3", features = [ +embassy-time = { version = "0.4.0", features = ["tick-hz-1_000_000"] } +embassy-executor = { version = "0.7.0", features = [ # "nightly", - "integrated-timers", "arch-riscv32", "executor-thread", ] } @@ -41,8 +40,8 @@ embedded-io = "0.6.1" futures-util = { version = "0.3.30", default-features = false } assign-resources = "0.4.1" embedded-graphics = "0.8.1" -embedded-hal-bus = { version = "0.2.0", features = ["async"] } -embassy-usb = { version = "0.3.0", features = [ +embedded-hal-bus = { version = "0.3.0", features = ["async"] } +embassy-usb = { version = "0.4.0", features = [ "max-handler-count-8", "max-interface-count-8", ] } diff --git a/examples/hpm6e00evk/Cargo.toml b/examples/hpm6e00evk/Cargo.toml index 5d1df93..277bfb5 100644 --- a/examples/hpm6e00evk/Cargo.toml +++ b/examples/hpm6e00evk/Cargo.toml @@ -15,16 +15,15 @@ embedded-hal = "1.0.0" riscv = { version = "0.11.1", features = ["critical-section-single-hart"] } heapless = "0.8.0" -embassy-time = { version = "0.3.0", features = ["tick-hz-1_000_000"] } -embassy-executor = { version = "0.6.3", features = [ +embassy-time = { version = "0.4.0", features = ["tick-hz-1_000_000"] } +embassy-executor = { version = "0.7.0", features = [ # "nightly", - "integrated-timers", "arch-riscv32", "executor-thread", ] } embedded-io = "0.6.1" embedded-graphics = "0.8.1" -embassy-usb = { version = "0.3.0", features = [ +embassy-usb = { version = "0.4.0", features = [ "defmt", "max-handler-count-8", "max-interface-count-8", @@ -32,7 +31,7 @@ embassy-usb = { version = "0.3.0", features = [ futures-util = { version = "0.3", default-features = false } tinygif = "0.0.4" assign-resources = "0.4.1" -embedded-hal-bus = "0.2.0" +embedded-hal-bus = "0.3.0" embassy-sync = "0.6.1" usbd-hid = "0.8" static_cell = "2" diff --git a/src/embassy/time_driver_mchtmr.rs b/src/embassy/time_driver_mchtmr.rs index 3394e5d..da7ebf7 100644 --- a/src/embassy/time_driver_mchtmr.rs +++ b/src/embassy/time_driver_mchtmr.rs @@ -1,26 +1,21 @@ //! Embassy time driver using machine timer(mchtmr) -use core::cell::Cell; -use core::sync::atomic::{AtomicU32, AtomicU8, Ordering}; -use core::{mem, ptr}; +use core::cell::{Cell, RefCell}; +use core::task::Waker; use critical_section::{CriticalSection, Mutex}; -use embassy_time_driver::AlarmHandle; +use embassy_time_driver::{Driver, TICK_HZ}; +use embassy_time_queue_utils::Queue; use hpm_metapac::sysctl::vals; use hpm_metapac::{MCHTMR, SYSCTL}; +use portable_atomic::{AtomicU64, Ordering}; use crate::sysctl::ClockConfig; use crate::{interrupt, pac}; -pub const ALARM_COUNT: usize = 1; - +// Alarm state structure to manage individual alarms struct AlarmState { timestamp: Cell, - - // This is really a Option<(fn(*mut ()), *mut ())> - // but fn pointers aren't allowed in const yet - callback: Cell<*const ()>, - ctx: Cell<*mut ()>, } unsafe impl Send for AlarmState {} @@ -29,26 +24,32 @@ impl AlarmState { const fn new() -> Self { Self { timestamp: Cell::new(u64::MAX), - callback: Cell::new(ptr::null()), - ctx: Cell::new(ptr::null_mut()), } } } -pub struct MachineTimerDriver { - alarm_count: AtomicU8, - alarms: Mutex<[AlarmState; ALARM_COUNT]>, - period: AtomicU32, +// Machine timer based time driver implementation +pub(crate) struct MachineTimerDriver { + // Total number of ticks since system start + ticks: AtomicU64, + // Number of allocated alarms + alarm: Mutex, + queue: Mutex>, } +// Constant initialization for alarm states +#[allow(clippy::declare_interior_mutable_const)] const ALARM_STATE_NEW: AlarmState = AlarmState::new(); + +// Macro to create a static driver instance embassy_time_driver::time_driver_impl!(static DRIVER: MachineTimerDriver = MachineTimerDriver { - period: AtomicU32::new(1), // avoid div by zero - alarm_count: AtomicU8::new(0), - alarms: Mutex::new([ALARM_STATE_NEW; ALARM_COUNT]), + ticks: AtomicU64::new(0), + alarm: Mutex::new(ALARM_STATE_NEW), + queue: Mutex::new(RefCell::new(Queue::new())) }); impl MachineTimerDriver { + // Initialize the machine timer driver fn init(&'static self) { // FIXME: The name in SDK is MCHTMR0 #[cfg(hpm67)] @@ -62,9 +63,6 @@ impl MachineTimerDriver { }; let cnt_per_second = crate::sysctl::clocks().get_freq(&mchtmr_cfg).0 as u64; - let cnt_per_tick = cnt_per_second / embassy_time_driver::TICK_HZ; - - self.period.store(cnt_per_tick as u32, Ordering::Relaxed); // make sure mchtmr will not be gated on "wfi" // Design consideration: use WAIT is also useful to enter low-power mode @@ -87,103 +85,71 @@ impl MachineTimerDriver { MCHTMR.mtimecmp().write_value(u64::MAX - 1); } - #[inline(always)] + // Machine timer interrupt handler fn on_interrupt(&self) { unsafe { riscv::register::mie::clear_mtimer(); } critical_section::with(|cs| { - self.trigger_alarm(cs); + // Increment global tick counter + let current_ticks = self.ticks.fetch_add(1, Ordering::Relaxed); + self.check_and_trigger_alarm(current_ticks, cs); }) } - #[inline(always)] - fn trigger_alarm(&self, cs: CriticalSection) { - let alarm = &self.alarms.borrow(cs)[0]; - alarm.timestamp.set(u64::MAX); - - // Call after clearing alarm, so the callback can set another alarm. - - // safety: - // - we can ignore the possiblity of `f` being unset (null) because of the safety contract of `allocate_alarm`. - // - other than that we only store valid function pointers into alarm.callback - let f: fn(*mut ()) = unsafe { mem::transmute(alarm.callback.get()) }; - f(alarm.ctx.get()); + // Check if an alarm is due and trigger it if necessary + #[inline] + fn check_and_trigger_alarm(&self, current_time: u64, cs: CriticalSection) { + let alarm = &self.alarm.borrow(cs); + let alarm_timestamp = alarm.timestamp.get(); + + // Check if alarm is scheduled and due + if alarm_timestamp != u64::MAX && current_time >= alarm_timestamp { + let mut next = self.queue.borrow(cs).borrow_mut().next_expiration(current_time); + while !self.set_alarm(cs, next) { + next = self.queue.borrow(cs).borrow_mut().next_expiration(self.now()); + } + } } - fn get_alarm<'a>(&'a self, cs: CriticalSection<'a>, alarm: AlarmHandle) -> &'a AlarmState { - // safety: we're allowed to assume the AlarmState is created by us, and - // we never create one that's out of bounds. - unsafe { self.alarms.borrow(cs).get_unchecked(alarm.id() as usize) } + // Set alarm timestamp + fn set_alarm(&self, cs: CriticalSection, timestamp: u64) -> bool { + if self.now() >= timestamp { + // Alarm time has passed, cannot set + return false; + } + self.alarm.borrow(cs).timestamp.set(timestamp); + if self.now() >= timestamp { + self.alarm.borrow(cs).timestamp.set(u64::MAX); + return false; + } + true } } -impl embassy_time_driver::Driver for MachineTimerDriver { +// Implement the Driver trait for MachineTimerDriver +impl Driver for MachineTimerDriver { + // Get current system time in ticks fn now(&self) -> u64 { - MCHTMR.mtime().read() / self.period.load(Ordering::Relaxed) as u64 + self.ticks.load(Ordering::Relaxed) } - unsafe fn allocate_alarm(&self) -> Option { - let id = self.alarm_count.fetch_update(Ordering::AcqRel, Ordering::Acquire, |x| { - if x < ALARM_COUNT as u8 { - Some(x + 1) - } else { - None - } - }); - - match id { - Ok(id) => Some(AlarmHandle::new(id)), - Err(_) => None, - } - } - - fn set_alarm_callback(&self, alarm: AlarmHandle, callback: fn(*mut ()), ctx: *mut ()) { + fn schedule_wake(&self, at: u64, waker: &Waker) { critical_section::with(|cs| { - let alarm = self.get_alarm(cs, alarm); - - alarm.callback.set(callback as *const ()); - alarm.ctx.set(ctx); - }) - } - - fn set_alarm(&self, alarm: AlarmHandle, timestamp: u64) -> bool { - critical_section::with(|cs| { - let _n = alarm.id(); - - let alarm = self.get_alarm(cs, alarm); - alarm.timestamp.set(timestamp); + let mut queue = self.queue.borrow(cs).borrow_mut(); - let t = self.now(); - if timestamp <= t { - // If alarm timestamp has passed the alarm will not fire. - // Disarm the alarm and return `false` to indicate that. - unsafe { - riscv::register::mie::clear_mtimer(); + 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()); } - - alarm.timestamp.set(u64::MAX); - - return false; - } - - let safe_timestamp = timestamp - .saturating_add(1) - .overflowing_mul(self.period.load(Ordering::Relaxed) as u64) - .0; - - MCHTMR.mtimecmp().write_value(safe_timestamp); - unsafe { - riscv::register::mie::set_mtimer(); } - - true }) } } -// Core local interrupts are handled in CORE_LOCAL +// Machine timer interrupt handler (to be implemented in your interrupt vector) #[interrupt] fn MachineTimer() { DRIVER.on_interrupt();