use crate::peripherals::Tcxo;
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub struct Instant(u64);
impl Instant {
#[instability::unstable]
pub fn now() -> Self {
let tcxo = unsafe { &*Tcxo::ptr() };
let status = tcxo.tcxo_status().read().bits();
unsafe {
tcxo.tcxo_status().write(|w| w.bits(status | 0x01));
}
while tcxo.tcxo_status().read().bits() & 0x10 == 0 {}
let c0 = tcxo.tcxo_count0().read().bits() as u64;
let c1 = tcxo.tcxo_count1().read().bits() as u64;
let c2 = tcxo.tcxo_count2().read().bits() as u64;
let c3 = tcxo.tcxo_count3().read().bits() as u64;
Instant((c3 << 48) | (c2 << 32) | (c1 << 16) | c0)
}
#[instability::unstable]
pub fn elapsed(&self) -> Duration {
Instant::now().checked_duration_since(*self).unwrap_or(Duration::from_micros(0))
}
pub fn duration_since(&self, earlier: Instant) -> Duration {
self.checked_duration_since(earlier).unwrap_or(Duration::from_micros(0))
}
pub fn checked_duration_since(&self, earlier: Instant) -> Option<Duration> {
if self.0 >= earlier.0 { Some(Duration::from_micros(self.0 - earlier.0)) } else { None }
}
pub fn raw(&self) -> u64 {
self.0
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Default)]
pub struct Duration(u64);
impl Duration {
pub const fn from_micros(micros: u64) -> Self {
Duration(micros)
}
pub const fn from_millis(millis: u64) -> Self {
Duration(millis * 1_000)
}
pub const fn from_secs(secs: u64) -> Self {
Duration(secs * 1_000_000)
}
pub const fn as_micros(&self) -> u64 {
self.0
}
pub const fn as_millis(&self) -> u64 {
self.0 / 1_000
}
pub const fn as_secs(&self) -> u64 {
self.0 / 1_000_000
}
}
impl core::ops::Add for Duration {
type Output = Self;
fn add(self, rhs: Self) -> Self {
Duration(self.0 + rhs.0)
}
}
impl core::ops::AddAssign for Duration {
fn add_assign(&mut self, rhs: Self) {
self.0 += rhs.0;
}
}
impl core::ops::Sub for Duration {
type Output = Self;
fn sub(self, rhs: Self) -> Self {
Duration(self.0.saturating_sub(rhs.0))
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Rate(u32);
impl Rate {
pub const fn from_hz(hz: u32) -> Self {
Rate(hz)
}
pub const fn from_khz(khz: u32) -> Self {
Rate(khz * 1_000)
}
pub const fn from_mhz(mhz: u32) -> Self {
Rate(mhz * 1_000_000)
}
pub const fn to_hz(&self) -> u32 {
self.0
}
pub const fn to_khz(&self) -> u32 {
self.0 / 1_000
}
}
#[cfg(all(test, not(target_arch = "riscv32")))]
mod tests {
use super::*;
#[test]
fn test_duration_constructors() {
assert_eq!(Duration::from_micros(500).as_micros(), 500);
assert_eq!(Duration::from_millis(1).as_micros(), 1000);
assert_eq!(Duration::from_secs(1).as_micros(), 1_000_000);
assert_eq!(Duration::from_micros(1500).as_millis(), 1);
}
#[test]
fn test_duration_add_sub() {
let a = Duration::from_micros(100);
let b = Duration::from_micros(50);
assert_eq!((a + b).as_micros(), 150);
assert_eq!((a - b).as_micros(), 50);
assert_eq!((b - a).as_micros(), 0);
}
#[test]
fn test_duration_default() {
assert_eq!(Duration::default().as_micros(), 0);
}
#[test]
fn test_rate_constructors() {
assert_eq!(Rate::from_hz(1000).to_hz(), 1000);
assert_eq!(Rate::from_khz(1).to_hz(), 1000);
assert_eq!(Rate::from_mhz(1).to_hz(), 1_000_000);
assert_eq!(Rate::from_mhz(240).to_khz(), 240_000);
}
#[test]
fn test_instant_checked_duration() {
let early = Instant(100);
let late = Instant(200);
assert_eq!(late.checked_duration_since(early).unwrap().as_micros(), 100);
assert!(early.checked_duration_since(late).is_none());
}
}
#[cfg(all(test, not(target_arch = "riscv32")))]
mod proptests {
use super::{Duration, Instant, Rate};
use proptest::prelude::*;
proptest! {
#[test]
fn duration_millis_roundtrip(ms in 0u64..=(u64::MAX / 1_000)) {
let d = Duration::from_millis(ms);
prop_assert_eq!(d.as_micros(), ms * 1_000);
prop_assert_eq!(d.as_millis(), ms);
}
#[test]
fn duration_secs_roundtrip(s in 0u64..=(u64::MAX / 1_000_000)) {
let d = Duration::from_secs(s);
prop_assert_eq!(d.as_micros(), s * 1_000_000);
prop_assert_eq!(d.as_secs(), s);
}
#[test]
fn duration_unit_conversions_floor(us in any::<u64>()) {
let d = Duration::from_micros(us);
prop_assert_eq!(d.as_micros(), us);
prop_assert_eq!(d.as_millis(), us / 1_000);
prop_assert_eq!(d.as_secs(), us / 1_000_000);
prop_assert!(d.as_millis() * 1_000 <= us);
prop_assert!(d.as_secs() * 1_000_000 <= us);
prop_assert!(us - d.as_millis() * 1_000 < 1_000);
prop_assert!(us - d.as_secs() * 1_000_000 < 1_000_000);
}
#[test]
fn duration_sub_saturates(a in any::<u64>(), b in any::<u64>()) {
let da = Duration::from_micros(a);
let db = Duration::from_micros(b);
let diff = (da - db).as_micros();
prop_assert_eq!(diff, a.saturating_sub(b));
if b >= a {
prop_assert_eq!(diff, 0);
}
}
#[test]
fn duration_add_exact(a in any::<u64>(), b in any::<u64>()) {
prop_assume!(a.checked_add(b).is_some());
let sum = (Duration::from_micros(a) + Duration::from_micros(b)).as_micros();
prop_assert_eq!(sum, a + b);
}
#[test]
fn instant_checked_duration_since(later in any::<u64>(), earlier in any::<u64>()) {
let li = Instant(later);
let ei = Instant(earlier);
match li.checked_duration_since(ei) {
Some(d) => {
prop_assert!(later >= earlier);
prop_assert_eq!(d.as_micros(), later - earlier);
}
None => prop_assert!(later < earlier),
}
}
#[test]
fn instant_duration_since_floors(a in any::<u64>(), b in any::<u64>()) {
let ai = Instant(a);
let bi = Instant(b);
prop_assert_eq!(ai.duration_since(bi).as_micros(), a.saturating_sub(b));
}
#[test]
fn instant_raw_identity(v in any::<u64>()) {
prop_assert_eq!(Instant(v).raw(), v);
}
#[test]
fn rate_khz_roundtrip(khz in 0u32..=(u32::MAX / 1_000)) {
let r = Rate::from_khz(khz);
prop_assert_eq!(r.to_hz(), khz * 1_000);
prop_assert_eq!(r.to_khz(), khz);
}
#[test]
fn rate_hz_and_mhz(hz in any::<u32>(), mhz in 0u32..=(u32::MAX / 1_000_000)) {
prop_assert_eq!(Rate::from_hz(hz).to_hz(), hz);
prop_assert_eq!(Rate::from_mhz(mhz).to_hz(), mhz * 1_000_000);
prop_assert_eq!(Rate::from_hz(hz).to_khz(), hz / 1_000);
}
}
}