stm32_eth/ptp/
subseconds.rs

1/// The amount of nanoseconds per second.
2pub const NANOS_PER_SECOND: u32 = 1_000_000_000;
3
4/// The amount of subseconds per second.
5pub const SUBSECONDS_PER_SECOND: u32 = 0x7FFF_FFFF;
6
7/// The ratio to use to convert subseconds to seconds.
8pub const SUBSECONDS_TO_SECONDS: f32 = 1.0 / (SUBSECONDS_PER_SECOND as f32);
9
10const NS_PER_S: u64 = NANOS_PER_SECOND as u64;
11const SUBS_PER_S: u64 = SUBSECONDS_PER_SECOND as u64;
12
13/// A subsecond value as produced by the PTP peripheral
14#[cfg_attr(feature = "defmt", derive(defmt::Format))]
15#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
16pub struct Subseconds(u32);
17
18impl Subseconds {
19    /// The maximum possible value for [`Subseconds`]
20    pub const MAX_VALUE: u32 = SUBSECONDS_PER_SECOND;
21
22    /// The maximum possible [`Subseconds`]
23    pub const MAX: Self = Self(SUBSECONDS_PER_SECOND);
24
25    /// Zero [`Subseconds`]
26    pub const ZERO: Self = Self(0);
27
28    /// Create a new [`Subseconds`] from the provided value.
29    ///
30    /// The returned [`Subseconds`] represents a time of `value / 2^31` seconds.
31    ///
32    /// To obtain that representation in nanoseconds, see [`Subseconds::nanos`].
33    ///
34    /// To approximate a [`Subseconds`] from nanoseconds, see [`Subseconds::new_from_nanos`].
35    ///
36    /// Returns `None` if `value > SUBSECONDS_PER_SECOND`. (See [`SUBSECONDS_PER_SECOND`]).
37    pub const fn new(value: u32) -> Option<Self> {
38        if value > SUBSECONDS_PER_SECOND as u32 {
39            None
40        } else {
41            Some(Self(value))
42        }
43    }
44
45    /// Create a new [`Subseconds`] from the provided value, without verifying that `value`
46    /// is less than or equal to [`Self::MAX_VALUE`]).
47    ///
48    /// The returned [`Subseconds`] represents a time of `value / 2^31` seconds.
49    ///
50    /// To obtain that representation in nanoseconds, see [`Subseconds::nanos`].
51    ///
52    /// To approximate a [`Subseconds`] from nanoseconds, see [`Subseconds::new_from_nanos`]
53    pub(crate) const fn new_unchecked(value: u32) -> Self {
54        Self(value)
55    }
56
57    /// Create a new [`Subseconds`] from the given amount of nanoseconds,
58    /// using a round-to-nearest method.
59    ///
60    /// Returns [`None`] if `nanos >= NANOS_PER_SECOND`. (See [`NANOS_PER_SECOND`])
61    pub const fn new_from_nanos(nanos: u32) -> Option<Self> {
62        if nanos >= NANOS_PER_SECOND as u32 {
63            return None;
64        }
65
66        let subseconds = ((nanos as u64 * SUBS_PER_S) + (NS_PER_S / 2)) / NS_PER_S;
67
68        Some(Subseconds::new_unchecked(subseconds as u32))
69    }
70
71    /// Convert this [`Subseconds`] to nanoseconds, using a round-to-nearest method.
72    pub const fn nanos(&self) -> u32 {
73        let nanos = ((self.0 as u64 * NS_PER_S) + (SUBS_PER_S / 2)) / SUBS_PER_S;
74
75        nanos as u32
76    }
77
78    /// Get the raw value of this [`Subseconds`]
79    pub const fn raw(&self) -> u32 {
80        self.0
81    }
82
83    /// Convert this [`Subseconds`] to Hertz
84    pub(crate) const fn hertz(&self) -> u32 {
85        SUBSECONDS_PER_SECOND as u32 / self.0
86    }
87
88    pub(crate) const fn nearest_increment(input_clk_hz: u32) -> Subseconds {
89        let hclk_half_subs = (SUBSECONDS_PER_SECOND as u32 + (input_clk_hz / 2)) / input_clk_hz;
90
91        Self::new_unchecked(hclk_half_subs)
92    }
93}
94
95impl core::ops::Add<Subseconds> for Subseconds {
96    type Output = Self;
97
98    fn add(self, rhs: Subseconds) -> Self::Output {
99        Self(self.0.wrapping_add(rhs.0) % (SUBSECONDS_PER_SECOND + 1))
100    }
101}
102
103impl core::ops::AddAssign<Subseconds> for Subseconds {
104    fn add_assign(&mut self, rhs: Subseconds) {
105        *self = *self + rhs;
106    }
107}
108
109impl core::ops::Sub<Subseconds> for Subseconds {
110    type Output = Self;
111
112    fn sub(self, rhs: Subseconds) -> Self::Output {
113        Self(self.0.wrapping_sub(rhs.0) % (SUBSECONDS_PER_SECOND + 1))
114    }
115}
116
117impl core::ops::SubAssign<Subseconds> for Subseconds {
118    fn sub_assign(&mut self, rhs: Subseconds) {
119        *self = *self - rhs;
120    }
121}
122
123#[cfg(all(test, not(target_os = "none")))]
124mod test {
125
126    use super::*;
127
128    // Assert that values produced by [`Subseconds::nearest_increment`] for some
129    // valid frequencies are within the correct span for `stssi`
130    #[test]
131    fn correct_subsecond_increment() {
132        for i in (25_000..180_000).map(|v| v * 1_000) {
133            let subs = Subseconds::nearest_increment(i).raw();
134            assert!(subs > 0 && subs <= 255);
135        }
136    }
137
138    #[test]
139    fn from_nanos() {
140        for i in [0, 1, 2, 3, NANOS_PER_SECOND - 1] {
141            let subseconds = Subseconds::new_from_nanos(i).unwrap();
142            assert!(subseconds.raw() < SUBSECONDS_PER_SECOND);
143        }
144
145        assert!(Subseconds::new_from_nanos(NANOS_PER_SECOND).is_none());
146        assert!(Subseconds::new_from_nanos(u32::MAX).is_none());
147    }
148
149    #[test]
150    fn subsecond_math() {
151        let one = Subseconds::new(1).unwrap();
152        let two = Subseconds::new(2).unwrap();
153        let three = Subseconds::new(3).unwrap();
154        let max = Subseconds::new(SUBSECONDS_PER_SECOND).unwrap();
155        let zero = Subseconds::new(0).unwrap();
156
157        assert_eq!(one + two, three);
158        assert_eq!(two - one, one);
159
160        assert_eq!(one - max + max, one);
161        assert_eq!(one - two, max);
162        assert_eq!(one + max, zero);
163        assert_eq!(two + max, one);
164    }
165}