use crate::{dma::EthernetDMA, hal::rcc::Clocks, mac::EthernetMAC, peripherals::ETHERNET_PTP};
mod timestamp;
pub use timestamp::Timestamp;
#[cfg(all(not(feature = "stm32f1xx-hal"), feature = "async-await"))]
use {core::task::Poll, futures::task::AtomicWaker};
mod subseconds;
pub use subseconds::{Subseconds, NANOS_PER_SECOND, SUBSECONDS_PER_SECOND, SUBSECONDS_TO_SECONDS};
mod pps_pin;
pub use pps_pin::PPSPin;
pub struct EthernetPTP {
eth_ptp: ETHERNET_PTP,
}
impl EthernetPTP {
const fn calculate_regs(hclk: u32) -> (Subseconds, u32) {
let half_hclk = hclk / 2;
let stssi = Subseconds::nearest_increment(half_hclk);
let half_rate_subsec_increment_hz = stssi.hertz();
let tsa = ((half_rate_subsec_increment_hz as u64 * u32::MAX as u64) / hclk as u64) as u32;
(stssi, tsa)
}
pub(crate) fn new(
eth_ptp: ETHERNET_PTP,
clocks: Clocks,
_dma: &EthernetDMA,
) -> Self {
EthernetMAC::mask_timestamp_trigger_interrupt();
let hclk = clocks.hclk().to_Hz();
let (stssi, tsa) = Self::calculate_regs(hclk);
eth_ptp.ptptscr.write(|w| {
#[cfg(not(feature = "stm32f1xx-hal"))]
let w = w.tssarfe().set_bit();
w.tse().set_bit().tsfcu().set_bit()
});
eth_ptp
.ptpssir
.write(|w| unsafe { w.stssi().bits(stssi.raw() as u8) });
let mut me = Self { eth_ptp };
me.set_addend(tsa);
me.set_time(Timestamp::new_unchecked(false, 0, 0));
me
}
pub fn subsecond_increment(&self) -> Subseconds {
Subseconds::new_unchecked(self.eth_ptp.ptpssir.read().stssi().bits() as u32)
}
pub fn addend(&self) -> u32 {
self.eth_ptp.ptptsar.read().bits()
}
#[inline(always)]
pub fn set_addend(&mut self, rate: u32) {
let ptp = &self.eth_ptp;
ptp.ptptsar.write(|w| unsafe { w.bits(rate) });
#[cfg(feature = "stm32f1xx-hal")]
{
while ptp.ptptscr.read().tsaru().bit_is_set() {}
ptp.ptptscr.modify(|_, w| w.tsaru().set_bit());
while ptp.ptptscr.read().tsaru().bit_is_set() {}
}
#[cfg(not(feature = "stm32f1xx-hal"))]
{
while ptp.ptptscr.read().ttsaru().bit_is_set() {}
ptp.ptptscr.modify(|_, w| w.ttsaru().set_bit());
while ptp.ptptscr.read().ttsaru().bit_is_set() {}
}
}
pub fn set_time(&mut self, time: Timestamp) {
let ptp = &self.eth_ptp;
let seconds = time.seconds();
let subseconds = time.subseconds_signed();
ptp.ptptshur.write(|w| unsafe { w.bits(seconds) });
ptp.ptptslur.write(|w| unsafe { w.bits(subseconds) });
while ptp.ptptscr.read().tssti().bit_is_set() {}
ptp.ptptscr.modify(|_, w| w.tssti().set_bit());
while ptp.ptptscr.read().tssti().bit_is_set() {}
}
pub fn update_time(&mut self, time: Timestamp) {
let ptp = &self.eth_ptp;
let seconds = time.seconds();
let subseconds = time.subseconds_signed();
ptp.ptptshur.write(|w| unsafe { w.bits(seconds) });
ptp.ptptslur.write(|w| unsafe { w.bits(subseconds) });
let read_status = || {
let scr = ptp.ptptscr.read();
scr.tsstu().bit_is_set() || scr.tssti().bit_is_set()
};
while read_status() {}
ptp.ptptscr.modify(|_, w| w.tsstu().set_bit());
while ptp.ptptscr.read().tsstu().bit_is_set() {}
}
pub fn now() -> Timestamp {
Self::get_time()
}
pub fn get_time() -> Timestamp {
let try_read_time = || {
let eth_ptp = unsafe { &*ETHERNET_PTP::ptr() };
let seconds = eth_ptp.ptptshr.read().bits();
let subseconds = eth_ptp.ptptslr.read().bits();
let seconds_after = eth_ptp.ptptshr.read().bits();
if seconds == seconds_after {
Ok(Timestamp::from_parts(seconds, subseconds))
} else {
Err(())
}
};
loop {
if let Ok(res) = try_read_time() {
return res;
}
}
}
pub fn enable_pps<P>(&mut self, pin: P) -> P::Output
where
P: PPSPin,
{
pin.enable()
}
}
#[cfg(not(feature = "stm32f1xx-hal"))]
impl EthernetPTP {
#[cfg(feature = "async-await")]
fn waker() -> &'static AtomicWaker {
static WAKER: AtomicWaker = AtomicWaker::new();
&WAKER
}
fn set_target_time(&mut self, timestamp: Timestamp) {
let (high, low) = (timestamp.seconds(), timestamp.subseconds_signed());
self.eth_ptp
.ptptthr
.write(|w| unsafe { w.ttsh().bits(high) });
self.eth_ptp
.ptpttlr
.write(|w| unsafe { w.ttsl().bits(low) });
}
pub fn configure_target_time_interrupt(&mut self, timestamp: Timestamp) {
self.set_target_time(timestamp);
self.eth_ptp.ptptscr.modify(|_, w| w.tsite().set_bit());
EthernetMAC::unmask_timestamp_trigger_interrupt();
}
#[cfg(feature = "async-await")]
pub async fn wait_until(&mut self, timestamp: Timestamp) {
self.configure_target_time_interrupt(timestamp);
core::future::poll_fn(|ctx| {
if EthernetPTP::read_and_clear_interrupt_flag() {
Poll::Ready(())
} else if EthernetPTP::get_time().raw() >= timestamp.raw() {
Poll::Ready(())
} else {
EthernetPTP::waker().register(ctx.waker());
Poll::Pending
}
})
.await;
}
#[inline(always)]
fn read_and_clear_interrupt_flag() -> bool {
let eth_ptp = unsafe { &*ETHERNET_PTP::ptr() };
eth_ptp.ptptssr.read().tsttr().bit_is_set()
}
pub fn interrupt_handler() -> bool {
let eth_mac = unsafe { &*crate::peripherals::ETHERNET_MAC::ptr() };
let is_tsint = eth_mac.macsr.read().tsts().bit_is_set();
if is_tsint {
EthernetMAC::mask_timestamp_trigger_interrupt();
}
#[cfg(feature = "async-await")]
if let Some(waker) = EthernetPTP::waker().take() {
waker.wake();
} else {
EthernetPTP::read_and_clear_interrupt_flag();
}
#[cfg(not(feature = "async-await"))]
EthernetPTP::read_and_clear_interrupt_flag();
is_tsint
}
pub fn set_pps_freq(&mut self, pps_freq: u8) {
let pps_freq = pps_freq.min(31);
unsafe {
let ptpppscr = self.eth_ptp.ptpppscr.as_ptr() as *mut u32;
core::ptr::write_volatile(ptpppscr, pps_freq as u32);
}
}
}
#[cfg(all(test, not(target_os = "none")))]
mod test {
use super::*;
#[test]
fn hclk_to_regs() {
for hclk_hz in (25..180).map(|v| v * 1_000_000) {
let (stssi, tsa) = EthernetPTP::calculate_regs(hclk_hz);
let stssi = stssi.raw() as f64;
let tsa = tsa as f64;
let clock_ratio = (SUBSECONDS_PER_SECOND as f64 / stssi)
/ (hclk_hz as f64 * (tsa / 0xFFFF_FFFFu32 as f64));
let ppm = (clock_ratio - 1f64) * 1_000_000f64;
assert!(ppm <= 0.06, "{} at {}", ppm, hclk_hz);
}
}
}