//! Support for RISC-V's timers, using the Sstc extension. #![cfg(target_arch = "riscv64")] #![no_std] use core::{arch::asm, ops::Add, time::Duration}; /// The number of `Instant` "ticks" in a second. Initialized by the early-boot DeviceTree parser. pub static mut TIMEBASE_FREQUENCY: u32 = 0; /// A moment in time. #[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)] pub struct Instant(u64); impl Instant { /// Returns the current time as an `Instant`. pub fn now() -> Instant { let out; // SAFETY: We require the Sstc extension, enable it before jumping to Rust, and never // disable it. unsafe { asm!("rdtime {out}", out = out(reg) out, options(nomem, nostack)); } Instant(out) } } impl Add for Instant { type Output = Instant; #[allow(clippy::suspicious_arithmetic_impl)] fn add(self, duration: Duration) -> Instant { // SAFETY: TIMEBASE_FREQUENCY is never concurrently written to. let ticks_per_second = unsafe { TIMEBASE_FREQUENCY }; let ticks = duration.as_nanos().wrapping_mul(ticks_per_second as u128) / 1_000_000_000; Instant(self.0.wrapping_add(ticks as u64)) } } /// Sets the timer interrupt to fire at the given instant. Note that this sets a global value, /// rather than interacting with the scheduler in any way. pub fn set_timer(instant: Instant) { // SAFETY: We require the Sstc extension, enable it before jumping to Rust, and never // disable it. unsafe { asm!("csrw stimecmp, {instant}", instant = in(reg) instant.0, options(nomem, nostack)); } }