use crate::{
Clock,
wrapping_u64::{U64Calibration, WrappingU64Duration, WrappingU64Instant, WrappingU64Time},
};
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
pub struct GenericTimer<const BITS: u32 = 64>(());
impl<const BITS: u32> Clock for GenericTimer<BITS> {
type Time = WrappingU64Time<BITS>;
type Calibration = U64Calibration<BITS>;
#[inline(always)]
fn now(&self) -> WrappingU64Instant<BITS> {
let ticks: u64;
unsafe {
core::arch::asm!("mrs {}, cntvct_el0", out(reg) ticks, options(nomem, nostack));
}
WrappingU64Instant::wrapping_new(ticks)
}
}
#[derive(Debug)]
#[non_exhaustive]
pub struct GenericTimerUnavailable;
impl core::fmt::Display for GenericTimerUnavailable {
fn fmt(&self, formatter: &mut core::fmt::Formatter) -> core::fmt::Result {
formatter.write_str("No generic timer available")
}
}
#[cfg(feature = "std")]
impl std::error::Error for GenericTimerUnavailable {}
impl<const BITS: u32> GenericTimer<BITS> {
const CHECK_BITS: () = assert!(BITS >= 56 && BITS <= 64, "BITS must be in 56..=64");
pub fn new_assume_accessible() -> Self {
const { Self::CHECK_BITS };
GenericTimer(())
}
#[cfg(all(target_os = "linux", feature = "std"))]
pub fn try_new_linux_sys() -> Result<Self, GenericTimerUnavailable> {
const { Self::CHECK_BITS };
let counter_detected = std::fs::read_to_string(
"/sys/devices/system/clocksource/clocksource0/available_clocksource",
)
.is_ok_and(|x| x.split_whitespace().any(|x| x == "arch_sys_counter"));
if counter_detected {
Ok(GenericTimer(()))
} else {
Err(GenericTimerUnavailable)
}
}
pub fn read_cntfrq(&self) -> u32 {
let frequency: u32;
unsafe {
core::arch::asm!("mrs {:w}, cntfrq_el0", out(reg) frequency, options(pure, nomem, nostack));
}
frequency
}
pub fn calibration_from_cntfrq(&self) -> U64Calibration<BITS> {
let frequency = self.read_cntfrq() as u64;
if frequency == 0 {
panic!("cntfrq_el0 = 0");
}
U64Calibration::new(WrappingU64Duration::new(frequency), 1_000_000_000)
}
}