stm32_eth/ptp/
timestamp.rs

1use crate::dma::desc::Descriptor;
2
3use super::{Subseconds, NANOS_PER_SECOND};
4
5/// A timestamp produced by the PTP periperhal
6#[derive(Clone, Copy, Debug, PartialEq, Eq)]
7pub struct Timestamp(i64);
8
9#[cfg(feature = "defmt")]
10impl defmt::Format for Timestamp {
11    fn format(&self, fmt: defmt::Formatter) {
12        if self.is_positive() {
13            defmt::write!(fmt, "{}.{:09}", self.seconds(), self.nanos());
14        } else {
15            defmt::write!(fmt, "-{}.{:09}", self.seconds(), self.nanos());
16        }
17    }
18}
19
20impl Timestamp {
21    // The bit that represents the signedness of the timestamp in the
22    // subseconds value.
23    const SIGN_BIT: u32 = 0x8000_0000;
24
25    /// Create a new [`Timestamp`]
26    pub const fn new(negative: bool, seconds: u32, subseconds: Subseconds) -> Self {
27        Self::new_unchecked(negative, seconds, subseconds.raw())
28    }
29
30    /// Create a new [`Timestamp`] from the given raw value.
31    pub const fn new_raw(value: i64) -> Self {
32        Self(value)
33    }
34
35    /// Get the raw value of this [`Timestamp`]
36    pub const fn raw(&self) -> i64 {
37        self.0
38    }
39
40    /// Check whether this timestamp is negative or not.
41    pub const fn is_negative(&self) -> bool {
42        self.0.is_negative()
43    }
44
45    /// Check whether this timestamp is positive or not.
46    pub const fn is_positive(&self) -> bool {
47        !self.is_negative()
48    }
49
50    pub(crate) const fn new_unchecked(negative: bool, seconds: u32, subseconds: u32) -> Self {
51        let seconds: i64 = (seconds as i64) << 31;
52        let subseconds: i64 = subseconds as i64;
53
54        let mut total = seconds + subseconds;
55
56        if negative {
57            total = -total;
58        };
59
60        Self(total)
61    }
62
63    /// Get the second component of this timestamp
64    pub const fn seconds(&self) -> u32 {
65        (self.0.abs() >> 31) as u32
66    }
67
68    /// Get the raw subsecond value of this timestamp.
69    pub const fn subseconds(&self) -> Subseconds {
70        Subseconds::new_unchecked(self.0.abs() as u32 & Subseconds::MAX_VALUE)
71    }
72
73    /// Get the signed subsecond value of this timestamp.
74    ///
75    /// Note that this is _not_ an i32: it is, technically,
76    /// a u31 with a leading sign bit.
77    pub const fn subseconds_signed(&self) -> u32 {
78        let mut subseconds = self.subseconds().raw();
79
80        if self.0.is_negative() {
81            subseconds |= Self::SIGN_BIT;
82        }
83
84        subseconds
85    }
86
87    /// Get the nanosecond component of this timestamp
88    pub const fn nanos(&self) -> u32 {
89        self.subseconds().nanos()
90    }
91
92    /// Get the total amount of nanoseconds in this [`Timestamp`].
93    ///
94    /// Example:
95    /// ```rust
96    /// # use stm32_eth::ptp::{Subseconds, Timestamp};
97    /// let timestamp = Timestamp::new(false, 500, Subseconds::new_from_nanos(500_000).unwrap());
98    /// assert_eq!(timestamp.total_nanos(), 500 * 1_000_000_000 + 500_000);
99    ///
100    ///
101    /// let timestamp_neg = Timestamp::new(true, 500, Subseconds::new_from_nanos(500_000).unwrap());
102    /// assert_eq!(timestamp_neg.total_nanos(), -1 * (500 * 1_000_000_000 + 500_000));
103    /// ```
104    pub const fn total_nanos(&self) -> i64 {
105        let nanos = self.seconds() as i64 * NANOS_PER_SECOND as i64 + self.nanos() as i64;
106
107        if self.is_positive() {
108            nanos
109        } else {
110            -nanos
111        }
112    }
113
114    /// Create a new timestamp from the provided register values.
115    pub const fn from_parts(high: u32, low: u32) -> Timestamp {
116        let negative = (low & Self::SIGN_BIT) == Self::SIGN_BIT;
117        let subseconds = low & !(Self::SIGN_BIT);
118
119        Timestamp::new_unchecked(negative, high, subseconds)
120    }
121
122    /// Create a timestamp from the given descriptor
123    pub fn from_descriptor(desc: &Descriptor) -> Option<Self> {
124        #[cfg(not(feature = "stm32f1xx-hal"))]
125        {
126            let (high, low) = { (desc.read(7), desc.read(6)) };
127            Some(Self::from_parts(high, low))
128        }
129
130        #[cfg(feature = "stm32f1xx-hal")]
131        {
132            let (high, low) = { (desc.read(3), desc.read(2)) };
133
134            // The timestamp registers are written to all-ones if
135            // timestamping was no succesfull
136            if high == 0xFFFF_FFFF && low == 0xFFFF_FFFF {
137                None
138            } else {
139                Some(Self::from_parts(high, low))
140            }
141        }
142    }
143}
144
145impl core::ops::Add<Timestamp> for Timestamp {
146    type Output = Self;
147
148    fn add(self, rhs: Timestamp) -> Self::Output {
149        Self(self.0 + rhs.0)
150    }
151}
152
153impl core::ops::AddAssign<Timestamp> for Timestamp {
154    fn add_assign(&mut self, rhs: Timestamp) {
155        self.0 += rhs.0;
156    }
157}
158
159impl core::ops::Sub<Timestamp> for Timestamp {
160    type Output = Self;
161
162    fn sub(self, rhs: Timestamp) -> Self::Output {
163        Self(self.0 - rhs.0)
164    }
165}
166
167impl core::ops::SubAssign<Timestamp> for Timestamp {
168    fn sub_assign(&mut self, rhs: Timestamp) {
169        self.0 -= rhs.0
170    }
171}
172
173#[cfg(all(test, not(target_os = "none")))]
174mod test {
175    use crate::ptp::SUBSECONDS_PER_SECOND;
176
177    use super::{Subseconds, Timestamp};
178
179    fn subs(val: u32) -> Subseconds {
180        Subseconds::new(val).unwrap()
181    }
182
183    #[test]
184    fn timestamp_add() {
185        let one = Timestamp::new(false, 1, subs(1));
186        let one_big = Timestamp::new(false, 1, subs(SUBSECONDS_PER_SECOND - 1));
187        let two = Timestamp::new(false, 2, subs(2));
188        let three = Timestamp::new(false, 3, subs(3));
189
190        let one_neg = Timestamp::new(true, 1, subs(1));
191        let one_big_neg = Timestamp::new(true, 1, subs(SUBSECONDS_PER_SECOND - 1));
192        let two_neg = Timestamp::new(true, 2, subs(2));
193        let three_neg = Timestamp::new(true, 3, subs(3));
194
195        let one_minus_two = Timestamp::new(true, 1, subs(1));
196        let one_big_plus_two = Timestamp::new(false, 4, subs(0));
197        let two_minus_one_big = Timestamp::new(false, 0, subs(4));
198        let one_big_neg_plus_two_neg = Timestamp::new(true, 4, subs(0));
199
200        // +self + +rhs
201        assert_eq!(one + two, three);
202        assert_eq!(two + one, three);
203        assert_eq!(one_big + two, one_big_plus_two);
204        assert_eq!(two + one_big, one_big_plus_two);
205
206        // +self + -rhs
207        assert_eq!(one + two_neg, one_minus_two);
208        assert_eq!(two + one_big_neg, two_minus_one_big);
209
210        // -self + rhs
211        assert_eq!(one_neg + two, one);
212        assert_eq!(two + one_neg, one);
213
214        // -self + -rhs
215        assert_eq!(one_neg + two_neg, three_neg);
216        assert_eq!(two_neg + one_neg, three_neg);
217        assert_eq!(one_big_neg + two_neg, one_big_neg_plus_two_neg);
218        assert_eq!(two_neg + one_big_neg, one_big_neg_plus_two_neg);
219    }
220
221    #[test]
222    fn timestamp_sub() {
223        let one = Timestamp::new(false, 1, subs(1));
224        let one_big = Timestamp::new(false, 1, subs(SUBSECONDS_PER_SECOND - 1));
225        let two = Timestamp::new(false, 2, subs(2));
226        let three = Timestamp::new(false, 3, subs(3));
227
228        let one_neg = Timestamp::new(true, 1, subs(1));
229        let two_neg = Timestamp::new(true, 2, subs(2));
230        let three_neg = Timestamp::new(true, 3, subs(3));
231
232        let one_minus_two = Timestamp::new(true, 1, subs(1));
233        let one_minus_one_big = Timestamp::new(true, 0, subs(SUBSECONDS_PER_SECOND - 2));
234
235        assert_eq!(one - one_big, one_minus_one_big);
236
237        // +self - +rhs
238        assert_eq!(two - one, one);
239        assert_eq!(one - two, one_minus_two);
240
241        // +self - -rhs
242        assert_eq!(two - one_neg, three);
243        assert_eq!(one_neg - two, three_neg);
244
245        // -self - +rhs
246        assert_eq!(one_neg - two, three_neg);
247        assert_eq!(two - one_neg, three);
248
249        // -self - -rhs
250        assert_eq!(one_neg - two_neg, one);
251        assert_eq!(two_neg - one_neg, one_minus_two);
252    }
253}