moa/emulator/core/src/clock.rs

336 lines
8.0 KiB
Rust

use std::time::Duration;
use std::ops::{Add, AddAssign, Sub, SubAssign, Mul, MulAssign, Div, DivAssign};
/// Type to use for storing femtoseconds
///
/// In webassembly, using u128 results in exceedingly slow runtimes, so we use u64 instead
/// which is enough for 5 hours of simulation time.
#[cfg(not(target_arch = "wasm32"))]
type Femtos = u128;
#[cfg(target_arch = "wasm32")]
type Femtos = u64;
/// Represents a duration of time in femtoseconds
///
/// The `ClockDuration` type is used to represent lengths of time and is
/// intentionally similar to `std::time::Duration`, but which records
/// time as femtoseconds to keep accurancy when dealing with partial
/// nanosecond clock divisons.
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub struct ClockDuration {
femtos: Femtos,
}
impl ClockDuration {
pub const ZERO: Self = Self::from_femtos(0);
pub const MAX: Self = Self::from_femtos(Femtos::MAX);
pub const FEMTOS_PER_SEC: Femtos = 1_000_000_000_000_000;
pub const FEMTOS_PER_MILLISEC: Femtos = 1_000_000_000_000;
pub const FEMTOS_PER_MICROSEC: Femtos = 1_000_000_000;
pub const FEMTOS_PER_NANOSEC: Femtos = 1_000_000;
pub const FEMTOS_PER_PICOSEC: Femtos = 1_000;
#[inline]
pub const fn from_secs(secs: u64) -> Self {
Self {
femtos: secs as Femtos * Self::FEMTOS_PER_SEC,
}
}
#[inline]
pub const fn from_millis(millisecs: u64) -> Self {
Self {
femtos: millisecs as Femtos * Self::FEMTOS_PER_MILLISEC,
}
}
#[inline]
pub const fn from_micros(microsecs: u64) -> Self {
Self {
femtos: microsecs as Femtos * Self::FEMTOS_PER_MICROSEC,
}
}
#[inline]
pub const fn from_nanos(nanosecs: u64) -> Self {
Self {
femtos: nanosecs as Femtos * Self::FEMTOS_PER_NANOSEC,
}
}
#[inline]
pub const fn from_picos(picosecs: u128) -> Self {
Self {
femtos: picosecs as Femtos * Self::FEMTOS_PER_PICOSEC,
}
}
#[inline]
pub const fn from_femtos(femtos: Femtos) -> Self {
Self {
femtos,
}
}
#[inline]
pub const fn as_secs(self) -> u64 {
(self.femtos / Self::FEMTOS_PER_SEC) as u64
}
#[inline]
pub const fn as_millis(self) -> u64 {
(self.femtos / Self::FEMTOS_PER_MILLISEC) as u64
}
#[inline]
pub const fn as_micros(self) -> u64 {
(self.femtos / Self::FEMTOS_PER_MICROSEC) as u64
}
#[inline]
pub const fn as_nanos(self) -> u64 {
(self.femtos / Self::FEMTOS_PER_NANOSEC) as u64
}
#[inline]
#[allow(clippy::unnecessary_cast)]
pub const fn as_picos(self) -> u128 {
(self.femtos / Self::FEMTOS_PER_PICOSEC) as u128
}
#[inline]
pub const fn as_femtos(self) -> Femtos {
self.femtos
}
#[inline]
pub const fn checked_add(self, rhs: Self) -> Option<Self> {
match self.femtos.checked_add(rhs.femtos) {
Some(femtos) => Some(Self::from_femtos(femtos)),
None => None,
}
}
#[inline]
pub const fn checked_sub(self, rhs: Self) -> Option<Self> {
match self.femtos.checked_sub(rhs.femtos) {
Some(femtos) => Some(Self::from_femtos(femtos)),
None => None,
}
}
}
impl Add for ClockDuration {
type Output = Self;
fn add(self, rhs: Self) -> Self::Output {
self.checked_add(rhs).expect("clock duration overflow during addition")
}
}
impl AddAssign for ClockDuration {
fn add_assign(&mut self, rhs: Self) {
*self = *self + rhs;
}
}
impl Sub for ClockDuration {
type Output = Self;
fn sub(self, rhs: Self) -> Self::Output {
self.checked_sub(rhs).expect("clock duration overflow during subtraction")
}
}
impl SubAssign for ClockDuration {
fn sub_assign(&mut self, rhs: Self) {
*self = *self - rhs;
}
}
impl Mul<u64> for ClockDuration {
type Output = Self;
fn mul(self, rhs: u64) -> Self::Output {
Self::from_femtos(self.femtos * rhs as Femtos)
}
}
impl MulAssign<u64> for ClockDuration {
fn mul_assign(&mut self, rhs: u64) {
*self = Self::from_femtos(self.femtos * rhs as Femtos);
}
}
impl Div<u64> for ClockDuration {
type Output = Self;
fn div(self, rhs: u64) -> Self::Output {
Self::from_femtos(self.femtos / rhs as Femtos)
}
}
impl DivAssign<u64> for ClockDuration {
fn div_assign(&mut self, rhs: u64) {
*self = Self::from_femtos(self.femtos / rhs as Femtos);
}
}
impl Div<ClockDuration> for ClockDuration {
type Output = u64;
fn div(self, rhs: ClockDuration) -> Self::Output {
(self.femtos / rhs.femtos) as u64
}
}
impl From<ClockDuration> for Duration {
fn from(value: ClockDuration) -> Self {
Duration::from_nanos(value.as_nanos())
}
}
impl From<Duration> for ClockDuration {
fn from(value: Duration) -> Self {
ClockDuration::from_nanos(value.as_nanos() as u64)
}
}
/// Represents time from the start of the simulation
///
/// `ClockTime` is for representing the current running clock. It uses a
/// duration to represent the time from simulation start, and is monotonic.
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub struct ClockTime(ClockDuration);
impl ClockTime {
pub const START: Self = Self(ClockDuration::ZERO);
pub const FOREVER: Self = Self(ClockDuration::MAX);
#[inline]
pub const fn as_duration(self) -> ClockDuration {
self.0
}
#[inline]
pub fn duration_since(self, other: Self) -> ClockDuration {
self.0 - other.0
}
#[inline]
pub const fn checked_add(self, duration: ClockDuration) -> Option<Self> {
match self.0.checked_add(duration) {
Some(duration) => Some(Self(duration)),
None => None,
}
}
#[inline]
pub const fn checked_sub(self, duration: ClockDuration) -> Option<Self> {
match self.0.checked_sub(duration) {
Some(duration) => Some(Self(duration)),
None => None,
}
}
}
impl Add<ClockDuration> for ClockTime {
type Output = Self;
fn add(self, rhs: ClockDuration) -> Self::Output {
Self(self.0.add(rhs))
}
}
impl AddAssign<ClockDuration> for ClockTime {
fn add_assign(&mut self, rhs: ClockDuration) {
*self = Self(self.0.add(rhs));
}
}
/// Represents a frequency in Hz
///
/// Clocks are usually given as a frequency, but durations are needed when dealing with clocks
/// and clock durations. This type makes it easier to create a clock of a given frequency and
/// convert it to a `ClockDuration`
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub struct Frequency {
hertz: u32,
}
impl Frequency {
#[inline]
pub const fn from_hz(hertz: u32) -> Self {
Self {
hertz,
}
}
#[inline]
pub const fn from_khz(khz: u32) -> Self {
Self {
hertz: khz * 1_000,
}
}
#[inline]
pub const fn from_mhz(mhz: u32) -> Self {
Self {
hertz: mhz * 1_000_000,
}
}
#[inline]
pub const fn as_hz(self) -> u32 {
self.hertz
}
#[inline]
pub const fn as_khz(self) -> u32 {
self.hertz / 1_000
}
#[inline]
pub const fn as_mhz(self) -> u32 {
self.hertz / 1_000_000
}
#[inline]
pub const fn period_duration(self) -> ClockDuration {
ClockDuration::from_femtos(ClockDuration::FEMTOS_PER_SEC / self.hertz as Femtos)
}
}
impl Mul<u32> for Frequency {
type Output = Self;
fn mul(self, rhs: u32) -> Self::Output {
Self::from_hz(self.hertz * rhs)
}
}
impl MulAssign<u32> for Frequency {
fn mul_assign(&mut self, rhs: u32) {
*self = Self::from_hz(self.hertz * rhs);
}
}
impl Div<u32> for Frequency {
type Output = Self;
fn div(self, rhs: u32) -> Self::Output {
Self::from_hz(self.hertz / rhs)
}
}
impl DivAssign<u32> for Frequency {
fn div_assign(&mut self, rhs: u32) {
*self = Self::from_hz(self.hertz / rhs);
}
}