aboutsummaryrefslogtreecommitdiff
path: root/sys/kern/kern_ntptime.c
diff options
context:
space:
mode:
authorPoul-Henning Kamp <phk@FreeBSD.org>1999-03-08 12:36:14 +0000
committerPoul-Henning Kamp <phk@FreeBSD.org>1999-03-08 12:36:14 +0000
commitc68996e27169d8d9c52b16489b348110d9cda0ea (patch)
treed93efb33176a9fcfab837232176d6e9901fdb95c /sys/kern/kern_ntptime.c
parent8d30de0d26cc3e6121a7b717a6e1c67c9f241546 (diff)
downloadsrc-c68996e27169d8d9c52b16489b348110d9cda0ea.tar.gz
src-c68996e27169d8d9c52b16489b348110d9cda0ea.zip
Integrate the new "nanokernel" PLL from Dave Mills.
This code is backwards compatible with the older "microkernel" PLL, but allows ntpd v4 to use nanosecond resolution. Many other improvements. PPS_SYNC and hardpps() are NOT supported yet.
Notes
Notes: svn path=/head/; revision=44574
Diffstat (limited to 'sys/kern/kern_ntptime.c')
-rw-r--r--sys/kern/kern_ntptime.c1282
1 files changed, 638 insertions, 644 deletions
diff --git a/sys/kern/kern_ntptime.c b/sys/kern/kern_ntptime.c
index 2f4114d7b35d..ec98387201b7 100644
--- a/sys/kern/kern_ntptime.c
+++ b/sys/kern/kern_ntptime.c
@@ -1,507 +1,276 @@
-/******************************************************************************
- * *
- * Copyright (c) David L. Mills 1993, 1994 *
- * *
- * Permission to use, copy, modify, and distribute this software and its *
- * documentation for any purpose and without fee is hereby granted, provided *
- * that the above copyright notice appears in all copies and that both the *
- * copyright notice and this permission notice appear in supporting *
- * documentation, and that the name University of Delaware not be used in *
- * advertising or publicity pertaining to distribution of the software *
- * without specific, written prior permission. The University of Delaware *
- * makes no representations about the suitability this software for any *
- * purpose. It is provided "as is" without express or implied warranty. *
- * *
- ******************************************************************************/
+/***********************************************************************
+ * *
+ * Copyright (c) David L. Mills 1993-1998 *
+ * *
+ * Permission to use, copy, modify, and distribute this software and *
+ * its documentation for any purpose and without fee is hereby *
+ * granted, provided that the above copyright notice appears in all *
+ * copies and that both the copyright notice and this permission *
+ * notice appear in supporting documentation, and that the name *
+ * University of Delaware not be used in advertising or publicity *
+ * pertaining to distribution of the software without specific, *
+ * written prior permission. The University of Delaware makes no *
+ * representations about the suitability this software for any *
+ * purpose. It is provided "as is" without express or implied *
+ * warranty. *
+ * *
+ **********************************************************************/
/*
- * Modification history kern_ntptime.c
+ * Adapted from the original sources for FreeBSD and timecounters by:
+ * Poul-Henning Kamp <phk@FreeBSD.org>
*
- * 24 Sep 94 David L. Mills
- * Tightened code at exits.
+ * The 32bit version of the "LP" macros seems a bit past its "sell by"
+ * date so I have retained only the 64bit version and included it directly
+ * in this file.
*
- * 24 Mar 94 David L. Mills
- * Revised syscall interface to include new variables for PPS
- * time discipline.
+ * Only minor changes done to interface with the timecounters over in
+ * sys/kern/kern_clock.c. Some of the comments below may be (even more)
+ * confusing and/or plain wrong in that context.
*
- * 14 Feb 94 David L. Mills
- * Added code for external clock
+ * The PPS_SYNC/hardpps() is currently not supported.
*
- * 28 Nov 93 David L. Mills
- * Revised frequency scaling to conform with adjusted parameters
- *
- * 17 Sep 93 David L. Mills
- * Created file
- */
-/*
- * ntp_gettime(), ntp_adjtime() - precision time interface for SunOS
- * V4.1.1 and V4.1.3
- *
- * These routines consitute the Network Time Protocol (NTP) interfaces
- * for user and daemon application programs. The ntp_gettime() routine
- * provides the time, maximum error (synch distance) and estimated error
- * (dispersion) to client user application programs. The ntp_adjtime()
- * routine is used by the NTP daemon to adjust the system clock to an
- * externally derived time. The time offset and related variables set by
- * this routine are used by hardclock() to adjust the phase and
- * frequency of the phase-lock loop which controls the system clock.
*/
-#include "opt_ntp.h"
-
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/sysproto.h>
#include <sys/kernel.h>
#include <sys/proc.h>
+#include <sys/time.h>
#include <sys/timex.h>
#include <sys/timepps.h>
#include <sys/sysctl.h>
/*
- * Phase/frequency-lock loop (PLL/FLL) definitions
- *
- * The following variables are read and set by the ntp_adjtime() system
- * call.
- *
- * time_state shows the state of the system clock, with values defined
- * in the timex.h header file.
- *
- * time_status shows the status of the system clock, with bits defined
- * in the timex.h header file.
- *
- * time_offset is used by the PLL/FLL to adjust the system time in small
- * increments.
- *
- * time_constant determines the bandwidth or "stiffness" of the PLL.
- *
- * time_tolerance determines maximum frequency error or tolerance of the
- * CPU clock oscillator and is a property of the architecture; however,
- * in principle it could change as result of the presence of external
- * discipline signals, for instance.
- *
- * time_precision is usually equal to the kernel tick variable; however,
- * in cases where a precision clock counter or external clock is
- * available, the resolution can be much less than this and depend on
- * whether the external clock is working or not.
- *
- * time_maxerror is initialized by a ntp_adjtime() call and increased by
- * the kernel once each second to reflect the maximum error
- * bound growth.
- *
- * time_esterror is set and read by the ntp_adjtime() call, but
- * otherwise not used by the kernel.
+ * Single-precision macros for 64-bit machines
*/
-static int time_status = STA_UNSYNC; /* clock status bits */
-static int time_state = TIME_OK; /* clock state */
-static long time_offset = 0; /* time offset (us) */
-static long time_constant = 0; /* pll time constant */
-static long time_tolerance = MAXFREQ; /* frequency tolerance (scaled ppm) */
-static long time_precision = 1; /* clock precision (us) */
-static long time_maxerror = MAXPHASE; /* maximum error (us) */
-static long time_esterror = MAXPHASE; /* estimated error (us) */
-static int time_daemon = 0; /* No timedaemon active */
+typedef long long l_fp;
+#define L_ADD(v, u) ((v) += (u))
+#define L_SUB(v, u) ((v) -= (u))
+#define L_ADDHI(v, a) ((v) += (long long)(a) << 32)
+#define L_NEG(v) ((v) = -(v))
+#define L_RSHIFT(v, n) \
+ do { \
+ if ((v) < 0) \
+ (v) = -(-(v) >> (n)); \
+ else \
+ (v) = (v) >> (n); \
+ } while (0)
+#define L_MPY(v, a) ((v) *= (a))
+#define L_CLR(v) ((v) = 0)
+#define L_ISNEG(v) ((v) < 0)
+#define L_LINT(v, a) ((v) = (long long)(a) << 32)
+#define L_GINT(v) ((v) < 0 ? -(-(v) >> 32) : (v) >> 32)
/*
- * The following variables establish the state of the PLL/FLL and the
- * residual time and frequency offset of the local clock. The scale
- * factors are defined in the timex.h header file.
+ * Generic NTP kernel interface
*
- * time_phase and time_freq are the phase increment and the frequency
- * increment, respectively, of the kernel time variable at each tick of
- * the clock.
- *
- * time_freq is set via ntp_adjtime() from a value stored in a file when
- * the synchronization daemon is first started. Its value is retrieved
- * via ntp_adjtime() and written to the file about once per hour by the
- * daemon.
- *
- * time_adj is the adjustment added to the value of tick at each timer
- * interrupt and is recomputed from time_phase and time_freq at each
- * seconds rollover.
- *
- * time_reftime is the second's portion of the system time on the last
- * call to ntp_adjtime(). It is used to adjust the time_freq variable
- * and to increase the time_maxerror as the time since last update
- * increases.
+ * These routines constitute the Network Time Protocol (NTP) interfaces
+ * for user and daemon application programs. The ntp_gettime() routine
+ * provides the time, maximum error (synch distance) and estimated error
+ * (dispersion) to client user application programs. The ntp_adjtime()
+ * routine is used by the NTP daemon to adjust the system clock to an
+ * externally derived time. The time offset and related variables set by
+ * this routine are used by other routines in this module to adjust the
+ * phase and frequency of the clock discipline loop which controls the
+ * system clock.
+ *
+ * When the kernel time is reckoned directly in nanoseconds (NANO
+ * defined), the time at each tick interrupt is derived directly from
+ * the kernel time variable. When the kernel time is reckoned in
+ * microseconds, (NANO undefined), the time is derived from the kernel
+ * time variable together with a variable representing the leftover
+ * nanoseconds at the last tick interrupt. In either case, the current
+ * nanosecond time is reckoned from these values plus an interpolated
+ * value derived by the clock routines in another architecture-specific
+ * module. The interpolation can use either a dedicated counter or a
+ * processor cycle counter (PCC) implemented in some architectures.
+ *
+ * Note that all routines must run at priority splclock or higher.
*/
-long time_phase = 0; /* phase offset (scaled us) */
-static long time_freq = 0; /* frequency offset (scaled ppm) */
-long time_adj = 0; /* tick adjust (scaled 1 / hz) */
-static long time_reftime = 0; /* time at last adjustment (s) */
-#ifdef PPS_SYNC
/*
- * The following variables are used only if the kernel PPS discipline
- * code is configured (PPS_SYNC). The scale factors are defined in the
- * timex.h header file.
- *
- * pps_time contains the time at each calibration interval, as read by
- * microtime(). pps_count counts the seconds of the calibration
- * interval, the duration of which is nominally pps_shift in powers of
- * two.
- *
- * pps_offset is the time offset produced by the time median filter
- * pps_tf[], while pps_jitter is the dispersion (jitter) measured by
- * this filter.
- *
- * pps_freq is the frequency offset produced by the frequency median
- * filter pps_ff[], while pps_stabil is the dispersion (wander) measured
- * by this filter.
- *
- * pps_usec is latched from a high resolution counter or external clock
- * at pps_time. Here we want the hardware counter contents only, not the
- * contents plus the time_tv.usec as usual.
- *
- * pps_valid counts the number of seconds since the last PPS update. It
- * is used as a watchdog timer to disable the PPS discipline should the
- * PPS signal be lost.
- *
- * pps_glitch counts the number of seconds since the beginning of an
- * offset burst more than tick/2 from current nominal offset. It is used
- * mainly to suppress error bursts due to priority conflicts between the
- * PPS interrupt and timer interrupt.
+ * Phase/frequency-lock loop (PLL/FLL) definitions
*
- * pps_intcnt counts the calibration intervals for use in the interval-
- * adaptation algorithm. It's just too complicated for words.
+ * The nanosecond clock discipline uses two variable types, time
+ * variables and frequency variables. Both types are represented as 64-
+ * bit fixed-point quantities with the decimal point between two 32-bit
+ * halves. On a 32-bit machine, each half is represented as a single
+ * word and mathematical operations are done using multiple-precision
+ * arithmetic. On a 64-bit machine, ordinary computer arithmetic is
+ * used.
+ *
+ * A time variable is a signed 64-bit fixed-point number in ns and
+ * fraction. It represents the remaining time offset to be amortized
+ * over succeeding tick interrupts. The maximum time offset is about
+ * 0.512 s and the resolution is about 2.3e-10 ns.
+ *
+ * 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * |s s s| ns |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | fraction |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *
+ * A frequency variable is a signed 64-bit fixed-point number in ns/s
+ * and fraction. It represents the ns and fraction to be added to the
+ * kernel time variable at each second. The maximum frequency offset is
+ * about +-512000 ns/s and the resolution is about 2.3e-10 ns/s.
+ *
+ * 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * |s s s s s s s s s s s s s| ns/s |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | fraction |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*/
-static struct timeval pps_time; /* kernel time at last interval */
-static long pps_offset = 0; /* pps time offset (us) */
-static long pps_jitter = MAXTIME; /* pps time dispersion (jitter) (us) */
-static long pps_tf[] = {0, 0, 0}; /* pps time offset median filter (us) */
-static long pps_freq = 0; /* frequency offset (scaled ppm) */
-static long pps_stabil = MAXFREQ; /* frequency dispersion (scaled ppm) */
-static long pps_ff[] = {0, 0, 0}; /* frequency offset median filter */
-static long pps_usec = 0; /* microsec counter at last interval */
-static long pps_valid = PPS_VALID; /* pps signal watchdog counter */
-static int pps_glitch = 0; /* pps signal glitch counter */
-static int pps_count = 0; /* calibration interval counter (s) */
-static int pps_shift = PPS_SHIFT; /* interval duration (s) (shift) */
-static int pps_intcnt = 0; /* intervals at current duration */
-
/*
- * PPS signal quality monitors
- *
- * pps_jitcnt counts the seconds that have been discarded because the
- * jitter measured by the time median filter exceeds the limit MAXTIME
- * (100 us).
- *
- * pps_calcnt counts the frequency calibration intervals, which are
- * variable from 4 s to 256 s.
- *
- * pps_errcnt counts the calibration intervals which have been discarded
- * because the wander exceeds the limit MAXFREQ (100 ppm) or where the
- * calibration interval jitter exceeds two ticks.
- *
- * pps_stbcnt counts the calibration intervals that have been discarded
- * because the frequency wander exceeds the limit MAXFREQ / 4 (25 us).
+ * The following variables establish the state of the PLL/FLL and the
+ * residual time and frequency offset of the local clock.
*/
-static long pps_jitcnt = 0; /* jitter limit exceeded */
-static long pps_calcnt = 0; /* calibration intervals */
-static long pps_errcnt = 0; /* calibration errors */
-static long pps_stbcnt = 0; /* stability limit exceeded */
-#endif /* PPS_SYNC */
+#define SHIFT_PLL 4 /* PLL loop gain (shift) */
+#define SHIFT_FLL 2 /* FLL loop gain (shift) */
-static void hardupdate __P((int64_t offset, int prescaled));
+static int time_state = TIME_OK; /* clock state */
+static int time_status = STA_UNSYNC; /* clock status bits */
+static long time_constant; /* poll interval (shift) (s) */
+static long time_precision = 1; /* clock precision (ns) */
+static long time_maxerror = MAXPHASE / 1000; /* maximum error (us) */
+static long time_esterror = MAXPHASE / 1000; /* estimated error (us) */
+static long time_reftime; /* time at last adjustment (s) */
+static long time_tick; /* nanoseconds per tick (ns) */
+static l_fp time_offset; /* time offset (ns) */
+static l_fp time_freq; /* frequency offset (ns/s) */
+#ifdef PPS_SYNC
/*
- * hardupdate() - local clock update
- *
- * This routine is called by ntp_adjtime() to update the local clock
- * phase and frequency. The implementation is of an adaptive-parameter,
- * hybrid phase/frequency-lock loop (PLL/FLL). The routine computes new
- * time and frequency offset estimates for each call. If the kernel PPS
- * discipline code is configured (PPS_SYNC), the PPS signal itself
- * determines the new time offset, instead of the calling argument.
- * Presumably, calls to ntp_adjtime() occur only when the caller
- * believes the local clock is valid within some bound (+-128 ms with
- * NTP). If the caller's time is far different than the PPS time, an
- * argument will ensue, and it's not clear who will lose.
- *
- * For uncompensated quartz crystal oscillatores and nominal update
- * intervals less than 1024 s, operation should be in phase-lock mode
- * (STA_FLL = 0), where the loop is disciplined to phase. For update
- * intervals greater than thiss, operation should be in frequency-lock
- * mode (STA_FLL = 1), where the loop is disciplined to frequency.
- *
- * Note: splclock() is in effect.
+ * The following variables are used when a pulse-per-second (PPS) signal
+ * is available and connected via a modem control lead. They establish
+ * the engineering parameters of the clock discipline loop when
+ * controlled by the PPS signal.
*/
-static void
-hardupdate(offset, prescaled)
- int64_t offset;
- int prescaled;
-{
- long mtemp;
- int64_t ltemp;
+#define PPS_FAVG 2 /* min freq avg interval (s) (shift) */
+#define PPS_FAVGMAX 8 /* max freq avg interval (s) (shift) */
+#define PPS_PAVG 4 /* phase avg interval (s) (shift) */
+#define PPS_VALID 120 /* PPS signal watchdog max (s) */
+#define MAXTIME 500000 /* max PPS error (jitter) (ns) */
+#define MAXWANDER 500000 /* max PPS wander (ns/s/s) */
+
+struct ppstime {
+ long sec; /* PPS seconds */
+ long nsec; /* PPS nanoseconds */
+ long count; /* PPS nanosecond counter */
+};
+static struct ppstime pps_tf[3]; /* phase median filter */
+static struct ppstime pps_filt; /* phase offset */
+static l_fp pps_freq; /* scaled frequency offset (ns/s) */
+static long pps_lastfreq; /* last scaled freq offset (ns/s) */
+static long pps_offacc; /* offset accumulator */
+static long pps_jitter; /* scaled time dispersion (ns) */
+static long pps_stabil; /* scaled frequency dispersion (ns/s) */
+static long pps_lastcount; /* last counter offset */
+static long pps_lastsec; /* time at last calibration (s) */
+static int pps_valid; /* signal watchdog counter */
+static int pps_shift = PPS_FAVG; /* interval duration (s) (shift) */
+static int pps_intcnt; /* wander counter */
+static int pps_offcnt; /* offset accumulator counter */
- if (!(time_status & STA_PLL) && !(time_status & STA_PPSTIME))
- return;
- if (prescaled)
- ltemp = offset;
- else
- ltemp = offset << SHIFT_UPDATE;
-#ifdef PPS_SYNC
- if (time_status & STA_PPSTIME && time_status & STA_PPSSIGNAL)
- ltemp = pps_offset << SHIFT_UPDATE;
+/*
+ * PPS signal quality monitors
+ */
+static long pps_calcnt; /* calibration intervals */
+static long pps_jitcnt; /* jitter limit exceeded */
+static long pps_stbcnt; /* stability limit exceeded */
+static long pps_errcnt; /* calibration errors */
#endif /* PPS_SYNC */
+/*
+ * End of phase/frequency-lock loop (PLL/FLL) definitions
+ */
- /*
- * Scale the phase adjustment and clamp to the operating range.
- */
- if (ltemp > (MAXPHASE << SHIFT_UPDATE))
- time_offset = MAXPHASE << SHIFT_UPDATE;
- else if (ltemp < -(MAXPHASE << SHIFT_UPDATE))
- time_offset = -(MAXPHASE << SHIFT_UPDATE);
- else
- time_offset = ltemp;
-
- /*
- * Select whether the frequency is to be controlled and in which
- * mode (PLL or FLL). Clamp to the operating range. Ugly
- * multiply/divide should be replaced someday.
- */
- if (time_status & STA_FREQHOLD || time_reftime == 0)
- time_reftime = time_second;
- mtemp = time_second - time_reftime;
- time_reftime = time_second;
- if (time_status & STA_FLL) {
- if (mtemp >= MINSEC) {
- ltemp = ((time_offset / mtemp) << (SHIFT_USEC -
- SHIFT_UPDATE));
- if (ltemp < 0)
- time_freq -= -ltemp >> SHIFT_KH;
- else
- time_freq += ltemp >> SHIFT_KH;
- }
- } else {
- if (mtemp < MAXSEC) {
- ltemp = time_offset * mtemp;
- if (ltemp < 0)
- time_freq -= -ltemp >> ((int64_t)time_constant +
- time_constant + SHIFT_KF -
- SHIFT_USEC + SHIFT_UPDATE);
- else
- time_freq += ltemp >> ((int64_t)time_constant +
- time_constant + SHIFT_KF -
- SHIFT_USEC + SHIFT_UPDATE);
- }
- }
- if (time_freq > time_tolerance)
- time_freq = time_tolerance;
- else if (time_freq < -time_tolerance)
- time_freq = -time_tolerance;
-}
+static void ntp_init(void);
+static void hardupdate(long offset);
/*
- * On rollover of the second the phase adjustment to be used for
- * the next second is calculated. Also, the maximum error is
- * increased by the tolerance. If the PPS frequency discipline
- * code is present, the phase is increased to compensate for the
- * CPU clock oscillator frequency error.
+ * ntp_gettime() - NTP user application interface
*
- * On a 32-bit machine and given parameters in the timex.h
- * header file, the maximum phase adjustment is +-512 ms and
- * maximum frequency offset is a tad less than) +-512 ppm. On a
- * 64-bit machine, you shouldn't need to ask.
+ * See the timex.h header file for synopsis and API description.
*/
-void
-ntp_update_second(struct timecounter *tc)
-{
- u_int32_t *newsec;
- long ltemp;
-
- if (!time_daemon)
- return;
-
- newsec = &tc->tc_offset_sec;
- time_maxerror += time_tolerance >> SHIFT_USEC;
-
- /*
- * Compute the phase adjustment for the next second. In
- * PLL mode, the offset is reduced by a fixed factor
- * times the time constant. In FLL mode the offset is
- * used directly. In either mode, the maximum phase
- * adjustment for each second is clamped so as to spread
- * the adjustment over not more than the number of
- * seconds between updates.
- */
- if (time_offset < 0) {
- ltemp = -time_offset;
- if (!(time_status & STA_FLL))
- ltemp >>= SHIFT_KG + time_constant;
- if (ltemp > (MAXPHASE / MINSEC) << SHIFT_UPDATE)
- ltemp = (MAXPHASE / MINSEC) << SHIFT_UPDATE;
- time_offset += ltemp;
- time_adj = -ltemp << (SHIFT_SCALE - SHIFT_UPDATE);
- } else {
- ltemp = time_offset;
- if (!(time_status & STA_FLL))
- ltemp >>= SHIFT_KG + time_constant;
- if (ltemp > (MAXPHASE / MINSEC) << SHIFT_UPDATE)
- ltemp = (MAXPHASE / MINSEC) << SHIFT_UPDATE;
- time_offset -= ltemp;
- time_adj = ltemp << (SHIFT_SCALE - SHIFT_UPDATE);
- }
-
- /*
- * Compute the frequency estimate and additional phase
- * adjustment due to frequency error for the next
- * second. When the PPS signal is engaged, gnaw on the
- * watchdog counter and update the frequency computed by
- * the pll and the PPS signal.
- */
-#ifdef PPS_SYNC
- pps_valid++;
- if (pps_valid == PPS_VALID) {
- pps_jitter = MAXTIME;
- pps_stabil = MAXFREQ;
- time_status &= ~(STA_PPSSIGNAL | STA_PPSJITTER |
- STA_PPSWANDER | STA_PPSERROR);
- }
- ltemp = time_freq + pps_freq;
-#else
- ltemp = time_freq;
-#endif /* PPS_SYNC */
- if (ltemp < 0)
- time_adj -= -ltemp << (SHIFT_SCALE - SHIFT_USEC);
- else
- time_adj += ltemp << (SHIFT_SCALE - SHIFT_USEC);
-
- tc->tc_adjustment = time_adj;
-
- /* XXX - this is really bogus, but can't be fixed until
- xntpd's idea of the system clock is fixed to know how
- the user wants leap seconds handled; in the mean time,
- we assume that users of NTP are running without proper
- leap second support (this is now the default anyway) */
- /*
- * Leap second processing. If in leap-insert state at
- * the end of the day, the system clock is set back one
- * second; if in leap-delete state, the system clock is
- * set ahead one second. The microtime() routine or
- * external clock driver will insure that reported time
- * is always monotonic. The ugly divides should be
- * replaced.
- */
- switch (time_state) {
-
- case TIME_OK:
- if (time_status & STA_INS)
- time_state = TIME_INS;
- else if (time_status & STA_DEL)
- time_state = TIME_DEL;
- break;
-
- case TIME_INS:
- if ((*newsec) % 86400 == 0) {
- (*newsec)--;
- time_state = TIME_OOP;
- }
- break;
-
- case TIME_DEL:
- if (((*newsec) + 1) % 86400 == 0) {
- (*newsec)++;
- time_state = TIME_WAIT;
- }
- break;
-
- case TIME_OOP:
- time_state = TIME_WAIT;
- break;
-
- case TIME_WAIT:
- if (!(time_status & (STA_INS | STA_DEL)))
- time_state = TIME_OK;
- break;
- }
-}
-
static int
ntp_sysctl SYSCTL_HANDLER_ARGS
{
- struct timeval atv;
- struct ntptimeval ntv;
- int s;
+ struct ntptimeval ntv; /* temporary structure */
+ struct timespec atv; /* nanosecond time */
- s = splclock();
- microtime(&atv);
- ntv.time = atv;
+ nanotime(&atv);
+ ntv.time.tv_sec = atv.tv_sec;
+ ntv.time.tv_nsec = atv.tv_nsec;
ntv.maxerror = time_maxerror;
ntv.esterror = time_esterror;
- splx(s);
-
ntv.time_state = time_state;
/*
- * Status word error decode. If any of these conditions
- * occur, an error is returned, instead of the status
- * word. Most applications will care only about the fact
- * the system clock may not be trusted, not about the
- * details.
+ * Status word error decode. If any of these conditions occur,
+ * an error is returned, instead of the status word. Most
+ * applications will care only about the fact the system clock
+ * may not be trusted, not about the details.
*
* Hardware or software error
*/
- if (time_status & (STA_UNSYNC | STA_CLOCKERR)) {
- ntv.time_state = TIME_ERROR;
- }
+ if ((time_status & (STA_UNSYNC | STA_CLOCKERR)) ||
/*
- * PPS signal lost when either time or frequency
- * synchronization requested
+ * PPS signal lost when either time or frequency synchronization
+ * requested
*/
- if (time_status & (STA_PPSFREQ | STA_PPSTIME) &&
- !(time_status & STA_PPSSIGNAL)) {
- ntv.time_state = TIME_ERROR;
- }
+ (time_status & (STA_PPSFREQ | STA_PPSTIME) &&
+ !(time_status & STA_PPSSIGNAL)) ||
/*
- * PPS jitter exceeded when time synchronization
- * requested
+ * PPS jitter exceeded when time synchronization requested
*/
- if (time_status & STA_PPSTIME &&
- time_status & STA_PPSJITTER) {
- ntv.time_state = TIME_ERROR;
- }
+ (time_status & STA_PPSTIME &&
+ time_status & STA_PPSJITTER) ||
/*
- * PPS wander exceeded or calibration error when
- * frequency synchronization requested
+ * PPS wander exceeded or calibration error when frequency
+ * synchronization requested
*/
- if (time_status & STA_PPSFREQ &&
- time_status & (STA_PPSWANDER | STA_PPSERROR)) {
+ (time_status & STA_PPSFREQ &&
+ time_status & (STA_PPSWANDER | STA_PPSERROR)))
ntv.time_state = TIME_ERROR;
- }
return (sysctl_handle_opaque(oidp, &ntv, sizeof ntv, req));
}
-SYSCTL_NODE(_kern, KERN_NTP_PLL, ntp_pll, CTLFLAG_RW, 0,
- "NTP kernel PLL related stuff");
-SYSCTL_PROC(_kern_ntp_pll, NTP_PLL_GETTIME, gettime, CTLTYPE_OPAQUE|CTLFLAG_RD,
+SYSCTL_NODE(_kern, OID_AUTO, ntp_pll, CTLFLAG_RW, 0, "");
+SYSCTL_PROC(_kern_ntp_pll, OID_AUTO, gettime, CTLTYPE_OPAQUE|CTLFLAG_RD,
0, sizeof(struct ntptimeval) , ntp_sysctl, "S,ntptimeval", "");
+
/*
* ntp_adjtime() - NTP daemon application interface
+ *
+ * See the timex.h header file for synopsis and API description.
*/
#ifndef _SYS_SYSPROTO_H_
struct ntp_adjtime_args {
- struct timex *tp;
+ struct timex *tp;
};
#endif
int
ntp_adjtime(struct proc *p, struct ntp_adjtime_args *uap)
{
- struct timex ntv;
- int modes;
- int s;
+ struct timex ntv; /* temporary structure */
+ int modes; /* mode bits from structure */
+ int s; /* caller priority */
int error;
- time_daemon = 1;
-
error = copyin((caddr_t)uap->tp, (caddr_t)&ntv, sizeof(ntv));
if (error)
- return error;
+ return(error);
/*
* Update selected clock variables - only the superuser can
@@ -509,17 +278,16 @@ ntp_adjtime(struct proc *p, struct ntp_adjtime_args *uap)
* the assumption the superuser should know what it is doing.
*/
modes = ntv.modes;
- if ((modes != 0)
- && (error = suser(p->p_cred->pc_ucred, &p->p_acflag)))
- return error;
-
+ error = suser(p->p_cred->pc_ucred, &p->p_acflag);
+ if (error)
+ return (error);
s = splclock();
- if (modes & MOD_FREQUENCY)
+ if (modes & MOD_FREQUENCY) {
+ L_LINT(time_freq, ntv.freq / SCALE_PPM);
#ifdef PPS_SYNC
- time_freq = ntv.freq - pps_freq;
-#else /* PPS_SYNC */
- time_freq = ntv.freq;
+ pps_freq = time_freq;
#endif /* PPS_SYNC */
+ }
if (modes & MOD_MAXERROR)
time_maxerror = ntv.maxerror;
if (modes & MOD_ESTERROR)
@@ -530,93 +298,301 @@ ntp_adjtime(struct proc *p, struct ntp_adjtime_args *uap)
}
if (modes & MOD_TIMECONST)
time_constant = ntv.constant;
- if (modes & MOD_OFFSET)
- hardupdate(ntv.offset, modes & MOD_DOSCALE);
+ if (modes & MOD_NANO)
+ time_status |= STA_NANO;
+ if (modes & MOD_MICRO)
+ time_status &= ~STA_NANO;
+ if (modes & MOD_CLKB)
+ time_status |= STA_CLK;
+ if (modes & MOD_CLKA)
+ time_status &= ~STA_CLK;
+ if (modes & MOD_OFFSET) {
+ if (time_status & STA_NANO)
+ hardupdate(ntv.offset);
+ else
+ hardupdate(ntv.offset * 1000);
+ }
- ntv.modes |= MOD_CANSCALE;
/*
* Retrieve all clock variables
*/
- if (modes & MOD_DOSCALE)
- ntv.offset = time_offset;
- else if (time_offset < 0)
- ntv.offset = -(-time_offset >> SHIFT_UPDATE);
+ if (time_status & STA_NANO)
+ ntv.offset = L_GINT(time_offset);
else
- ntv.offset = time_offset >> SHIFT_UPDATE;
-#ifdef PPS_SYNC
- ntv.freq = time_freq + pps_freq;
-#else /* PPS_SYNC */
- ntv.freq = time_freq;
-#endif /* PPS_SYNC */
+ ntv.offset = L_GINT(time_offset) / 1000;
+ ntv.freq = L_GINT(time_freq) * SCALE_PPM;
ntv.maxerror = time_maxerror;
ntv.esterror = time_esterror;
ntv.status = time_status;
- ntv.constant = time_constant;
- ntv.precision = time_precision;
- ntv.tolerance = time_tolerance;
+ if (ntv.constant < 0)
+ time_constant = 0;
+ else if (ntv.constant > MAXTC)
+ time_constant = MAXTC;
+ else
+ time_constant = ntv.constant;
+ if (time_status & STA_NANO)
+ ntv.precision = time_precision;
+ else
+ ntv.precision = time_precision / 1000;
+ ntv.tolerance = MAXFREQ * SCALE_PPM;
#ifdef PPS_SYNC
ntv.shift = pps_shift;
- ntv.ppsfreq = pps_freq;
- ntv.jitter = pps_jitter >> PPS_AVG;
+ ntv.ppsfreq = L_GINT(pps_freq) * SCALE_PPM;
+ ntv.jitter = pps_jitter;
+ if (time_status & STA_NANO)
+ ntv.jitter = pps_jitter;
+ else
+ ntv.jitter = pps_jitter / 1000;
ntv.stabil = pps_stabil;
ntv.calcnt = pps_calcnt;
ntv.errcnt = pps_errcnt;
ntv.jitcnt = pps_jitcnt;
ntv.stbcnt = pps_stbcnt;
#endif /* PPS_SYNC */
- (void)splx(s);
+ splx(s);
error = copyout((caddr_t)&ntv, (caddr_t)uap->tp, sizeof(ntv));
- if (!error) {
+ if (error)
+ return (error);
+
+ /*
+ * Status word error decode. See comments in
+ * ntp_gettime() routine.
+ */
+ if ((time_status & (STA_UNSYNC | STA_CLOCKERR)) ||
+ (time_status & (STA_PPSFREQ | STA_PPSTIME) &&
+ !(time_status & STA_PPSSIGNAL)) ||
+ (time_status & STA_PPSTIME &&
+ time_status & STA_PPSJITTER) ||
+ (time_status & STA_PPSFREQ &&
+ time_status & (STA_PPSWANDER | STA_PPSERROR)))
+ return (TIME_ERROR);
+ return (time_state);
+}
+
+/*
+ * second_overflow() - called after ntp_tick_adjust()
+ *
+ * This routine is ordinarily called immediately following the above
+ * routine ntp_tick_adjust(). While these two routines are normally
+ * combined, they are separated here only for the purposes of
+ * simulation.
+ */
+void
+ntp_update_second(struct timecounter *tcp)
+{
+ u_int32_t *newsec;
+ l_fp ftemp, time_adj; /* 32/64-bit temporary */
+
+ newsec = &tcp->tc_offset_sec;
+ time_maxerror += MAXFREQ / 1000;
+
+ /*
+ * Leap second processing. If in leap-insert state at
+ * the end of the day, the system clock is set back one
+ * second; if in leap-delete state, the system clock is
+ * set ahead one second. The nano_time() routine or
+ * external clock driver will insure that reported time
+ * is always monotonic.
+ */
+ switch (time_state) {
+
+ /*
+ * No warning.
+ */
+ case TIME_OK:
+ if (time_status & STA_INS)
+ time_state = TIME_INS;
+ else if (time_status & STA_DEL)
+ time_state = TIME_DEL;
+ break;
+
+ /*
+ * Insert second 23:59:60 following second
+ * 23:59:59.
+ */
+ case TIME_INS:
+ if (!(time_status & STA_INS))
+ time_state = TIME_OK;
+ else if ((*newsec) % 86400 == 0) {
+ (*newsec)--;
+ time_state = TIME_OOP;
+ }
+ break;
+
+ /*
+ * Delete second 23:59:59.
+ */
+ case TIME_DEL:
+ if (!(time_status & STA_DEL))
+ time_state = TIME_OK;
+ else if (((*newsec) + 1) % 86400 == 0) {
+ (*newsec)++;
+ time_state = TIME_WAIT;
+ }
+ break;
+
+ /*
+ * Insert second in progress.
+ */
+ case TIME_OOP:
+ time_state = TIME_WAIT;
+ break;
+
/*
- * Status word error decode. See comments in
- * ntp_gettime() routine.
+ * Wait for status bits to clear.
*/
- p->p_retval[0] = time_state;
- if (time_status & (STA_UNSYNC | STA_CLOCKERR))
- p->p_retval[0] = TIME_ERROR;
- if (time_status & (STA_PPSFREQ | STA_PPSTIME) &&
- !(time_status & STA_PPSSIGNAL))
- p->p_retval[0] = TIME_ERROR;
- if (time_status & STA_PPSTIME &&
- time_status & STA_PPSJITTER)
- p->p_retval[0] = TIME_ERROR;
- if (time_status & STA_PPSFREQ &&
- time_status & (STA_PPSWANDER | STA_PPSERROR))
- p->p_retval[0] = TIME_ERROR;
+ case TIME_WAIT:
+ if (!(time_status & (STA_INS | STA_DEL)))
+ time_state = TIME_OK;
}
- return error;
+
+ /*
+ * Compute the total time adjustment for the next
+ * second in ns. The offset is reduced by a factor
+ * depending on FLL or PLL mode and whether the PPS
+ * signal is operating. Note that the value is in effect
+ * scaled by the clock frequency, since the adjustment
+ * is added at each tick interrupt.
+ */
+ ftemp = time_offset;
+#ifdef PPS_SYNC
+ if (time_status & STA_PPSTIME && time_status &
+ STA_PPSSIGNAL)
+ L_RSHIFT(ftemp, PPS_FAVG);
+ else if (time_status & STA_MODE)
+#else
+ if (time_status & STA_MODE)
+#endif /* PPS_SYNC */
+ L_RSHIFT(ftemp, SHIFT_FLL);
+ else
+ L_RSHIFT(ftemp, SHIFT_PLL + time_constant);
+ time_adj = ftemp;
+ L_SUB(time_offset, ftemp);
+ L_ADD(time_adj, time_freq);
+ tcp->tc_adjustment = time_adj;
+#ifdef PPS_SYNC
+ if (pps_valid > 0)
+ pps_valid--;
+ else
+ time_status &= ~(STA_PPSSIGNAL | STA_PPSJITTER |
+ STA_PPSWANDER | STA_PPSERROR);
+#endif /* PPS_SYNC */
}
+/*
+ * ntp_init() - initialize variables and structures
+ *
+ * This routine must be called after the kernel variables hz and tick
+ * are set or changed and before the next tick interrupt. In this
+ * particular implementation, these values are assumed set elsewhere in
+ * the kernel. The design allows the clock frequency and tick interval
+ * to be changed while the system is running. So, this routine should
+ * probably be integrated with the code that does that.
+ */
+static void
+ntp_init()
+{
+
+ /*
+ * The following variable must be initialized any time the
+ * kernel variable hz is changed.
+ */
+ time_tick = NANOSECOND / hz;
+
+ /*
+ * The following variables are initialized only at startup. Only
+ * those structures not cleared by the compiler need to be
+ * initialized, and these only in the simulator. In the actual
+ * kernel, any nonzero values here will quickly evaporate.
+ */
+ L_CLR(time_offset);
+ L_CLR(time_freq);
#ifdef PPS_SYNC
+ pps_filt.sec = pps_filt.nsec = pps_filt.count = 0;
+ pps_tf[0] = pps_tf[1] = pps_tf[2] = pps_filt;
+ L_CLR(pps_freq);
+#endif /* PPS_SYNC */
+}
-/* We need this ugly monster twice, so let's macroize it. */
+SYSINIT(ntpclocks, SI_SUB_CLOCKS, SI_ORDER_FIRST, ntp_init, NULL)
-#define MEDIAN3X(a, m, s, i1, i2, i3) \
- do { \
- m = a[i2]; \
- s = a[i1] - a[i3]; \
- } while (0)
+/*
+ * hardupdate() - local clock update
+ *
+ * This routine is called by ntp_adjtime() to update the local clock
+ * phase and frequency. The implementation is of an adaptive-parameter,
+ * hybrid phase/frequency-lock loop (PLL/FLL). The routine computes new
+ * time and frequency offset estimates for each call. If the kernel PPS
+ * discipline code is configured (PPS_SYNC), the PPS signal itself
+ * determines the new time offset, instead of the calling argument.
+ * Presumably, calls to ntp_adjtime() occur only when the caller
+ * believes the local clock is valid within some bound (+-128 ms with
+ * NTP). If the caller's time is far different than the PPS time, an
+ * argument will ensue, and it's not clear who will lose.
+ *
+ * For uncompensated quartz crystal oscillators and nominal update
+ * intervals less than 256 s, operation should be in phase-lock mode,
+ * where the loop is disciplined to phase. For update intervals greater
+ * than 1024 s, operation should be in frequency-lock mode, where the
+ * loop is disciplined to frequency. Between 256 s and 1024 s, the mode
+ * is selected by the STA_MODE status bit.
+ */
+static void
+hardupdate(offset)
+ long offset; /* clock offset (ns) */
+{
+ long ltemp, mtemp;
+ l_fp ftemp;
-#define MEDIAN3(a, m, s) \
- do { \
- if (a[0] > a[1]) { \
- if (a[1] > a[2]) \
- MEDIAN3X(a, m, s, 0, 1, 2); \
- else if (a[2] > a[0]) \
- MEDIAN3X(a, m, s, 2, 0, 1); \
- else \
- MEDIAN3X(a, m, s, 0, 2, 1); \
- } else { \
- if (a[2] > a[1]) \
- MEDIAN3X(a, m, s, 2, 1, 0); \
- else if (a[0] > a[2]) \
- MEDIAN3X(a, m, s, 1, 0, 2); \
- else \
- MEDIAN3X(a, m, s, 1, 2, 0); \
- } \
- } while (0)
+ /*
+ * Select how the phase is to be controlled and from which
+ * source. If the PPS signal is present and enabled to
+ * discipline the time, the PPS offset is used; otherwise, the
+ * argument offset is used.
+ */
+ ltemp = offset;
+ if (ltemp > MAXPHASE)
+ ltemp = MAXPHASE;
+ else if (ltemp < -MAXPHASE)
+ ltemp = -MAXPHASE;
+ if (!(time_status & STA_PPSTIME && time_status & STA_PPSSIGNAL))
+ L_LINT(time_offset, ltemp);
+
+ /*
+ * Select how the frequency is to be controlled and in which
+ * mode (PLL or FLL). If the PPS signal is present and enabled
+ * to discipline the frequency, the PPS frequency is used;
+ * otherwise, the argument offset is used to compute it.
+ */
+ if (time_status & STA_PPSFREQ && time_status & STA_PPSSIGNAL) {
+ time_reftime = time_second;
+ return;
+ }
+ if (time_status & STA_FREQHOLD || time_reftime == 0)
+ time_reftime = time_second;
+ mtemp = time_second - time_reftime;
+ if (mtemp >= MINSEC && (time_status & STA_FLL || mtemp > MAXSEC)
+ ) {
+ L_LINT(ftemp, (ltemp << 4) / mtemp);
+ L_RSHIFT(ftemp, SHIFT_FLL + 4);
+ L_ADD(time_freq, ftemp);
+ time_status |= STA_MODE;
+ } else {
+ L_LINT(ftemp, ltemp);
+ L_RSHIFT(ftemp, (SHIFT_PLL + 2 + time_constant) << 1);
+ L_MPY(ftemp, mtemp);
+ L_ADD(time_freq, ftemp);
+ time_status &= ~STA_MODE;
+ }
+ time_reftime = time_second;
+ if (L_GINT(time_freq) > MAXFREQ)
+ L_LINT(time_freq, MAXFREQ);
+ else if (L_GINT(time_freq) < -MAXFREQ)
+ L_LINT(time_freq, -MAXFREQ);
+}
+#ifdef PPS_SYNC
/*
* hardpps() - discipline CPU clock oscillator to external PPS signal
*
@@ -625,198 +601,216 @@ ntp_adjtime(struct proc *p, struct ntp_adjtime_args *uap)
* and leaves it in a handy spot for the hardclock() routine. It
* integrates successive PPS phase differences and calculates the
* frequency offset. This is used in hardclock() to discipline the CPU
- * clock oscillator so that intrinsic frequency error is cancelled out.
- * The code requires the caller to capture the time and hardware counter
- * value at the on-time PPS signal transition.
+ * clock oscillator so that the intrinsic frequency error is cancelled
+ * out. The code requires the caller to capture the time and
+ * architecture-dependent hardware counter values in nanoseconds at the
+ * on-time PPS signal transition.
*
- * Note that, on some Unix systems, this routine runs at an interrupt
+ * Note that, on some Unix systems this routine runs at an interrupt
* priority level higher than the timer interrupt routine hardclock().
* Therefore, the variables used are distinct from the hardclock()
- * variables, except for certain exceptions: The PPS frequency pps_freq
- * and phase pps_offset variables are determined by this routine and
- * updated atomically. The time_tolerance variable can be considered a
- * constant, since it is infrequently changed, and then only when the
- * PPS signal is disabled. The watchdog counter pps_valid is updated
- * once per second by hardclock() and is atomically cleared in this
- * routine.
+ * variables, except for the actual time and frequency variables, which
+ * are determined by this routine and updated atomically.
*/
void
-hardpps(tvp, p_usec)
- struct timeval *tvp; /* time at PPS */
- long p_usec; /* hardware counter at PPS */
+hardpps(tsp, nsec)
+ struct timespec *tsp; /* time at PPS */
+ long nsec; /* hardware counter at PPS */
{
- long u_usec, v_usec, bigtick;
- long cal_sec, cal_usec;
+ long u_sec, u_nsec, v_nsec; /* temps */
+ l_fp ftemp;
/*
- * An occasional glitch can be produced when the PPS interrupt
- * occurs in the hardclock() routine before the time variable is
- * updated. Here the offset is discarded when the difference
- * between it and the last one is greater than tick/2, but not
- * if the interval since the first discard exceeds 30 s.
+ * The signal is first processed by a frequency discriminator
+ * which rejects noise and input signals with frequencies
+ * outside the range 1 +-MAXFREQ PPS. If two hits occur in the
+ * same second, we ignore the later hit; if not and a hit occurs
+ * outside the range gate, keep the later hit but do not
+ * process it.
*/
- time_status |= STA_PPSSIGNAL;
- time_status &= ~(STA_PPSJITTER | STA_PPSWANDER | STA_PPSERROR);
- pps_valid = 0;
- u_usec = -tvp->tv_usec;
- if (u_usec < -500000)
- u_usec += 1000000;
- v_usec = pps_offset - u_usec;
- if (v_usec < 0)
- v_usec = -v_usec;
- if (v_usec > (tick >> 1)) {
- if (pps_glitch > MAXGLITCH) {
- pps_glitch = 0;
- pps_tf[2] = u_usec;
- pps_tf[1] = u_usec;
- } else {
- pps_glitch++;
- u_usec = pps_offset;
- }
- } else
- pps_glitch = 0;
+ time_status |= STA_PPSSIGNAL | STA_PPSJITTER;
+ time_status &= ~(STA_PPSWANDER | STA_PPSERROR);
+ pps_valid = PPS_VALID;
+ u_sec = tsp->tv_sec;
+ u_nsec = tsp->tv_nsec;
+ if (u_nsec >= (NANOSECOND >> 1)) {
+ u_nsec -= NANOSECOND;
+ u_sec++;
+ }
+ v_nsec = u_nsec - pps_tf[0].nsec;
+ if (u_sec == pps_tf[0].sec && v_nsec < -MAXFREQ) {
+ return;
+ }
+ pps_tf[2] = pps_tf[1];
+ pps_tf[1] = pps_tf[0];
+ pps_tf[0].sec = u_sec;
+ pps_tf[0].nsec = u_nsec;
/*
- * A three-stage median filter is used to help deglitch the pps
+ * Compute the difference between the current and previous
+ * counter values. If the difference exceeds 0.5 s, assume it
+ * has wrapped around, so correct 1.0 s. If the result exceeds
+ * the tick interval, the sample point has crossed a tick
+ * boundary during the last second, so correct the tick. Very
+ * intricate.
+ */
+ u_nsec = nsec - pps_lastcount;
+ pps_lastcount = nsec;
+ if (u_nsec > (NANOSECOND >> 1))
+ u_nsec -= NANOSECOND;
+ else if (u_nsec < -(NANOSECOND >> 1))
+ u_nsec += NANOSECOND;
+ if (u_nsec > (time_tick >> 1))
+ u_nsec -= time_tick;
+ else if (u_nsec < -(time_tick >> 1))
+ u_nsec += time_tick;
+ pps_tf[0].count = pps_tf[1].count + u_nsec;
+ if (v_nsec > MAXFREQ) {
+ return;
+ }
+ time_status &= ~STA_PPSJITTER;
+
+ /*
+ * A three-stage median filter is used to help denoise the PPS
* time. The median sample becomes the time offset estimate; the
* difference between the other two samples becomes the time
* dispersion (jitter) estimate.
*/
- pps_tf[2] = pps_tf[1];
- pps_tf[1] = pps_tf[0];
- pps_tf[0] = u_usec;
- MEDIAN3(pps_tf, pps_offset, v_usec);
- if (v_usec > MAXTIME)
- pps_jitcnt++;
- v_usec = (v_usec << PPS_AVG) - pps_jitter;
- if (v_usec < 0)
- pps_jitter -= -v_usec >> PPS_AVG;
- else
- pps_jitter += v_usec >> PPS_AVG;
- if (pps_jitter > (MAXTIME >> 1))
- time_status |= STA_PPSJITTER;
+ if (pps_tf[0].nsec > pps_tf[1].nsec) {
+ if (pps_tf[1].nsec > pps_tf[2].nsec) {
+ pps_filt = pps_tf[1]; /* 0 1 2 */
+ u_nsec = pps_tf[0].nsec - pps_tf[2].nsec;
+ } else if (pps_tf[2].nsec > pps_tf[0].nsec) {
+ pps_filt = pps_tf[0]; /* 2 0 1 */
+ u_nsec = pps_tf[2].nsec - pps_tf[1].nsec;
+ } else {
+ pps_filt = pps_tf[2]; /* 0 2 1 */
+ u_nsec = pps_tf[0].nsec - pps_tf[1].nsec;
+ }
+ } else {
+ if (pps_tf[1].nsec < pps_tf[2].nsec) {
+ pps_filt = pps_tf[1]; /* 2 1 0 */
+ u_nsec = pps_tf[2].nsec - pps_tf[0].nsec;
+ } else if (pps_tf[2].nsec < pps_tf[0].nsec) {
+ pps_filt = pps_tf[0]; /* 1 0 2 */
+ u_nsec = pps_tf[1].nsec - pps_tf[2].nsec;
+ } else {
+ pps_filt = pps_tf[2]; /* 1 2 0 */
+ u_nsec = pps_tf[1].nsec - pps_tf[0].nsec;
+ }
+ }
/*
- * During the calibration interval adjust the starting time when
- * the tick overflows. At the end of the interval compute the
- * duration of the interval and the difference of the hardware
- * counters at the beginning and end of the interval. This code
- * is deliciously complicated by the fact valid differences may
- * exceed the value of tick when using long calibration
- * intervals and small ticks. Note that the counter can be
- * greater than tick if caught at just the wrong instant, but
- * the values returned and used here are correct.
+ * Nominal jitter is due to PPS signal noise and interrupt
+ * latency. If it exceeds the jitter limit, the sample is
+ * discarded. otherwise, if so enabled, the time offset is
+ * updated. The offsets are accumulated over the phase averaging
+ * interval to improve accuracy. The jitter is averaged only for
+ * performance monitoring. We can tolerate a modest loss of data
+ * here without degrading time accuracy.
*/
- bigtick = (long)tick << SHIFT_USEC;
- pps_usec -= pps_freq;
- if (pps_usec >= bigtick)
- pps_usec -= bigtick;
- if (pps_usec < 0)
- pps_usec += bigtick;
- pps_time.tv_sec++;
- pps_count++;
- if (pps_count < (1 << pps_shift))
- return;
- pps_count = 0;
- pps_calcnt++;
- u_usec = p_usec << SHIFT_USEC;
- v_usec = pps_usec - u_usec;
- if (v_usec >= bigtick >> 1)
- v_usec -= bigtick;
- if (v_usec < -(bigtick >> 1))
- v_usec += bigtick;
- if (v_usec < 0)
- v_usec = -(-v_usec >> pps_shift);
- else
- v_usec = v_usec >> pps_shift;
- pps_usec = u_usec;
- cal_sec = tvp->tv_sec;
- cal_usec = tvp->tv_usec;
- cal_sec -= pps_time.tv_sec;
- cal_usec -= pps_time.tv_usec;
- if (cal_usec < 0) {
- cal_usec += 1000000;
- cal_sec--;
+ if (u_nsec > MAXTIME) {
+ time_status |= STA_PPSJITTER;
+ pps_jitcnt++;
+ } else if (time_status & STA_PPSTIME) {
+ pps_offacc -= pps_filt.nsec;
+ pps_offcnt++;
}
- pps_time = *tvp;
+ if (pps_offcnt >= (1 << PPS_PAVG)) {
+ if (time_status & STA_PPSTIME) {
+ L_LINT(time_offset, pps_offacc);
+ L_RSHIFT(time_offset, PPS_PAVG);
+ }
+ pps_offacc = 0;
+ pps_offcnt = 0;
+
+ }
+ pps_jitter += (u_nsec - pps_jitter) >> PPS_FAVG;
+ u_sec = pps_tf[0].sec - pps_lastsec;
+ if (u_sec < (1 << pps_shift))
+ return;
/*
- * Check for lost interrupts, noise, excessive jitter and
- * excessive frequency error. The number of timer ticks during
- * the interval may vary +-1 tick. Add to this a margin of one
- * tick for the PPS signal jitter and maximum frequency
- * deviation. If the limits are exceeded, the calibration
- * interval is reset to the minimum and we start over.
+ * At the end of the calibration interval the difference between
+ * the first and last counter values becomes the scaled
+ * frequency. It will later be divided by the length of the
+ * interval to determine the frequency update. If the frequency
+ * exceeds a sanity threshold, or if the actual calibration
+ * interval is not equal to the expected length, the data are
+ * discarded. We can tolerate a modest loss of data here without
+ * degrading frequency ccuracy.
*/
- u_usec = (long)tick << 1;
- if (!((cal_sec == -1 && cal_usec > (1000000 - u_usec))
- || (cal_sec == 0 && cal_usec < u_usec))
- || v_usec > time_tolerance || v_usec < -time_tolerance) {
- pps_errcnt++;
- pps_shift = PPS_SHIFT;
- pps_intcnt = 0;
+ pps_calcnt++;
+ v_nsec = -pps_filt.count;
+ pps_lastsec = pps_tf[0].sec;
+ pps_tf[0].count = 0;
+ u_nsec = MAXFREQ << pps_shift;
+ if (v_nsec > u_nsec || v_nsec < -u_nsec || u_sec != (1 <<
+ pps_shift)) {
time_status |= STA_PPSERROR;
+ pps_errcnt++;
return;
}
/*
- * A three-stage median filter is used to help deglitch the pps
- * frequency. The median sample becomes the frequency offset
- * estimate; the difference between the other two samples
- * becomes the frequency dispersion (stability) estimate.
+ * If the actual calibration interval is not equal to the
+ * expected length, the data are discarded. If the wander is
+ * less than the wander threshold for four consecutive
+ * intervals, the interval is doubled; if it is greater than the
+ * threshold for four consecutive intervals, the interval is
+ * halved. The scaled frequency offset is converted to frequency
+ * offset. The stability metric is calculated as the average of
+ * recent frequency changes, but is used only for performance
+ * monitoring.
*/
- pps_ff[2] = pps_ff[1];
- pps_ff[1] = pps_ff[0];
- pps_ff[0] = v_usec;
- MEDIAN3(pps_ff, u_usec, v_usec);
-
- /*
- * Here the frequency dispersion (stability) is updated. If it
- * is less than one-fourth the maximum (MAXFREQ), the frequency
- * offset is updated as well, but clamped to the tolerance. It
- * will be processed later by the hardclock() routine.
- */
- v_usec = (v_usec >> 1) - pps_stabil;
- if (v_usec < 0)
- pps_stabil -= -v_usec >> PPS_AVG;
- else
- pps_stabil += v_usec >> PPS_AVG;
- if (pps_stabil > MAXFREQ >> 2) {
+ L_LINT(ftemp, v_nsec);
+ L_RSHIFT(ftemp, pps_shift);
+ L_SUB(ftemp, pps_freq);
+ u_nsec = L_GINT(ftemp);
+ if (u_nsec > MAXWANDER) {
+ L_LINT(ftemp, MAXWANDER);
+ pps_intcnt--;
+ time_status |= STA_PPSWANDER;
pps_stbcnt++;
+ } else if (u_nsec < -MAXWANDER) {
+ L_LINT(ftemp, -MAXWANDER);
+ pps_intcnt--;
time_status |= STA_PPSWANDER;
- return;
+ pps_stbcnt++;
+ } else {
+ pps_intcnt++;
}
- if (time_status & STA_PPSFREQ) {
- if (u_usec < 0) {
- pps_freq -= -u_usec >> PPS_AVG;
- if (pps_freq < -time_tolerance)
- pps_freq = -time_tolerance;
- u_usec = -u_usec;
- } else {
- pps_freq += u_usec >> PPS_AVG;
- if (pps_freq > time_tolerance)
- pps_freq = time_tolerance;
+ if (pps_intcnt >= 4) {
+ pps_intcnt = 4;
+ if (pps_shift < PPS_FAVGMAX) {
+ pps_shift++;
+ pps_intcnt = 0;
+ }
+ } else if (pps_intcnt <= -4) {
+ pps_intcnt = -4;
+ if (pps_shift > PPS_FAVG) {
+ pps_shift--;
+ pps_intcnt = 0;
}
}
+ if (u_nsec < 0)
+ u_nsec = -u_nsec;
+ pps_stabil += (u_nsec * SCALE_PPM - pps_stabil) >> PPS_FAVG;
/*
- * Here the calibration interval is adjusted. If the maximum
- * time difference is greater than tick / 4, reduce the interval
- * by half. If this is not the case for four consecutive
- * intervals, double the interval.
+ * The frequency offset is averaged into the PPS frequency. If
+ * enabled, the system clock frequency is updated as well.
*/
- if (u_usec << pps_shift > bigtick >> 2) {
- pps_intcnt = 0;
- if (pps_shift > PPS_SHIFT)
- pps_shift--;
- } else if (pps_intcnt >= 4) {
- pps_intcnt = 0;
- if (pps_shift < PPS_SHIFTMAX)
- pps_shift++;
- } else
- pps_intcnt++;
+ L_RSHIFT(ftemp, PPS_FAVG);
+ L_ADD(pps_freq, ftemp);
+ u_nsec = L_GINT(pps_freq);
+ if (u_nsec > MAXFREQ)
+ L_LINT(pps_freq, MAXFREQ);
+ else if (u_nsec < -MAXFREQ)
+ L_LINT(pps_freq, -MAXFREQ);
+ if (time_status & STA_PPSFREQ)
+ time_freq = pps_freq;
}
-
#endif /* PPS_SYNC */
int