1use std::{fmt, num::NonZeroU64};
2
3use crate::time::FineDuration;
4
5#[cfg(target_arch = "aarch64")]
6#[path = "aarch64.rs"]
7mod arch;
8
9#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
10#[path = "x86.rs"]
11mod arch;
12
13/// [CPU timestamp counter](https://en.wikipedia.org/wiki/Time_Stamp_Counter).
14#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
15#[repr(transparent)]
16pub(crate) struct TscTimestamp {
17 pub value: u64,
18}
19
20impl TscTimestamp {
21 /// Gets the timestamp frequency.
22 ///
23 /// On AArch64, this simply reads `cntfrq_el0`. On x86, this measures the
24 /// TSC frequency.
25 #[inline]
26 #[allow(unreachable_code)]
27 pub fn frequency() -> Result<NonZeroU64, TscUnavailable> {
28 // Miri does not support inline assembly.
29 #[cfg(miri)]
30 return Err(TscUnavailable::Unimplemented);
31
32 #[cfg(any(target_arch = "aarch64", target_arch = "x86", target_arch = "x86_64"))]
33 return NonZeroU64::new(arch::frequency()?).ok_or(TscUnavailable::ZeroFrequency);
34
35 Err(TscUnavailable::Unimplemented)
36 }
37
38 /// Reads the timestamp counter.
39 #[inline(always)]
40 pub fn start() -> Self {
41 #[allow(unused)]
42 let value = 0;
43
44 #[cfg(target_arch = "aarch64")]
45 let value = arch::timestamp();
46
47 #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
48 let value = arch::start_timestamp();
49
50 Self { value }
51 }
52
53 /// Reads the timestamp counter.
54 #[inline(always)]
55 pub fn end() -> Self {
56 #[allow(unused)]
57 let value = 0;
58
59 #[cfg(target_arch = "aarch64")]
60 let value = arch::timestamp();
61
62 #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
63 let value = arch::end_timestamp();
64
65 Self { value }
66 }
67
68 pub fn duration_since(self, earlier: Self, frequency: NonZeroU64) -> FineDuration {
69 const PICOS: u128 = 1_000_000_000_000;
70
71 let Some(diff) = self.value.checked_sub(earlier.value) else {
72 return Default::default();
73 };
74
75 FineDuration { picos: (diff as u128 * PICOS) / frequency.get() as u128 }
76 }
77}
78
79/// Reason for why the timestamp counter cannot be used.
80#[derive(Clone, Copy)]
81pub(crate) enum TscUnavailable {
82 /// Not yet implemented for this platform.
83 Unimplemented,
84
85 /// Got a frequency of 0.
86 ZeroFrequency,
87
88 /// Missing the appropriate instructions.
89 #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
90 MissingInstructions,
91
92 /// The timestamp counter is not guaranteed to be constant.
93 #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
94 VariableFrequency,
95}
96
97impl fmt::Display for TscUnavailable {
98 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
99 let reason: &'static str = match self {
100 Self::Unimplemented => "unimplemented",
101 Self::ZeroFrequency => "zero TSC frequency",
102
103 #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
104 Self::MissingInstructions => "missing instructions",
105
106 #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
107 Self::VariableFrequency => "variable TSC frequency",
108 };
109
110 f.write_str(data:reason)
111 }
112}
113