embassy-rs/embassy · error
Ethernet PTP clock already started
Error message
Ethernet PTP clock already started
What it means
start_ptp() guards on the ptp_clock_taken flag: the Ethernet peripheral exposes a single PTP clock, and this instance already handed out a PtpClock from a previous call. The panic prevents two PtpClock handles from racing over the same hardware timestamping block; the faulting input is a duplicate start_ptp() call on the same Ethernet object.
Solutions
- Call start_ptp only once; keep and reuse the returned PtpClock.
- Drop the existing PtpClock before calling start_ptp again.
- Guard PTP startup behind a OnceCell/init flag in application code.
Example fix
// before // each reconnect: let ptp = eth.start_ptp(cfg); // after static PTP: OnceCell<PtpClock<...>> = OnceCell::new(); PTP.get_or_init(|| eth.start_ptp(cfg));
Defensive patterns
Strategy: validation
Validate before calling
static PTP_TAKEN: AtomicBool = AtomicBool::new(false);
fn start_ptp_once(eth: &mut Ethernet) -> PtpClock<...> {
if PTP_TAKEN.load(Ordering::SeqCst) { panic!("start_ptp must only be called once"); }
PTP_TAKEN.store(true, Ordering::SeqCst);
eth.start_ptp(ptp_cfg)
} Prevention
- Keep the returned PtpClock alive in app state instead of restarting.
- Route all network init through one function so start_ptp cannot be duplicated.
- Drop the PtpClock before any re-init.
When it happens
Trigger: Calling start_ptp() twice on the same v2 Ethernet instance, or restarting PTP while the previous PtpClock has not been dropped.
Common situations: Network re-init on reconnect; two tasks both calling start_ptp; duplicated init sequences in different code paths.
Understand the failure class
Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.
Related errors
AI-assisted analysis of embassy-rs/embassy@463a07b963 (2026-09-10).
Data as JSON: /api/errors/43cd57862d3153a1.
Report an issue: GitHub.
Appendix: source
Thrown at embassy-stm32/src/eth/v2/mod.rs:582
#[cfg(eth_v2)]
{
interrupt::ETH.unpend();
unsafe { interrupt::ETH.enable() };
}
#[cfg(any(eth_v2a, eth_v2b))]
{
interrupt::ETH1.unpend();
unsafe { interrupt::ETH1.enable() };
}
this
}
/// Start the Ethernet MAC PTP clock.
#[cfg(feature = "ptp")]
pub fn start_ptp(&mut self, config: PtpClockConfig) -> PtpClock<T> {
if self.ptp_clock_taken {
panic!("Ethernet PTP clock already started");
}
let clock = PtpClock::start(config);
self.ptp_clock_taken = true;
clock
}
}
impl<'d, T: Instance, P: Phy> Drop for Ethernet<'d, T, P> {
fn drop(&mut self) {
let dma = T::regs().ethernet_dma();
let mac = T::regs().ethernet_mac();
let mtl = T::regs().ethernet_mtl();
// Disable the TX DMA and wait for any previous transmissions to be completed
ch0!(dma, dmac_tx_cr).modify(|w| w.set_st(false));
while {
let txqueue = ch0!(mtl, mtl_tx_qdr).read();View on GitHub (pinned to 463a07b963)