From 4bc75e18f72fcbd83d6fb2b4efa964d8bf31ed1c Mon Sep 17 00:00:00 2001
From: Benoit Rat <benoit@sevensols.com>
Date: Fri, 5 Apr 2019 11:17:16 +0200
Subject: [PATCH] tools: wr_date: add diff command to track WR/FPGA (HW) VS
 linux (SW) time

The output will be something like:

wr_date -v diff
TAI(HW)-UTC(SW): +36.602286
UTC(HW)-UTC(SW): -0.397714

Conflicts:
	userspace/tools/wr_date.c
---
 userspace/tools/wr_date.c | 113 ++++++++++++++++++++++++++++++++++----
 1 file changed, 102 insertions(+), 11 deletions(-)

diff --git a/userspace/tools/wr_date.c b/userspace/tools/wr_date.c
index 827e2f6bd..888d5e1a4 100644
--- a/userspace/tools/wr_date.c
+++ b/userspace/tools/wr_date.c
@@ -28,6 +28,7 @@
 
 /* Address for hardware, from nic-hardware.h */
 #define FPGA_BASE_PPSG  0x10010500
+#define PPSG_STEP_IN_NS 16 /* we count a 16.5MHz */
 
 extern int init_module(void *module, unsigned long len, const char *options);
 extern int delete_module(const char *module, unsigned int flags);
@@ -47,6 +48,7 @@ void help(char *prgname)
 		"    set <value>     set WR time to scalar seconds\n"
 		"    set host        set TAI from current host time\n"
 		"    stat            print statistics between TAI (WR time) and linux UTC\n"
+		"    diff            show the difference between WR FPGA time (HW) and linux time (SW)\n"
 /*		"    set ntp         set TAI from ntp and leap seconds" */
 /*		"    set ntp:<ip>    set from specified ntp server\n" */
 		, WRDATE_CFG_FILE);
@@ -83,6 +85,29 @@ int wrdate_cfgfile(char *fname)
 }
 
 
+/* This returns wr time, used for syncing to a second transition */
+uint64_t gettimeof_wr(struct timeval *tv, struct PPSG_WB *pps)
+{
+	uint32_t tai_h,tai_l,nsec, tmp1, tmp2;
+	uint64_t tai;
+
+	tai_h = pps->CNTR_UTCHI;
+
+	do {
+		tai_l = pps->CNTR_UTCLO;
+		nsec = pps->CNTR_NSEC * PPSG_STEP_IN_NS;
+		tmp1 = pps->CNTR_UTCHI;
+		tmp2 = pps->CNTR_UTCLO;
+	} while((tmp1 != tai_h) || (tmp2 != tai_l));
+
+	tai = (uint64_t)(tai_h) << 32 | tai_l;
+
+	tv->tv_sec = tai;
+	tv->tv_usec = nsec / 1000;
+
+	return tai;
+}
+
 int get_kern_leaps(void)
 {
 	struct timex tx = {0};
@@ -148,19 +173,81 @@ int wrdate_get(struct PPSG_WB *pps, int tohost)
 	return 0;
 }
 
-/* This returns wr time, used for syncing to a second transition */
-void gettimeof_wr(struct timeval *tv, struct PPSG_WB *pps)
+
+/**
+ * Function to subtract timeval in a robust way
+ *
+ * In order to properly print the result on screen you can use:
+ *
+ *     int neg=timeval_subtract(&diff, &a, &b);
+ *     printf("%c%li.%06li\n",neg?'-':'+',labs(diff.tv_sec),labs(diff.tv_usec));
+ *
+ * @ref: https://stackoverflow.com/questions/15846762/timeval-subtract-explanation
+ * @note for safety reason a copy of x,y is used internally so x,y are never modified
+ * @param[inout] result A pointer on a timeval structure where the result will be stored.
+ * @param[in] x A pointer on x timeval struct
+ * @param[in] y A pointer on y timeval struct
+ * @return 1 if result is negative (seconds or useconds)
+ *
+ *
+ */
+int timeval_subtract(struct timeval *result, struct timeval *x, struct timeval *y)
 {
-	unsigned long tail, nsec, tmp2;
+	struct timeval xx = *x;
+	struct timeval yy = *y;
+	x = &xx; y = &yy;
+
+	if (x->tv_usec > 999999)
+	{
+		x->tv_sec += x->tv_usec / 1000000;
+		x->tv_usec %= 1000000;
+	}
 
-	/* FIXME: not 2038-clean */
-	do {
-		tail = pps->CNTR_UTCLO;
-		nsec = pps->CNTR_NSEC * 16; /* we count a 16.5MHz */
-		tmp2 = pps->CNTR_UTCLO;
-	} while(tmp2 != tail);
-	tv->tv_sec = tail;
-	tv->tv_usec = nsec / 1000;
+	if (y->tv_usec > 999999)
+	{
+		y->tv_sec += y->tv_usec / 1000000;
+		y->tv_usec %= 1000000;
+	}
+
+	result->tv_sec = x->tv_sec - y->tv_sec;
+	result->tv_usec = x->tv_usec - y->tv_usec;
+
+	if(result->tv_sec>0 && result->tv_usec < 0)
+	{
+		result->tv_usec += 1000000;
+		result->tv_sec--; // borrow
+	}
+	else if(result->tv_sec<0 && result->tv_usec > 0)
+	{
+		result->tv_usec -= 1000000;
+		result->tv_sec++; // borrow
+	}
+
+	return (result->tv_sec < 0) || (result->tv_usec<0);
+}
+
+
+
+int wrdate_diff(struct PPSG_WB *pps)
+{
+	struct timeval sw, hw, diff;
+	int neg=0;
+
+	gettimeof_wr(&hw, pps);
+	gettimeofday(&sw, NULL);
+
+	neg=timeval_subtract(&diff, &hw, &sw);
+
+	printf("%s%c%li.%06li\n",opt_verbose?("TAI(HW)-UTC(SW): "):(""),neg?'-':'+',labs(diff.tv_sec),labs(diff.tv_usec));
+	if(opt_verbose)
+	{
+
+		hw.tv_sec-=get_kern_leaps(); //Convert HW clock from TAI to UTC
+
+		neg=timeval_subtract(&diff, &hw, &sw);
+		printf("UTC(HW)-UTC(SW): %c%li.%06li\n",neg?'-':'+',labs(diff.tv_sec),labs(diff.tv_usec));
+	}
+	return 0;
 }
 
 /* Fix the TAI representation looking at the leap file */
@@ -538,6 +625,10 @@ int main(int argc, char **argv)
 		return wrdate_get(pps, tohost);
 	}
 
+	if (!strcmp(cmd, "diff")) {
+		return wrdate_diff(pps);
+	}
+
 	if (!strcmp(cmd, "stat")) {
 		/* parse the optional "tohost" argument */
 		return wrdate_stat(pps);
-- 
GitLab