From 585db4b657840cf8989e52cf0f93490d277e4103 Mon Sep 17 00:00:00 2001 From: "Daniel P. Carvalho" Date: Sat, 19 Sep 2026 14:40:40 -0300 Subject: [PATCH] netutils/ptpd: Add ingress latency compensation for RX timestamps. The MAC latches a hardware receive timestamp later than the frame reaches the wire, because of the PHY and the clock domain crossing. This fixed delay is the ingressLatency port parameter of IEEE 1588 and shows up as a constant phase error between the local and the master clock. Subtract the configured latency from every hardware receive timestamp in ptp_getrxtime(), the single place where they enter the daemon, so Sync, Delay_Resp and the peer delay messages are all corrected. - Add CONFIG_NETUTILS_PTPD_INGRESS_LATENCY_NS (default 0, which applies no compensation). - Add the -I option to override it at run time. - Add ingress_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 | 20 ++++++++++++++++++++ netutils/ptpd/ptpd.c | 21 +++++++++++++++++++++ system/ptpd/ptpd_main.c | 7 ++++++- 4 files changed, 48 insertions(+), 1 deletion(-) diff --git a/include/netutils/ptpd.h b/include/netutils/ptpd.h index ae397d57c..f418e6892 100644 --- a/include/netutils/ptpd.h +++ b/include/netutils/ptpd.h @@ -53,6 +53,7 @@ struct ptpd_config_s enum ptp_delay_mechanism_e delay_mechanism; bool bmca; sa_family_t af; + int32_t ingress_latency_ns; /* Hardware RX timestamp latency (ns) */ }; /* PTPD status information structure */ diff --git a/netutils/ptpd/Kconfig b/netutils/ptpd/Kconfig index 0bd2389fd..fc26e821b 100644 --- a/netutils/ptpd/Kconfig +++ b/netutils/ptpd/Kconfig @@ -219,4 +219,24 @@ config NETUTILS_PTPD_STATUSFILE memory, making it work across all build modes (Flat, Protected, Kernel). Written atomically via temp + rename. +config NETUTILS_PTPD_INGRESS_LATENCY_NS + int "PTP hardware receive timestamp latency (ns)" + default 0 + range -1000000 1000000 + ---help--- + Fixed delay, in nanoseconds, between a frame reaching the wire + reference plane and the moment the MAC latches its hardware + receive timestamp. This is the ingressLatency port parameter of + IEEE 1588: the PHY and the MAC clock-domain crossing make the + timestamp point lag the true arrival of the frame. + + The latency is subtracted from every hardware receive timestamp, + so a positive value moves the timestamps earlier. It has no + effect with software timestamping. It can be overridden at + run time with the -I option. + + The value depends on the PHY and board and has to be measured, + for example by comparing a physical PPS output against a + reference. The default of 0 applies no compensation. + endif # NETUTILS_PTPD diff --git a/netutils/ptpd/ptpd.c b/netutils/ptpd/ptpd.c index e8d08c5d3..ae56660b6 100644 --- a/netutils/ptpd/ptpd.c +++ b/netutils/ptpd/ptpd.c @@ -314,6 +314,22 @@ static int64_t timespec_to_ms(FAR const struct timespec *ts) return ts->tv_sec * MSEC_PER_SEC + (ts->tv_nsec / NSEC_PER_MSEC); } +/* Add a positive or negative number of nanoseconds to a timespec value. */ + +static void timespec_add_ns(FAR struct timespec *ts, int64_t ns) +{ + int64_t total = ts->tv_sec * NSEC_PER_SEC + ts->tv_nsec + ns; + + ts->tv_sec = total / NSEC_PER_SEC; + ts->tv_nsec = total % NSEC_PER_SEC; + + if (ts->tv_nsec < 0) + { + ts->tv_sec--; + ts->tv_nsec += NSEC_PER_SEC; + } +} + /* Get positive or negative delta between two timespec values. * If value would exceed int64 limit (292 years), return INT64_MAX/MIN. */ @@ -500,6 +516,11 @@ static int ptp_getrxtime(FAR struct ptp_state_s *state, if (ts->tv_sec > 0 || ts->tv_nsec > 0) { + /* The MAC latches the timestamp later than the frame + * reaches the wire: compensate the ingress latency. + */ + + timespec_add_ns(ts, -state->config->ingress_latency_ns); return OK; } } diff --git a/system/ptpd/ptpd_main.c b/system/ptpd/ptpd_main.c index 516b75b5f..370f915e1 100644 --- a/system/ptpd/ptpd_main.c +++ b/system/ptpd/ptpd_main.c @@ -164,6 +164,7 @@ static void usage(FAR const char *progname) " -P P2P, support peer delay request-response\n" " -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" " -t [pid] look the status of ptp daemon\n" " -d [pid] stop ptp daemon\n", progname); @@ -195,8 +196,9 @@ int main(int argc, FAR char *argv[]) #endif config.bmca = false; config.af = AF_INET; + config.ingress_latency_ns = CONFIG_NETUTILS_PTPD_INGRESS_LATENCY_NS; - while ((option = getopt(argc, argv, "p:i:t:d:rs246BEHSP")) != ERROR) + while ((option = getopt(argc, argv, "p:i:t:d:I:rs246BEHSP")) != ERROR) { switch (option) { @@ -251,6 +253,9 @@ int main(int argc, FAR char *argv[]) case 'p': config.clock = optarg; break; + case 'I': + config.ingress_latency_ns = atoi(optarg); + break; case 'r': config.clock = "realtime"; break;