From c78e4d69eadf15376b05877db36bd2f63dc3b8a6 Mon Sep 17 00:00:00 2001 From: "Daniel P. Carvalho" Date: Sat, 19 Sep 2026 15:45:39 -0300 Subject: [PATCH] netutils/ptpd: Add egress latency compensation for TX timestamps. The frame leaves the MAC later than the moment its hardware transmit timestamp is latched, because of the clock domain crossing and the PHY. This fixed delay is the egressLatency port parameter of IEEE 1588. Add the configured latency to every hardware transmit timestamp obtained through MSG_ERRQUEUE, the counterpart of the ingress compensation. - Add CONFIG_NETUTILS_PTPD_EGRESS_LATENCY_NS (default 0, which applies no compensation). - Add the -O option to override it at run time. - Add egress_latency_ns to struct ptpd_config_s. Software timestamps are not affected. Assisted-by: Claude:claude-sonnet-5 Signed-off-by: Daniel P. Carvalho --- include/netutils/ptpd.h | 1 + netutils/ptpd/Kconfig | 21 +++++++++++++++++++++ netutils/ptpd/ptpd.c | 6 ++++++ system/ptpd/ptpd_main.c | 11 ++++++++++- 4 files changed, 38 insertions(+), 1 deletion(-) diff --git a/include/netutils/ptpd.h b/include/netutils/ptpd.h index f418e6892..c86f3d647 100644 --- a/include/netutils/ptpd.h +++ b/include/netutils/ptpd.h @@ -54,6 +54,7 @@ struct ptpd_config_s bool bmca; sa_family_t af; int32_t ingress_latency_ns; /* Hardware RX timestamp latency (ns) */ + int32_t egress_latency_ns; /* Hardware TX timestamp latency (ns) */ }; /* PTPD status information structure */ diff --git a/netutils/ptpd/Kconfig b/netutils/ptpd/Kconfig index 371497609..eb560d7b0 100644 --- a/netutils/ptpd/Kconfig +++ b/netutils/ptpd/Kconfig @@ -259,4 +259,25 @@ config NETUTILS_PTPD_INGRESS_LATENCY_NS for example by comparing a physical PPS output against a reference. The default of 0 applies no compensation. +config NETUTILS_PTPD_EGRESS_LATENCY_NS + int "PTP hardware transmit timestamp latency (ns)" + default 0 + range -1000000 1000000 + depends on NET_TIMESTAMP + ---help--- + Fixed delay, in nanoseconds, between the MAC latching a hardware + transmit timestamp and the frame reaching the wire reference + plane. This is the egressLatency port parameter of IEEE 1588: the + clock domain crossing and the PHY make the frame leave later than + the timestamp point. + + The latency is added to every hardware transmit timestamp, so a + positive value moves the timestamps later. It has no effect with + software timestamping or when the driver does not provide + hardware transmit timestamps. It can be overridden at run time + with the -O option. + + The value depends on the PHY and board and has to be measured. + The default of 0 applies no compensation. + endif # NETUTILS_PTPD diff --git a/netutils/ptpd/ptpd.c b/netutils/ptpd/ptpd.c index dcccd2c1f..f10606bf5 100644 --- a/netutils/ptpd/ptpd.c +++ b/netutils/ptpd/ptpd.c @@ -1128,6 +1128,12 @@ static int ptp_sendmsg(FAR struct ptp_state_s *state, FAR const void *buf, if (ptp_get_tx_timestamp(state, sendts) == OK) { state->hwts_tx_failures = 0; + + /* The frame reaches the wire later than the MAC latches the + * timestamp: compensate the egress latency. + */ + + timespec_add_ns(sendts, state->config->egress_latency_ns); } else { diff --git a/system/ptpd/ptpd_main.c b/system/ptpd/ptpd_main.c index 370f915e1..3347f6c2d 100644 --- a/system/ptpd/ptpd_main.c +++ b/system/ptpd/ptpd_main.c @@ -165,6 +165,7 @@ static void usage(FAR const char *progname) " -i [dev] interface device to use, for example 'eth0'\n" " -p [dev] clock device to use\n" " -I [ns] hardware RX timestamp latency to compensate\n" + " -O [ns] hardware TX timestamp latency to compensate\n" " -t [pid] look the status of ptp daemon\n" " -d [pid] stop ptp daemon\n", progname); @@ -197,8 +198,13 @@ int main(int argc, FAR char *argv[]) config.bmca = false; config.af = AF_INET; config.ingress_latency_ns = CONFIG_NETUTILS_PTPD_INGRESS_LATENCY_NS; +#ifdef CONFIG_NET_TIMESTAMP + config.egress_latency_ns = CONFIG_NETUTILS_PTPD_EGRESS_LATENCY_NS; +#else + config.egress_latency_ns = 0; +#endif - while ((option = getopt(argc, argv, "p:i:t:d:I:rs246BEHSP")) != ERROR) + while ((option = getopt(argc, argv, "p:i:t:d:I:O:rs246BEHSP")) != ERROR) { switch (option) { @@ -256,6 +262,9 @@ int main(int argc, FAR char *argv[]) case 'I': config.ingress_latency_ns = atoi(optarg); break; + case 'O': + config.egress_latency_ns = atoi(optarg); + break; case 'r': config.clock = "realtime"; break;