use core::cell::RefCell;
use embassy_sync::blocking_mutex::Mutex;
use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
use embassy_time_driver::Driver;
use embassy_time_queue_utils::Queue;
use mpfs_hal::pac;
const MCYCLE_VS_TIMER_RATIO: u64 =
pac::LIBERO_SETTING_MSS_SYSTEM_CLK as u64 / pac::LIBERO_SETTING_MSS_APB_AHB_CLK as u64;
struct TimeDriver {
queue: Mutex<CriticalSectionRawMutex, RefCell<Queue>>,
}
embassy_time_driver::time_driver_impl!(static DRIVER: TimeDriver = TimeDriver {
queue: Mutex::const_new(CriticalSectionRawMutex::new(), RefCell::new(Queue::new())),
});
impl Driver for TimeDriver {
fn now(&self) -> u64 {
unsafe { pac::readmcycle() }
}
fn schedule_wake(&self, at: u64, waker: &core::task::Waker) {
critical_section::with(|cs| {
let mut queue = self.queue.borrow(cs).borrow_mut();
#[cfg(feature = "debug-logs")]
mpfs_hal::print_unguarded!("schedule wake for hart {} at {}\n", pac::hart_id(), at);
if queue.schedule_wake(at, waker) {
let mut next = queue.next_expiration(self.now());
while !self.set_alarm(next) {
next = queue.next_expiration(self.now());
}
}
})
}
}
impl TimeDriver {
fn set_alarm(&self, timestamp: u64) -> bool {
#[cfg(feature = "debug-logs")]
mpfs_hal::print_unguarded!("Setting alarm for hart {} to {}\n", hart_id, timestamp,);
let now = self.now();
if timestamp <= now {
#[cfg(feature = "debug-logs")]
mpfs_hal::print_unguarded!("Alarm already expired\n");
return false; }
if timestamp == u64::MAX {
#[cfg(feature = "debug-logs")]
mpfs_hal::print_unguarded!("Alarm set to max\n");
return true; }
#[cfg(feature = "debug-logs")]
mpfs_hal::print_unguarded!("Setting alarm\n");
let diff = timestamp - now;
self._set_alarm(diff);
true
}
fn _set_alarm(&self, interval: u64) {
let mut counter = interval / MCYCLE_VS_TIMER_RATIO;
if counter == 0 {
counter = 1;
}
let load_value_u = (counter >> 32) as u32;
let load_value_l = counter as u32;
unsafe {
pac::MSS_TIM64_load_immediate(pac::TIMER_LO, load_value_u, load_value_l);
pac::MSS_TIM64_start(pac::TIMER_LO);
pac::MSS_TIM64_enable_irq(pac::TIMER_LO);
}
}
fn trigger_alarm(&self) -> bool {
let ret = critical_section::with(|cs| {
let mut queue = self.queue.borrow(cs).borrow_mut();
let mut next = queue.next_expiration(self.now());
while !self.set_alarm(next) {
next = queue.next_expiration(self.now());
}
next != u64::MAX
});
unsafe {
if !ret {
pac::MSS_TIM64_stop(pac::TIMER_LO);
}
pac::MSS_TIM64_clear_irq(pac::TIMER_LO);
}
ret
}
}
pub(crate) fn init() {
unsafe {
pac::mss_config_clk_rst(
pac::mss_peripherals__MSS_PERIPH_TIMER,
pac::MPFS_HAL_FIRST_HART as u8,
pac::PERIPH_RESET_STATE__PERIPHERAL_ON,
);
pac::PLIC_SetPriority(pac::PLIC_IRQn_Type_PLIC_TIMER1_INT_OFFSET, 2);
pac::PLIC_SetPriority(pac::PLIC_IRQn_Type_PLIC_TIMER2_INT_OFFSET, 2);
pac::reset_mtime();
pac::MSS_TIM64_init(pac::TIMER_LO, pac::__mss_timer_mode_MSS_TIMER_ONE_SHOT_MODE);
}
}
#[unsafe(no_mangle)]
extern "C" fn PLIC_timer1_IRQHandler() -> u8 {
#[cfg(feature = "debug-logs")]
mpfs_hal::print_unguarded!("Hart {} timer! at {}\n", pac::hart_id(), DRIVER.now());
let pending = DRIVER.trigger_alarm();
#[cfg(feature = "debug-logs")]
mpfs_hal::print_unguarded!("returning from timer\n");
return if pending {
pac::EXT_IRQ_KEEP_ENABLED
} else {
pac::EXT_IRQ_DISABLE
} as u8;
}