diff options
Diffstat (limited to 'doc/rfc/rfc1589.txt')
-rw-r--r-- | doc/rfc/rfc1589.txt | 2075 |
1 files changed, 2075 insertions, 0 deletions
diff --git a/doc/rfc/rfc1589.txt b/doc/rfc/rfc1589.txt new file mode 100644 index 0000000..f5d45a1 --- /dev/null +++ b/doc/rfc/rfc1589.txt @@ -0,0 +1,2075 @@ + + + + + + +Network Working Group D. Mills +Request for Comments: 1589 University of Delaware +Category: Informational March 1994 + + + A Kernel Model for Precision Timekeeping + +Status of this Memo + + This memo provides information for the Internet community. This memo + does not specify an Internet standard of any kind. Distribution of + this memo is unlimited. + +Overview + + This memorandum describes an engineering model which implements a + precision time-of-day function for a generic operating system. The + model is based on the principles of disciplined oscillators and + phase-lock loops (PLL) often found in the engineering literature. It + has been implemented in the Unix kernel for several workstations, + including those made by Sun Microsystems and Digital Equipment. The + model changes the way the system clock is adjusted in time and + frequency, as well as provides mechanisms to discipline its frequency + to an external precision timing source. The model incorporates a + generic system-call interface for use with the Network Time Protocol + (NTP) or similar time synchronization protocol. The NTP Version 3 + daemon xntpd operates with this model to provide synchronization + limited in principle only by the accuracy and stability of the + external timing source. + + This memorandum does not obsolete or update any RFC. It does not + propose a standard protocol, specification or algorithm. It is + intended to provoke comment, refinement and alternative + implementations. While a working knowledge of NTP is not required for + an understanding of the design principles or implementation of the + model, it may be helpful in understanding how the model behaves in a + fully functional timekeeping system. The architecture and design of + NTP is described in [1], while the current NTP Version 3 protocol + specification is given in RFC-1305 [2] and a subset of the protocol, + the Simple Network Time Protocol (SNTP), in RFC-1361 [4]. + + The model has been implemented in three Unix kernels for Sun + Microsystems and Digital Equipment workstations. In addition, for the + Digital machines the model provides improved precision to one + microsecond (us). Since these specific implementations involve + modifications to licensed code, they cannot be provided directly. + Inquiries should be directed to the manufacturer's representatives. + However, the engineering model for these implementations, including a + + + +Mills [Page 1] + +RFC 1589 Kernel Model for Precision Timekeeping March 1994 + + + simulator with code segments almost identical to the implementations, + but not involving licensed code, is available via anonymous FTP from + host louie.udel.edu in the directory pub/ntp and compressed tar + archive kernel.tar.Z. The NTP Version 3 distribution can be obtained + via anonymous ftp from the same host and directory in the compressed + tar archive xntp3.3g.tar.Z, where the version number shown as 3.3g + may be adjusted for new versions as they occur. + +1. Introduction + + This memorandum describes a model and programming interface for + generic operating system software that manages the system clock and + timer functions. The model provides improved accuracy and stability + for most workstations and servers using the Network Time Protocol + (NTP) or similar time synchronization protocol. This memorandum + describes the principles of design and implementation of the model. + Related technical reports discuss the design approach, engineering + analysis and performance evaluation of the model as implemented in + Unix kernels for Sun Microsystems and Digital Equipment workstations. + The NTP Version 3 daemon xntpd operates with these implementations to + provide improved accuracy and stability, together with diminished + overhead in the operating system and network. In addition, the model + supports the use of external timing sources, such as precision + pulse-per-second (PPS) signals and the industry standard IRIG timing + signals. The NTP daemon automatically detects the presence of the new + features and utilizes them when available. + + There are three prototype implementations of the model presented in + this memorandum, one each for the Sun Microsystems SPARCstation with + the SunOS 4.1.x kernel, Digital Equipment DECstation 5000 with the + Ultrix 4.x kernel and Digital Equipment 3000 AXP Alpha with the OSF/1 + V1.x kernel. In addition, for the DECstation 5000/240 and 3000 AXP + Alpha machines, a special feature provides improved precision to 1 us + (Sun 4.1.x kernels already do provide 1-us precision). Other than + improving the system clock accuracy, stability and precision, these + implementations do not change the operation of existing Unix system + calls which manage the system clock, such as gettimeofday(), + settimeofday() and adjtime(); however, if the new features are in + use, the operations of gettimeofday() and adjtime() can be controlled + instead by new system calls ntp_gettime() and ntp_adjtime() as + described below. + + A detailed description of the variables and algorithms is given in + the hope that similar functionality can be incorporated in Unix + kernels for other machines. The algorithms involve only minor changes + to the system clock and interval timer routines and include + interfaces for application programs to learn the system clock status + and certain statistics of the time synchronization process. Detailed + + + +Mills [Page 2] + +RFC 1589 Kernel Model for Precision Timekeeping March 1994 + + + installation instructions are given in a specific README files + included in the kernel distributions. + + In this memorandum, NTP Version 3 and the Unix implementation xntp3 + are used as an example application of the new system calls for use by + a synchronization daemon. In principle, the new system calls can be + used by other protocols and implementations as well. Even in cases + where the local time is maintained by periodic exchanges of messages + at relatively long intervals, such as using the NIST Automated + Computer Time Service, the ability to precisely adjust the system + clock frequency simplifies the synchronization procedures and allows + the telephone call frequency to be considerably reduced. + +2. Design Approach + + While not strictly necessary for an understanding or implementation + of the model, it may be helpful to briefly describe how NTP operates + to control the system clock in a client workstation. As described in + [1], the NTP protocol exchanges timestamps with one or more peers + sharing a synchronization subnet to calculate the time offsets + between peer clocks and the local clock. These offsets are processed + by several algorithms which refine and combine the offsets to produce + an ensemble average, which is then used to adjust the local clock + time and frequency. The manner in which the local clock is adjusted + represents the main topic of this memorandum. The goal in the + enterprise is the most accurate and stable system clock possible with + the available kernel software and workstation hardware. + + In order to understand how the new software works, it is useful to + review how most Unix kernels maintain the system time. In the Unix + design a hardware counter interrupts the kernel at a fixed rate: 100 + Hz in the SunOS kernel, 256 Hz in the Ultrix kernel and 1024 Hz in + the OSF/1 kernel. Since the Ultrix timer interval (reciprocal of the + rate) does not evenly divide one second in microseconds, the Ultrix + kernel adds 64 microseconds once each second, so the timescale + consists of 255 advances of 3906 us plus one of 3970 us. Similarly, + the OSF/1 kernel adds 576 us once each second, so its timescale + consists of 1023 advances of 976 us plus one of 1552 us. + + 2.1. Mechanisms to Adjust Time and Frequency + + In most Unix kernels it is possible to slew the system clock to a + new offset relative to the current time by using the adjtime() + system call. To do this the clock frequency is changed by adding + or subtracting a fixed amount (tickadj) at each timer interrupt + (tick) for a calculated number of ticks. Since this calculation + involves dividing the requested offset by tickadj, it is possible + to slew to a new offset with a precision only of tickadj, which is + + + +Mills [Page 3] + +RFC 1589 Kernel Model for Precision Timekeeping March 1994 + + + usually in the neighborhood of 5 us, but sometimes much more. This + results in a roundoff error which can accumulate to an + unacceptable degree, so that special provisions must be made in + the clock adjustment procedures of the synchronization daemon. + + In order to implement a frequency-discipline function, it is + necessary to provide time offset adjustments to the kernel at + regular adjustment intervals using the adjtime() system call. In + order to reduce the system clock jitter to the regime considered + in this memorandum, it is necessary that the adjustment interval + be relatively small, in the neighborhood of 1 s. However, the Unix + adjtime() implementation requires each offset adjustment to + complete before another one can be begun, which means that large + adjustments must be amortized in possibly many adjustment + intervals. The requirement to implement the adjustment interval + and compensate for roundoff error considerably complicates the + synchronizing daemon implementation. + + In the new model this scheme is replaced by another that + represents the system clock as a multiple-word, precision-time + variable in order to provide very precise clock adjustments. At + each timer interrupt a precisely calibrated quantity is added to + the kernel time variable and overflows propagated as required. The + quantity is computed as in the NTP local clock model described in + [3], which operates as an adaptive-parameter, first-order, type-II + phase-lock loop (PLL). In principle, this PLL design can provide + precision control of the system clock oscillator within 1 us and + frequency to within parts in 10^11. While precisions of this order + are surely well beyond the capabilities of the CPU clock + oscillator used in typical workstations, they are appropriate + using precision external oscillators as described below. + + The PLL design is identical to the one originally implemented in + NTP and described in [3]. In this design the software daemon + simulates the PLL using the adjtime() system call; however, the + daemon implementation is considerably complicated by the + considerations described above. The modified kernel routines + implement the PLL in the kernel using precision time and frequency + representions, so that these complications are avoided. A new + system call ntp_adjtime() is called only as each new time update + is determined, which in NTP occurs at intervals of from 16 s to + 1024 s. In addition, doing frequency compensation in the kernel + means that the system time runs true even if the daemon were to + cease operation or the network paths to the primary + synchronization source fail. + + In the new model the new ntp_adjtime() operates in a way similar + to the original adjtime() system call, but does so independently + + + +Mills [Page 4] + +RFC 1589 Kernel Model for Precision Timekeeping March 1994 + + + of adjtime(), which continues to operate in its traditional + fashion. When used with NTP, it is the design intent that + settimeofday() or adjtime() be used only for system time + adjustments greater than +-128 ms, although the dynamic range of + the new model is much larger at +-512 ms. It has been the Internet + experience that the need to change the system time in increments + greater than +-128 ms is extremely rare and is usually associated + with a hardware or software malfunction or system reboot. + + The easiest way to set the time is with the settimeofday() system + call; however, this can under some conditions cause the clock to + jump backward. If this cannot be tolerated, adjtime() can be used + to slew the clock to the new value without running backward or + affecting the frequency discipline process. Once the system clock + has been set within +-128 ms, the ntp_adjtime() system call is + used to provide periodic updates including the time offset, + maximum error, estimated error and PLL time constant. With NTP the + update interval depends on the measured dispersion and time + constant; however, the scheme is quite forgiving and neither + moderate loss of updates nor variations in the update interval are + serious. + + 2.2 Daemon and Application Interface + + Unix application programs can read the system clock using the + gettimeofday() system call, which returns only the system time and + timezone data. For some applications it is useful to know the + maximum error of the reported time due to all causes, including + clock reading errors, oscillator frequency errors and accumulated + latencies on the path to a primary synchronization source. + However, in the new model the PLL adjusts the system clock to + compensate for its intrinsic frequency error, so that the time + errors expected in normal operation will usually be much less than + the maximum error. The programming interface includes a new system + call ntp_gettime(), which returns the system time, as well as the + maximum error and estimated error. This interface is intended to + support applications that need such things, including distributed + file systems, multimedia teleconferencing and other real-time + applications. The programming interface also includes the new + system call ntp_adjtime() mentioned previously, which can be used + to read and write kernel variables for time and frequency + adjustment, PLL time constant, leap-second warning and related + data. + + In addition, the kernel adjusts the maximum error to grow by an + amount equal to the oscillator frequency tolerance times the + elapsed time since the last update. The default engineering + parameters have been optimized for update intervals in the order + + + +Mills [Page 5] + +RFC 1589 Kernel Model for Precision Timekeeping March 1994 + + + of 64 s. For other intervals the PLL time constant can be adjusted + to optimize the dynamic response over intervals of 16-1024 s. + Normally, this is automatically done by NTP. In any case, if + updates are suspended, the PLL coasts at the frequency last + determined, which usually results in errors increasing only to a + few tens of milliseconds over a day using room-temperature quartz + oscillators of typical modern workstations. + + While any synchronization daemon can in principle be modified to + use the new system calls, the most likely will be users of the NTP + Version 3 daemon xntpd. The xntpd code determines whether the new + system calls are implemented and automatically reconfigures as + required. When implemented, the daemon reads the frequency offset + from a file and provides it and the initial time constant via + ntp_adjtime(). In subsequent calls to ntp_adjtime(), only the time + offset and time constant are affected. The daemon reads the + frequency from the kernel using ntp_adjtime() at intervals of + about one hour and writes it to a system file. This information is + recovered when the daemon is restarted after reboot, for example, + so the sometimes extensive training period to learn the frequency + separately for each system can be avoided. + + 2.3. Precision Clocks for DECstation 5000/240 and 3000 AXP Alpha + + The stock microtime() routine in the Ultrix kernel returns system + time to the precision of the timer interrupt interval, which is in + the 1-4 ms range. However, in the DECstation 5000/240 and possibly + other machines of that family, there is an undocumented IOASIC + hardware register that counts system bus cycles at a rate of 25 + MHz. The new microtime() routine for the Ultrix kernel uses this + register to interpolate system time between timer interrupts. This + results in a precision of 1 us for all time values obtained via + the gettimeofday() and ntp_gettime() system calls. For the Digital + Equipment 3000 AXP Alpha, the architecture provides a hardware + Process Cycle Counter and a machine instruction rpcc to read it. + This counter operates at the fundamental frequency of the CPU + clock or some submultiple of it, 133.333 MHz for the 3000/400 for + example. The new microtime() routine for the OSF/1 kernel uses + this counter in the same fashion as the Ultrix routine. + + In both the Ultrix and OSF/1 kernels the gettimeofday() and + ntp_gettime() system call use the new microtime() routine, which + returns the actual interpolated value, but does not change the + kernel time variable. Therefore, other routines that access the + kernel time variable directly and do not call either + gettimeofday(), ntp_gettime() or microtime() will continue their + present behavior. The microtime() feature is independent of other + features described here and is operative even if the kernel PLL or + + + +Mills [Page 6] + +RFC 1589 Kernel Model for Precision Timekeeping March 1994 + + + new system calls have not been implemented. + + The SunOS kernel already includes a system clock with 1-us + resolution; so, in principle, no microtime() routine is necessary. + An existing kernel routine uniqtime() implements this function, + but it is coded in the C language and is rather slow at 42-85 us + per call. A replacement microtime() routine coded in assembler + language is available in the NTP Version 3 distribution and is + much faster at about 3 us per call. + + 2.4. External Time and Frequency Discipline + + The overall accuracy of a time synchronization subnet with respect + to Coordinated Universal Time (UTC) depends on the accuracy and + stability of the primary synchronization source, usually a radio + or satellite receiver, and the system clock oscillator of the + primary server. As discussed in [5], the traditional interface + using an RS232 protocol and serial port precludes the full + accuracy of the radio clock. In addition, the poor stability of + typical CPU clock oscillators limits the accuracy, whether or not + precision time sources are available. There are, however, several + ways in which the system clock accuracy and stability can be + improved to the degree limited only by the accuracy and stability + of the synchronization source and the jitter of the operating + system. + + Many radio clocks produce special signals that can be used by + external equipment to precisely synchronize time and frequency. + Most produce a pulse-per-second (PPS) signal that can be read via + a modem-control lead of a serial port and some produce a special + IRIG signal that can be read directly by a bus peripheral, such as + the KSI/Odetics TPRO IRIG SBus interface, or indirectly via the + audio codec of some workstations, as described in [5]. In the NTP + Version 3 distribution, the PPS signal can be used to augment the + less precise ASCII serial timecode to improve accuracy to the + order of microseconds. Support is also included in the + distribution for the TPRO interface as well as the audio codec; + however, the latter requires a modified kernel audio driver + contained in the bsd_audio.tar.Z distribution in the same host and + directory as the NTP Version 3 distribution mentioned previously. + + 2.4.1. PPS Signal + + The NTP Version 3 distribution includes a special ppsclock + module for the SunOS 4.1.x kernel that captures the PPS signal + presented via a modem-control lead of a serial port. Normally, + the ppsclock module produces a timestamp at each transition of + the PPS signal and provides it to the synchronization daemon + + + +Mills [Page 7] + +RFC 1589 Kernel Model for Precision Timekeeping March 1994 + + + for integration with the serial ASCII timecode, also produced + by the radio clock. With the conventional PLL implementation in + either the daemon or the kernel as described above, the + accuracy of this scheme is limited by the intrinsic stability + of the CPU clock oscillator to a millisecond or two, depending + on environmental temperature variations. + + The ppsclock module has been modified to in addition call a new + kernel routine hardpps() once each second. The kernel routine + compares the timestamp with a sample of the CPU clock + oscillator to develop a frequency offset estimate. This offset + is used to discipline the oscillator frequency, nominally to + within a few parts in 10^8, which is about two orders of + magnitude better than the undisciplined oscillator. The new + feature is conditionally compiled in the code described below + only if the PPS_SYNC option is used in the kernel configuration + file. + + When using the PPS signal to adjust the time, there is a + problem with the SunOS implementation which is very delicate to + fix. The Sun serial port interrupt routine operates at + interrupt priority level 12, while the timer interrupt routine + operates at priority 10. Thus, it is possible that the PPS + signal interrupt can occur during the timer interrupt routine, + with result that a tick increment can be missed and the + returned time early by one tick. It may happen that, if the CPU + clock oscillator is within a few ppm of the PPS oscillator, + this condition can persist for two or more successive PPS + interrupts. A useful workaround has been to use a median filter + to process the PPS sample offsets. In this filter the sample + offsets in a window of 20 samples are sorted by offset and the + six highest and six lowest outlyers discarded. The average of + the eight samples remaining becomes the output of the filter. + + The problem is not nearly so serious when using the PPS signal + to discipline the frequency of the CPU clock oscillator. In + this case the quantity of interest is the contents of the + microseconds counter only, which does not depend on the kernel + time variable. + + 2.4.2. External Clocks + + It is possible to replace the system clock function with an + external bus peripheral. The TPRO device mentioned previously + can be used to provide IRIG-synchronized time with a precision + of 1 us. A driver for this device tprotime.c and header file + tpro.h are included in the kernel.tar.Z distribution mentioned + previously. Using this device the system clock is read directly + + + +Mills [Page 8] + +RFC 1589 Kernel Model for Precision Timekeeping March 1994 + + + from the interface; however, the device does not record the + year, so special provisions have to be made to obtain the year + from the kernel time variable and initialize the driver + accordingly. This feature is conditionally compiled in the code + described below only if the EXT_CLOCK option is used in the + kernel configuration file. + + While the system clock function is provided directly by the + microtime() routine in the driver, the kernel time variable + must be disciplined as well, since not all system timing + functions use the microtime() routine. This is done by + measuring the difference between the microtime() clock and + kernel time variable and using the difference to adjust the + kernel PLL as if the adjustment were provided by an external + peer and NTP. + + A good deal of error checking is done in the TPRO driver, since + the system clock is vulnerable to a misbehaving radio clock, + IRIG signal source, interface cables and TPRO device itself. + Unfortunately, there is no easy way to utilize the extensive + diversity and redundancy capabilities available in the NTP + synchronization daemon. In order to avoid disruptions that + might occur if the TPRO time is far different from the kernel + time variable, the latter is used instead of the former if the + difference between the two exceeds 1000 s; presumably in that + case operator intervention is required. + + 2.4.3. External Oscillators + + Even if a source of PPS or IRIG signals is not available, it is + still possible to improve the stability of the system clock + through the use of a specialized bus peripheral. In order to + explore the benefits of such an approach, a special SBus + peripheral caled HIGHBALL has been constructed. The device + includes a pair of 32-bit hardware counters in Unix timeval + format, together with a precision, oven-controlled quartz + oscillator with a stability of a few parts in 10^9. A driver + for this device hightime.c and header file high.h are included + in the kernel.tar.Z distribution mentioned previously. This + feature is conditionally compiled in the code described below + only if the EXT_CLOCK and HIGHBALL options are used in the + kernel configuration file. + + Unlike the external clock case, where the system clock function + is provided directly by the microtime() routine in the driver, + the HIGHBALL counter offsets with respect to UTC must be + provided first. This is done using the ordinary kernel PLL, + but controlling the counter offsets directly, rather than the + + + +Mills [Page 9] + +RFC 1589 Kernel Model for Precision Timekeeping March 1994 + + + kernel time variable. At first, this might seem to defeat the + purpose of the design, since the jitter and wander of the + synchronization source will affect the counter offsets and thus + the accuracy of the time. However, the jitter is much reduced + by the PLL and the wander is small, especially if using a radio + clock or another primary server disciplined in the same way. + In practice, the scheme works to reduce the incidental wander + to a few parts in 10^8, or about the same as using the PPS + signal. + + As in the previous case, the kernel time variable must be + disciplined as well, since not all system timing functions use + the microtime() routine. However, the kernel PLL cannot be used + for this, since it is already in use providing offsets for the + HIGHBALL counters. Therefore, a special correction is + calculated from the difference between the microtime() clock + and the kernel time variable and used to adjust the kernel time + variable at the next timer interrupt. This somewhat roundabout + approach is necessary in order that the adjustment does not + cause the kernel time variable to jump backwards and possibly + lose or duplicate a timer event. + + 2.5 Other Features + + It is a design feature of the NTP architecture that the system + clocks in a synchronization subnet are to read the same or nearly + the same values before during and after a leap-second event, as + declared by national standards bodies. The new model is designed + to implement the leap event upon command by an ntp_adjtime() + argument. The intricate and sometimes arcane details of the model + and implementation are discussed in [3] and [5]. Further details + are given in the technical summary later in this memorandum. + +3. Technical Summary + + In order to more fully understand the workings of the model, a stand- + alone simulator kern.c and header file timex.h are included in the + kernel.tar.Z distribution mentioned previously. In addition, a + complete C program kern_ntptime.c which implements the ntp_gettime() + and ntp_adjtime() functions is provided, but with the vendor-specific + argument-passing code deleted. Since the distribution is somewhat + large, due to copious comments and ornamentation, it is impractical + to include a listing of these programs in this memorandum. In any + case, implementors may choose to snip portions of the simulator for + use in new kernel designs, but, due to formatting conventions, this + would be difficult if included in this memorandum. + + + + + +Mills [Page 10] + +RFC 1589 Kernel Model for Precision Timekeeping March 1994 + + + The kern.c program is an implementation of an adaptive-parameter, + first-order, type-II phase-lock loop. The system clock is implemented + using a set of variables and algorithms defined in the simulator and + driven by explicit offsets generated by a driver program also + included in the program. The algorithms include code fragments almost + identical to those in the machine-specific kernel implementations and + operate in the same way, but the operations can be understood + separately from any licensed source code into which these fragments + may be integrated. The code fragments themselves are not derived from + any licensed code. The following discussion assumes that the + simulator code is available for inspection. + + 3.1. PLL Simulation + + The simulator operates in conformance with the analytical model + described in [3]. The main() program operates as a driver for the + fragments hardupdate(), hardclock(), second_overflow(), hardpps() + and microtime(), although not all functions implemented in these + fragments are simulated. The program simulates the PLL at each + timer interrupt and prints a summary of critical program variables + at each time update. + + There are three defined options in the kernel configuration file + specific to each implementation. The PPS_SYNC option provides + support for a pulse-per-second (PPS) signal, which is used to + discipline the frequency of the CPU clock oscillator. The + EXT_CLOCK option provides support for an external kernel-readable + clock, such as the KSI/Odetics TPRO IRIG interface or HIGHBALL + precision oscillator, both for the SBus. The TPRO option provides + support for the former, while the HIGHBALL option provides support + for the latter. External clocks are implemented as the microtime() + clock driver, with the specific source code selected by the kernel + configuration file. + + 3.1.1. The hardupdate() Fragment + + The hardupdate() fragment is called by ntp_adjtime() as each + update is computed to adjust the system clock phase and + frequency. Note that the time constant is in units of powers of + two, so that multiplies can be done by simple shifts. The phase + variable is computed as the offset divided by the time + constant. Then, the time since the last update is computed and + clamped to a maximum (for robustness) and to zero if + initializing. The offset is multiplied (sorry about the ugly + multiply) by the result and divided by the square of the time + constant and then added to the frequency variable. Note that + all shifts are assumed to be positive and that a shift of a + signed quantity to the right requires a little dance. + + + +Mills [Page 11] + +RFC 1589 Kernel Model for Precision Timekeeping March 1994 + + + With the defines given, the maximum time offset is determined + by the size in bits of the long type (32 or 64) less the + SHIFT_UPDATE scale factor (12) or at least 20 bits (signed). + The scale factor is chosen so that there is no loss of + significance in later steps, which may involve a right shift up + to SHIFT_UPDATE bits. This results in a time adjustment range + over +-512 ms. Since time_constant must be greater than or + equal to zero, the maximum frequency offset is determined by + the SHIFT_USEC scale factor (16) or at least 16 bits (signed). + This results in a frequency adjustment range over +-31,500 ppm. + + In the addition step, the value of offset * mtemp is not + greater than MAXPHASE * MAXSEC = 31 bits (signed), which will + not overflow a long add on a 32-bit machine. There could be a + loss of precision due to the right shift of up to 12 bits, + since time_constant is bounded at 6. This results in a net + worst-case frequency resolution of about .063 ppm, which is not + significant for most quartz oscillators. The worst case could + be realized only if the NTP peer misbehaves according to the + protocol specification. + + The time_offset value is clamped upon entry. The time_phase + variable is an accumulator, so is clamped to the tolerance on + every call. This helps to damp transients before the oscillator + frequency has been determined, as well as to satisfy the + correctness assertions if the time synchronization protocol or + implementation misbehaves. + + 3.1.2. The hardclock() Fragment + + The hardclock() fragment is inserted in the hardware timer + interrupt routine at the point the system clock is to be + incremented. Previous to this fragment the time_update variable + has been initialized to the value computed by the adjtime() + system call in the stock Unix kernel, normally plus/minus the + tickadj value, which is usually in the order of 5 us. The + time_phase variable, which represents the instantaneous phase + of the system clock, is advanced by time_adj, which is + calculated in the second_overflow() fragment described below. + If the value of time_phase exceeds 1 us in scaled units, + time_update is increased by the (signed) excess and time_phase + retains the residue. + + Except in the case of an external oscillator such as the + HIGHBALL interface, the hardclock() fragment advances the + system clock by the value of tick plus time_update. However, in + the case of an external oscillator, the system clock is + obtained directly from the interface and time_update used to + + + +Mills [Page 12] + +RFC 1589 Kernel Model for Precision Timekeeping March 1994 + + + discipline that interface instead. However, the system clock + must still be disciplined as explained previously, so the value + of clock_cpu computed by the second_overflow() fragment is used + instead. + + 3.1.3. The second_overflow() Fragment + + The second_overflow() fragment is inserted at the point where + the microseconds field of the system time variable is being + checked for overflow. Upon overflow the maximum error + time_maxerror is increased by time_tolerance to reflect the + maximum time offset due to oscillator frequency error. Then, + the increment time_adj to advance the kernel time variable is + calculated from the (scaled) time_offset and time_freq + variables updated at the last call to the hardclock() fragment. + + The phase adjustment is calculated as a (signed) fraction of + the time_offset remaining, where the fraction is added to + time_adj, then subtracted from time_offset. This technique + provides a rapid convergence when offsets are high, together + with good resolution when offsets are low. The frequency + adjustment is the sum of the (scaled) time_freq variable, an + adjustment necessary when the tick interval does not evenly + divide one second fixtick and PPS frequency adjustment pps_ybar + (if configured). + + The scheme of approximating exact multiply/divide operations + with shifts produces good results, except when an exact + calculation is required, such as when the PPS signal is being + used to discipling the CPU clock oscillator frequency, as + described below. As long as the actual oscillator frequency is + a power of two in seconds, no correction is required. However, + in the SunOS kernel the clock frequency is 100 Hz, which + results in an error factor of 0.78. In this case the code + increases time_adj by a factor of 1.25, which results in an + overall error less than three percent. + + On rollover of the day, the leap-second state machine described + below determines whether a second is to be inserted or deleted + in the timescale. The microtime() routine insures that the + reported time is always monotonically increasing. + + 3.1.4. The hardpps() Fragment + + The hardpps() fragment is operative only if the PPS_SYNC option + is specified in the kernel configuration file. It is called + from the serial port driver or equivalent interface at the on- + time transition of the PPS signal. The fragment operates as a + + + +Mills [Page 13] + +RFC 1589 Kernel Model for Precision Timekeeping March 1994 + + + first-order, type-I frequency-lock loop (FLL) controlled by the + difference between the frequency represented by the pps_ybar + variable and the frequency of the hardware clock oscillator. + + In order to avoid calling the microtime() routine more than + once for each PPS transition, the interface requires the + calling program to capture the system time and hardware counter + contents at the on-time transition of the PPS signal and + provide a pointer to the timestamp (Unix timeval) and counter + contents as arguments to the hardpps() call. The hardware + counter contents can be determined by saving the microseconds + field of the system time, calling the microtime() routine, and + subtracting the saved value. If a counter overflow has occured + during the process, the resulting microseconds value will be + negative, in which case the caller adds 1000000 to normalize + the microseconds field. + + The frequency of the hardware oscillator can be determined from + the difference in hardware counter readings at the beginning + and end of the calibration interval divided by the duration of + the interval. However, the oscillator frequency tolerance, as + much as 100 ppm, may cause the difference to exceed the tick + value, creating an ambiguity. In order to avoid this ambiguity, + the hardware counter value at the beginning of the interval is + increased by the current pps_ybar value once each second, but + computed modulo the tick value. At the end of the interval, the + difference between this value and the value computed from the + hardware counter is used as a control signal sample for the + FLL. + + Control signal samples which exceed the frequency tolerance are + discarded, as well as samples resulting from excessive interval + duration jitter. Surviving samples are then processed by a + three-stage median filter. The signal which drives the FLL is + derived from the median sample, while the average of + differences between the other two samples is used as a measure + of dispersion. If the dispersion is below the threshold + pps_dispmax, the median is used to correct the pps_ybar value + with a weight expressed as a shift PPS_AVG (2). In addition to + the averaging function, pps_disp is increased by the amount + pps_dispinc once each second. The result is that, should the + dispersion be exceptionally high, or if the PPS signal fails + for some reason, the pps_disp will eventually exceed + pps_dispmax and raise an alarm. + + Initially, an approximate value for pps_ybar is not known, so + the duration of the calibration interval must be kept small to + avoid overflowing the tick. The time difference at the end of + + + +Mills [Page 14] + +RFC 1589 Kernel Model for Precision Timekeeping March 1994 + + + the calibration interval is measured. If greater than a + fraction tick/4, the interval is reduced by half. If less than + this fraction for four successive calibration intervals, the + interval is doubled. This design automatically adapts to + nominal jitter in the PPS signal, as well as the value of tick. + The duration of the calibration interval is set by the + pps_shift variable as a shift in powers of two. The minimum + value PPS_SHIFT (2) is chosen so that with the highest CPU + oscillator frequency 1024 Hz and frequency tolerance 100 ppm + the tick will not overflow. The maximum value PPS_SHIFTMAX (8) + is chosen such that the maximum averaging time is about 1000 s + as determined by measurements of Allan variance [5]. + + Should the PPS signal fail, the current frequency estimate + pps_ybar continues to be used, so the nominal frequency remains + correct subject only to the instability of the undisciplined + oscillator. The procedure to save and restore the frequency + estimate works as follows. When setting the frequency from a + file, the time_freq value is set as the file value minus the + pps_ybar value; when retrieving the frequency, the two values + are added before saving in the file. This scheme provides a + seamless interface should the PPS signal fail or the kernel + configuration change. Note that the frequency discipline is + active whether or not the synchronization daemon is active. + Since all Unix systems take some time after reboot to build a + running system, usually by that time the discipline process has + already settled down and the initial transients due to + frequency discipline have damped out. + + 3.1.4. External Clock Interface + + The external clock driver interface is implemented with two + routines, microtime(), which returns the current clock time, + and clock_set(), which furnishes the apparent system time + derived from the kernel time variable. The latter routine is + called only when the clock is set using the settimeofday() + system call, but can be called from within the driver, such as + when the year rolls over, for example. + + In the stock SunOS kernel and modified Ultrix and OSF/1 + kernels, the microtime() routine returns the kernel time + variable plus an interpolation between timer interrupts based + on the contents of a hardware counter. In the case of an + external clock, such as described above, the system clock is + read directly from the hardware clock registers. Examples of + external clock drivers are in the tprotime.c and hightime.c + routines included in the kernel.tar.Z distribution. + + + + +Mills [Page 15] + +RFC 1589 Kernel Model for Precision Timekeeping March 1994 + + + The external clock routines return a status code which + indicates whether the clock is operating correctly and the + nature of the problem, if not. The return code is interpreted + by the ntp_gettime() system call, which transitions the status + state machine to the TIME_ERR state if an error code is + returned. This is the only error checking implemented for the + external clock in the present version of the code. + + The simulator has been used to check the PLL operation over the + design envelope of +-512 ms in time error and +-100 ppm in + frequency error. This confirms that no overflows occur and that + the loop initially converges in about 15 minutes for timer + interrupt rates from 50 Hz to 1024 Hz. The loop has a normal + overshoot of a few percent and a final convergence time of several + hours, depending on the initial time and frequency error. + + 3.2. Leap Seconds + + It does not seem generally useful in the user application + interface to provide additional details private to the kernel and + synchronization protocol, such as stratum, reference identifier, + reference timestamp and so forth. It would in principle be + possible for the application to independently evaluate the quality + of time and project into the future how long this time might be + "valid." However, to do that properly would duplicate the + functionality of the synchronization protocol and require + knowledge of many mundane details of the platform architecture, + such as the subnet configuration, reachability status and related + variables. For the curious, the ntp_adjtime() system call can be + used to reveal some of these mysteries. + + However, the user application may need to know whether a leap + second is scheduled, since this might affect interval calculations + spanning the event. A leap-warning condition is determined by the + synchronization protocol (if remotely synchronized), by the + timecode receiver (if available), or by the operator (if awake). + This condition is set by the synchronization daemon on the day the + leap second is to occur (30 June or 31 December, as announced) by + specifying in a ntp_adjtime() system call a clock status of either + TIME_DEL, if a second is to be deleted, or TIME_INS, if a second + is to be inserted. Note that, on all occasions since the inception + of the leap-second scheme, there has never been a deletion + occasion, nor is there likely to be one in future. If the value is + TIME_DEL, the kernel adds one second to the system time + immediately following second 23:59:58 and resets the clock status + to TIME_OK. If the value is TIME_INS, the kernel subtracts one + second from the system time immediately following second 23:59:59 + and resets the clock status to TIME_OOP, in effect causing system + + + +Mills [Page 16] + +RFC 1589 Kernel Model for Precision Timekeeping March 1994 + + + time to repeat second 59. Immediately following the repeated + second, the kernel resets the clock status to TIME_OK. + + Depending upon the system call implementation, the reported time + during a leap second may repeat (with the TIME_OOP return code set + to advertise that fact) or be monotonically adjusted until system + time "catches up" to reported time. With the latter scheme the + reported time will be correct before and shortly after the leap + second (depending on the number of microtime() calls during the + leap second), but freeze or slowly advance during the leap second + itself. However, Most programs will probably use the ctime() + library routine to convert from timeval (seconds, microseconds) + format to tm format (seconds, minutes,...). If this routine is + modified to use the ntp_gettime() system call and inspect the + return code, it could simply report the leap second as second 60. + + 3.3. Clock Status State Machine + + The various options possible with the system clock model described + in this memorandum require a careful examination of the state + transitions, status indications and recovery procedures should a + crucial signal or interface fail. In this section is presented a + prototype state machine designed to support leap second insertion + and deletion, as well as reveal various kinds of errors in the + synchronization process. The states of this machine are decoded as + follows: + + TIME_OK If an external clock is present, it is working properly + and the system clock is derived from it. If no external + clock is present, the synchronization daemon is working + properly and the system clock is synchronized to a radio + clock or one or more peers. + + TIME_INS An insertion of one second in the system clock has been + declared following the last second of the current day, + but has not yet been executed. + + TIME_DEL A deletion of the last second of the current day has + been declared, but not yet executed. + + TIME_OOP An insertion of one second in the system clock has been + declared following the last second of the current day. + The second is in progress, but not yet completed. + Library conversion routines should interpret this second + as 23:59:60. + + + + + + +Mills [Page 17] + +RFC 1589 Kernel Model for Precision Timekeeping March 1994 + + + TIME_BAD Either (a) the synchronization daemon has declared the + protocol is not working properly, (b) all sources of + outside synchronization have been lost or (c) an + external clock is present and it has just become + operational following a non-operational condition. + + TIME_ERR An external clock is present, but is in a non- + operational condition. + + In all except the TIME_ERR state the system clock is derived from + either an external clock, if present, or the kernel time variable, + if not. In the TIME_ERR state the external clock is present, but + not working properly, so the system clock may be derived from the + kernel time variable. The following diagram indicates the normal + transitions of the state machine. Not all valid transitions are + shown. + + +--------+ +--------+ +--------+ +--------+ + | | | | | | | | + |TIME_BAD|---->|TIME_OK |<----|TIME_OOP|<----|TIME_INS| + | | | | | | | | + +--------+ +--------+ +--------+ +--------+ + A A + | | + | | + +--------+ +--------+ + | | | | + |TIME_ERR| |TIME_DEL| + | | | | + +--------+ +--------+ + + The state machine makes a transition once each second at an + instant where the microseconds field of the kernel time variable + overflows and one second is added to the seconds field. However, + this condition is checked at each timer interrupt, which may not + exactly coincide with the actual instant of overflow. This may + lead to some interesting anomalies, such as a status indication of + a leap second in progress (TIME_OOP) when actually the leap second + had already expired. + + The following state transitions are executed automatically by the + kernel: + + any state -> TIME_ERR This transition occurs when an external + clock is present and an attempt is made to + read it when in a non-operational + condition. + + + + +Mills [Page 18] + +RFC 1589 Kernel Model for Precision Timekeeping March 1994 + + + TIME_INS -> TIME_OOP This transition occurs immediately + following second 86,400 of the current day + when an insert-second event has been + declared. + + TIME_OOP -> TIME_OK This transition occurs immediately + following second 86,401 of the current + day; that is, one second after entry to + the TIME_OOP state. + + TIME_DEL -> TIME_OK This transition occurs immediately + following second 86,399 of the current day + when a delete-second event has been + declared. + + The following state transitions are executed by specific + ntp_adjtime() system calls: + + TIME_OK -> TIME_INS This transition occurs as the result of a + ntp_adjtime() system call to declare an + insert-second event. + + TIME_OK -> TIME_DEL This transition occurs as the result of a + ntp_adjtime() system call to declare a + delete-second event. + + any state -> TIME_BAD This transition occurs as the result of a + ntp_adjtime() system call to declare loss + of all sources of synchronization or in + other cases of error. + + The following table summarizes the actions just before, during and + just after a leap-second event. Each line in the table shows the + UTC and NTP times at the beginning of the second. The left column + shows the behavior when no leap event is to occur. In the middle + column the state machine is in TIME_INS at the end of UTC second + 23:59:59 and the NTP time has just reached 400. The NTP time is + set back one second to 399 and the machine enters TIME_OOP. At the + end of the repeated second the machine enters TIME_OK and the UTC + and NTP times are again in correspondence. In the right column the + state machine is in TIME_DEL at the end of UTC second 23:59:58 and + the NTP time has just reached 399. The NTP time is incremented, + the machine enters TIME_OK and both UTC and NTP times are again in + correspondence. + + + + + + + +Mills [Page 19] + +RFC 1589 Kernel Model for Precision Timekeeping March 1994 + + + No Leap Leap Insert Leap Delete + UTC NTP UTC NTP UTC NTP + --------------------------------------------- + 23:59:58|398 23:59:58|398 23:59:58|398 + | | | + 23:59:59|399 23:59:59|399 00:00:00|400 + | | | + 00:00:00|400 23:59:60|399 00:00:01|401 + | | | + 00:00:01|401 00:00:00|400 00:00:02|402 + | | | + 00:00:02|402 00:00:01|401 00:00:03|403 + | | | + + To determine local midnight without fuss, the kernel code simply + finds the residue of the time.tv_sec (or time.tv_sec + 1) value + mod 86,400, but this requires a messy divide. Probably a better + way to do this is to initialize an auxiliary counter in the + settimeofday() routine using an ugly divide and increment the + counter at the same time the time.tv_sec is incremented in the + timer interrupt routine. For future embellishment. + +4. Programming Model and Interfaces + + This section describes the programming model for the synchronization + daemon and user application programs. The ideas are based on + suggestions from Jeff Mogul and Philip Gladstone and a similar + interface designed by the latter. It is important to point out that + the functionality of the original Unix adjtime() system call is + preserved, so that the modified kernel will work as the unmodified + one, should the new features not be in use. In this case the + ntp_adjtime() system call can still be used to read and write kernel + variables that might be used by a synchronization daemon other than + NTP, for example. + + 4.1. The ntp_gettime() System Call + + The syntax and semantics of the ntp_gettime() call are given in + the following fragment of the timex.h header file. This file is + identical, except for the SHIFT_HZ define, in the SunOS, Ultrix + and OSF/1 kernel distributions. (The SHIFT_HZ define represents + the logarithm to the base 2 of the clock oscillator frequency + specific to each system type.) Note that the timex.h file calls + the syscall.h system header file, which must be modified to define + the SYS_ntp_gettime system call specific to each system type. The + kernel distributions include directions on how to do this. + + + + + +Mills [Page 20] + +RFC 1589 Kernel Model for Precision Timekeeping March 1994 + + + /* + * This header file defines the Network Time Protocol (NTP) + * interfaces for user and daemon application programs. These are + * implemented using private system calls and data structures and + * require specific kernel support. + * + * NAME + * ntp_gettime - NTP user application interface + * + * SYNOPSIS + * #include <sys/timex.h> + * + * int system call(SYS_ntp_gettime, tptr) + * + * int SYS_ntp_gettime defined in syscall.h header file + * struct ntptimeval *tptr pointer to ntptimeval structure + * + * NTP user interface - used to read kernel clock values + * Note: maximum error = NTP synch distance = dispersion + delay / + * 2 + * estimated error = NTP dispersion. + */ + struct ntptimeval { + struct timeval time; /* current time */ + long maxerror; /* maximum error (us) */ + long esterror; /* estimated error (us) */ + }; + + The ntp_gettime() system call returns three values in the + ntptimeval structure: the current time in unix timeval format plus + the maximum and estimated errors in microseconds. While the 32-bit + long data type limits the error quantities to something more than + an hour, in practice this is not significant, since the protocol + itself will declare an unsynchronized condition well below that + limit. In the NTP Version 3 specification, if the protocol + computes either of these values in excess of 16 seconds, they are + clamped to that value and the system clock declared + unsynchronized. + + Following is a detailed description of the ntptimeval structure + members. + + + + + + + + + + +Mills [Page 21] + +RFC 1589 Kernel Model for Precision Timekeeping March 1994 + + + struct timeval time; /* current time */ + + This member returns the current system time, expressed as a + Unix timeval structure. The timeval structure consists of two + 32-bit words; the first returns the number of seconds past 1 + January 1970, while the second returns the number of + microseconds. + + long maxerror; /* maximum error (us) */ + + This member returns the time_maxerror kernel variable in + microseconds. See the entry for this variable in section 5 for + additional information. + + long esterror; /* estimated error (us) */ + + This member returns the time_esterror kernel variable in + microseconds. See the entry for this variable in section 5 for + additional information. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Mills [Page 22] + +RFC 1589 Kernel Model for Precision Timekeeping March 1994 + + + 4.2. The ntp_adjtime() System Call + + The syntax and semantics of the ntp_adjtime() call are given in + the following fragment of the timex.h header file. Note that, as + in the ntp_gettime() system call, the syscall.h system header file + must be modified to define the SYS_ntp_adjtime system call + specific to each system type. + + /* + * NAME + * ntp_adjtime - NTP daemon application interface + * + * SYNOPSIS + * #include <sys/timex.h> + * + * int system call(SYS_ntp_adjtime, mode, tptr) + * + * int SYS_ntp_adjtime defined in syscall.h header file + * struct timex *tptr pointer to timex structure + * + * NTP daemon interface - used to discipline kernel clock + * oscillator + */ + struct timex { + int mode; /* mode selector */ + long offset; /* time offset (us) */ + long frequency; /* frequency offset (scaled ppm) */ + long maxerror; /* maximum error (us) */ + long esterror; /* estimated error (us) */ + int status; /* clock command/status */ + long time_constant; /* pll time constant */ + long precision; /* clock precision (us) (read only) + */ + long tolerance; /* clock frequency tolerance (scaled + * ppm) (read only) */ + /* + * The following read-only structure members are implemented + * only if the PPS signal discipline is configured in the + * kernel. + */ + long ybar; /* frequency estimate (scaled ppm) */ + long disp; /* dispersion estimate (scaled ppm) + */ + int shift; /* interval duration (s) (shift) */ + long calcnt; /* calibration intervals */ + long jitcnt; /* jitter limit exceeded */ + long discnt; /* dispersion limit exceeded */ + }; + + + +Mills [Page 23] + +RFC 1589 Kernel Model for Precision Timekeeping March 1994 + + + The ntp_adjtime() system call is used to read and write certain + time-related kernel variables summarized in this and subsequent + sections. Writing these variables can only be done in superuser + mode. To write a variable, the mode structure member is set with + one or more bits, one of which is assigned each of the following + variables in turn. The current values for all variables are + returned in any case; therefore, a mode argument of zero means to + return these values without changing anything. + + Following is a description of the timex structure members. + + int mode; /* mode selector */ + + This is a bit-coded variable selecting one or more structure + members, with one bit assigned each member. If a bit is set, + the value of the associated member variable is copied to the + corresponding kernel variable; if not, the member is ignored. + The bits are assigned as given in the following fragment of the + timex.h header file. Note that the precision and tolerance are + determined by the kernel and cannot be changed by + ntp_adjtime(). + + /* + * Mode codes (timex.mode) + */ + #define ADJ_OFFSET 0x0001 /* time offset */ + #define ADJ_FREQUENCY 0x0002 /* frequency offset */ + #define ADJ_MAXERROR 0x0004 /* maximum time error */ + #define ADJ_ESTERROR 0x0008 /* estimated time error */ + #define ADJ_STATUS 0x0010 /* clock status */ + #define ADJ_TIMECONST 0x0020 /* pll time constant */ + + long offset; /* time offset (us) */ + + If selected, this member replaces the value of the time_offset + kernel variable in microseconds. The absolute value must be + less than MAXPHASE microseconds defined in the timex.h header + file. See the entry for this variable in section 5 for + additional information. + + If within range and the PPS signal and/or external oscillator + are configured and operating properly, the clock status is + automatically set to TIME_OK. + + + + + + + + +Mills [Page 24] + +RFC 1589 Kernel Model for Precision Timekeeping March 1994 + + + long time_constant; /* pll time constant */ + + If selected, this member replaces the value of the + time_constant kernel variable. The value must be between zero + and MAXTC defined in the timex.h header file. See the entry for + this variable in section 5 for additional information. + + long frequency; /* frequency offset (scaled ppm) */ + + If selected, this member replaces the value of the + time_frequency kernel variable. The value is in ppm, with the + integer part in the high order 16 bits and fraction in the low + order 16 bits. The absolute value must be in the range less + than MAXFREQ ppm defined in the timex.h header file. See the + entry for this variable in section 5 for additional + information. + + long maxerror; /* maximum error (us) */ + + If selected, this member replaces the value of the + time_maxerror kernel variable in microseconds. See the entry + for this variable in section 5 for additional information. + + long esterror; /* estimated error (us) */ + + If selected, this member replaces the value of the + time_esterror kernel variable in microseconds. See the entry + for this variable in section 5 for additional information. + + int status; /* clock command/status */ + + If selected, this member replaces the value of the time_status + kernel variable. See the entry for this variable in section 5 + for additional information. + + In order to set this variable by ntp_adjtime(), either (a) the + current clock status must be TIME_OK or (b) the member value is + TIME_BAD; that is, the ntp_adjtime() call can always set the + clock to the unsynchronized state or, if the clock is running + correctly, can set it to any state. In any case, the + ntp_adjtime() call always returns the current state in this + member, so the caller can determine whether or not the request + succeeded. + + + + + + + + +Mills [Page 25] + +RFC 1589 Kernel Model for Precision Timekeeping March 1994 + + + long time_constant; /* pll time constant */ + + If selected, this member replaces the value of the + time_constant kernel variable. The value must be between zero + and MAXTC defined in the timex.h header file. See the entry for + this variable in section 5 for additional information. + + long precision; /* clock precision (us) (read only) */ + + This member returns the time_precision kernel variable in + microseconds. The variable can be written only by the kernel. + See the entry for this variable in section 5 for additional + information. + + long tolerance; /* clock frequency tolerance (scaled ppm) + */ + + This member returns the time_tolerance kernel variable in + microseconds. The variable can be written only by the kernel. + See the entry for this variable in section 5 for additional + information. + + long ybar; /* frequency estimate (scaled ppm) */ + + This member returns the pps_ybar kernel variable in + microseconds. The variable can be written only by the kernel. + See the entry for this variable in section 5 for additional + information. + + long disp; /* dispersion estimate (scaled ppm) */ + + This member returns the pps_disp kernel variable in + microseconds. The variable can be written only by the kernel. + See the entry for this variable in section 5 for additional + information. + + int shift; /* interval duration (s) (shift) */ + + This member returns the pps_shift kernel variable in + microseconds. The variable can be written only by the kernel. + See the entry for this variable in section 5 for additional + information. + + + + + + + + + +Mills [Page 26] + +RFC 1589 Kernel Model for Precision Timekeeping March 1994 + + + long calcnt; /* calibration intervals */ + + This member returns the pps_calcnt kernel variable in + microseconds. The variable can be written only by the kernel. + See the entry for this variable in section 5 for additional + information. + + long jitcnt; /* jitter limit exceeded */ + + This member returns the pps_jittcnt kernel variable in + microseconds. The variable can be written only by the kernel. + See the entry for this variable in section 5 for additional + information. + + long discnt; /* dispersion limit exceeded */ + + This member returns the pps_discnt kernel variable in + microseconds. The variable can be written only by the kernel. + See the entry for this variable in section 5 for additional + information. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Mills [Page 27] + +RFC 1589 Kernel Model for Precision Timekeeping March 1994 + + + 4.3. Command/Status Codes + + The kernel routines use the system clock status variable + time_status, which records whether the clock is synchronized, + waiting for a leap second, etc. The value of this variable is + returned as the result code by both the ntp_gettime() and + ntp_adjtime() system calls. In addition, it can be explicitly read + and written using the ntp_adjtime() system call, but can be + written only in superuser mode. Values presently defined in the + timex.h header file are as follows: + + /* + * Clock command/status codes (timex.status) + */ + #define TIME_OK 0 /* clock synchronized */ + #define TIME_INS 1 /* insert leap second */ + #define TIME_DEL 2 /* delete leap second */ + #define TIME_OOP 3 /* leap second in progress */ + #define TIME_BAD 4 /* kernel clock not synchronized */ + #define TIME_ERR 5 /* external oscillator not + synchronized */ + + A detailed description of these codes as used by the leap-second + state machine is given later in this memorandum. In case of a + negative result code, the kernel has intercepted an invalid + address or (in case of the ntp_adjtime() system call), a superuser + violation. + +5. Kernel Variables + + This section contains a list of kernel variables and a detailed + description of their function, initial value, scaling and limits. + + 5.1. Interface Variables + + The following variables are read and set by the ntp_adjtime() + system call. Additional automatic variables are used as + temporaries as described in the code fragments. + + int time_status = TIME_BAD; + + This variable controls the state machine used to insert or + delete leap seconds and show the status of the timekeeping + system, PPS signal and external oscillator, if configured. + + + + + + + +Mills [Page 28] + +RFC 1589 Kernel Model for Precision Timekeeping March 1994 + + + long time_offset = 0; + + This variable is used by the PLL to adjust the system time in + small increments. It is scaled by (1 << SHIFT_UPDATE) (12) in + microseconds. The maximum value that can be represented is + about +-512 ms and the minimum value or precision is a few + parts in 10^10 s. + + long time_constant = 0; /* pll time constant */ + + This variable determines the bandwidth or "stiffness" of the + PLL. The value is used as a shift between zero and MAXTC (6), + with the effective PLL time constant equal to a multiple of (1 + << time_constant) in seconds. For room-temperature quartz + oscillator the recommended default value is 2, which + corresponds to a PLL time constant of about 900 s and a maximum + update interval of about 64 s. The maximum update interval + scales directly with the time constant, so that at the maximum + time constant of 6, the update interval can be as large as 1024 + s. + + Values of time_constant between zero and 2 can be used if quick + convergence is necessary; values between 2 and 6 can be used to + reduce network load, but at a modest cost in accuracy. Values + above 6 are appropriate only if an external oscillator is + present. + + long time_tolerance = MAXFREQ; /* frequency tolerance (ppm) */ + + This variable represents the maximum frequency error or + tolerance in ppm of the particular 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. It is expressed as a positive number + greater than zero in parts-per-million (ppm). + + The recommended value of MAXFREQ is 200 ppm is appropriate for + room-temperature quartz oscillators used in typical + workstations. However, it can change due to the operating + condition of the PPS signal and/or external oscillator. With + either the PPS signal or external oscillator, the recommended + value for MAXFREQ is 100 ppm. + + + + + + + + + +Mills [Page 29] + +RFC 1589 Kernel Model for Precision Timekeeping March 1994 + + + long time_precision = 1000000 / HZ; /* clock precision (us) */ + + This variable represents the maximum error in reading the + system clock in microseconds. It is usually based on the number + of microseconds between timer interrupts, 10000 us for the + SunOS kernel, 3906 us for the Ultrix kernel, 976 us for the + OSF/1 kernel. However, in cases where the time can be + interpolated between timer interrupts with microsecond + resolution, such as in the unmodified SunOS kernel and modified + Ultrix and OSF/1 kernels, the precision is specified as 1 us. + In cases where a PPS signal or external oscillator is + available, the precision can depend on the operating condition + of the signal or oscillator. This variable is determined by the + kernel for use by the synchronization daemon, but is otherwise + not used by the kernel. + + long time_maxerror = MAXPHASE; /* maximum error */ + + This variable establishes the maximum error of the indicated + time relative to the primary synchronization source in + microseconds. For NTP, the value is initialized by a + ntp_adjtime() call to the synchronization distance, which is + equal to the root dispersion plus one-half the root delay. It + is increased by a small amount (time_tolerance) each second to + reflect the clock frequency tolerance. This variable is + computed by the synchronization daemon and the kernel, but is + otherwise not used by the kernel. + + long time_esterror = MAXPHASE; /* estimated error */ + + This variable establishes the expected error of the indicated + time relative to the primary synchronization source in + microseconds. For NTP, the value is determined as the root + dispersion, which represents the best estimate of the actual + error of the system clock based on its past behavior, together + with observations of multiple clocks within the peer group. + This variable is computed by the synchronization daemon and + returned in system calls, but is otherwise not used by the + kernel. + + + + + + + + + + + + +Mills [Page 30] + +RFC 1589 Kernel Model for Precision Timekeeping March 1994 + + + 5.2. Phase-Lock Loop Variables + + The following variables establish the state of the PLL and the + residual time and frequency offset of the system clock. Additional + automatic variables are used as temporaries as described in the + code fragments. + + long time_phase = 0; /* phase offset (scaled us) */ + + The time_phase variable represents the phase of the kernel time + variable at each tick of the clock. This variable is scaled by + (1 << SHIFT_SCALE) (23) in microseconds, giving a maximum + adjustment of about +-256 us/tick and a resolution less than + one part in 10^12. + + long time_offset = 0; /* time offset (scaled us) */ + + The time_offset variable represents the time offset of the CPU + clock oscillator. It is recalculated as each update to the + system clock is received via the hardupdate() routine and at + each second in the seconds_overflow routine. This variable is + scaled by (1 << SHIFT_UPDATE) (12) in microseconds, giving a + maximum adjustment of about +-512 ms and a resolution of a few + parts in 10^10 s. + + long time_freq = 0; /* frequency offset (scaled ppm) */ + + The time_freq variable represents the frequency offset of the + CPU clock oscillator. It is recalculated as each update to the + system clock is received via the hardupdate() routine. It can + also be set via ntp_adjtime() from a value stored in a file + when the synchronization daemon is first started. It can be + retrieved via ntp_adjtime() and written to the file about once + per hour by the daemon. The time_freq variable is scaled by (1 + << SHIFT_KF) (16) ppm, giving it a maximum value well in excess + of the limit of +-256 ppm imposed by other constraints. The + precision of this representation (frequency resolution) is + parts in 10^11, which is adequate for all but the best external + oscillators. + + time_adj = 0; /* tick adjust (scaled 1 / HZ) */ + + The time_adj variable is the adjustment added to the value of + tick at each timer interrupt. It is computed once each second + from the time_offset, time_freq and, if the PPS signal is + present, the ps_ybar variable once each second. + + + + + +Mills [Page 31] + +RFC 1589 Kernel Model for Precision Timekeeping March 1994 + + + long time_reftime = 0; /* time at last adjustment (s) */ + + This variable is the seconds portion of the system time on the + last update received by the hardupdate() routine. It is used to + compute the time_freq variable as the time since the last + update increases. + + int fixtick = 1000000 % HZ; /* amortization factor */ + + In the Ultrix and OSF/1 kernels, the interval between timer + interrupts does not evenly divide the number of microseconds in + the second. In order that the clock runs at a precise rate, it + is necessary to introduce an amortization factor into the local + timescale. In the original Unix code, the value of fixtick is + amortized once each second, introducing an additional source of + jitter; in the new model the value is amortized at each tick of + the system clock, reducing the jitter by the reciprocal of the + clock oscillator frequency. This is not a new kernel variable, + but a new use of an existing kernel variable. + + 5.3. Pulse-per-second (PPS) Frequency-Lock Loop Variables + + The following variables are used only if a pulse-per-second (PPS) + signal is available and connected via a modem-control lead, such + as produced by the optional ppsclock feature incorporated in the + serial port driver. They establish the design parameters of the + PPS frequency-lock loop used to discipline the CPU clock + oscillator to an external PPS signal. Additional automatic + variables are used as temporaries as described in the code + fragments. + + long pps_usec; /* microseconds at last pps */ + + The pps_usec variable is latched from a high resolution counter + or external oscillator at each PPS interrupt. In determining + this value, only the hardware counter contents are used, not + the contents plus the kernel time variable, as returned by the + microtime() routine. + + long pps_ybar = 0; /* pps frequency offset estimate */ + + The pps_ybar variable is the average CPU clock oscillator + frequency offset relative to the PPS disciplining signal. It is + scaled in the same units as the time_freq variable. + + + + + + + +Mills [Page 32] + +RFC 1589 Kernel Model for Precision Timekeeping March 1994 + + + pps_disp = MAXFREQ; /* dispersion estimate (scaled ppm) */ + + The pps_disp variable represents the average sample dispersion + measured over the last three samples. It is scaled in the same + units as the time_freq variable. + + pps_dispmax = MAXFREQ / 2; /* dispersion threshold */ + + The pps_dispmax variable is used as a dispersion threshold. If + pps_disp is less than this threshold, the median sample is used + to update the pps_ybar estimate; if not, the sample is + discarded. + + pps_dispinc = MAXFREQ >> (PPS_SHIFT + 4); /* pps dispersion + increment/sec */ + + The pps_dispinc variable is the increment to add to pps_disp + once each second. It is computed such that, if no PPS samples + have arrived for several calibration intervals, the value of + pps_disp will exceed the pps_dispmax threshold and raise an + alarm. + + int pps_mf[] = {0, 0, 0}; /* pps median filter */ + + The pps-mf[] array is used as a median filter to detect and + discard jitter in the PPS signal. + + int pps_count = 0; /* pps calibrate interval counter */ + + The pps_count variable measures the length of the calibration + interval used to calculate the frequency. It normally counts + from zero to the value 1 << pps_shift. + + pps_shift = PPS_SHIFT; /* interval duration (s) (shift) */ + + The pps_shift variable determines the duration of the + calibration interval, 1 << pps_shift s. + + pps_intcnt = 0; /* intervals at current duration */ + + The pps_intcnt variable counts the number of calibration + intervals at the current interval duration. It is reset to zero + after four intervals and when the interval duration is changed. + + long pps_calcnt = 0; /* calibration intervals */ + + The pps_calcnt variable counts the number of calibration + intervals. + + + +Mills [Page 33] + +RFC 1589 Kernel Model for Precision Timekeeping March 1994 + + + long pps_jitcnt = 0; /* jitter limit exceeded */ + + The pps_jitcnt variable counts the number of resets due to + excessive jitter or frequency offset. These resets are + usually due to excessive noise in the PPS signal or + interface. + + long pps_discnt = 0; /* dispersion limit exceeded */ + + The pps_discnt variable counts the number of calibration + intervals where the dispersion is above the pps_dispmax + limit. These resets are usually due to excessive frequency + wander in the PPS signal source. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Mills [Page 34] + +RFC 1589 Kernel Model for Precision Timekeeping March 1994 + + + 5.4. External Oscillator Variables + + The following variables are used only if an external oscillator + (HIGHBALL or TPRO) is present. Additional automatic variables are + used as temporaries as described in the code fragments. + + int clock_count = 0; /* CPU clock counter */ + + The clock_count variable counts the seconds between adjustments + to the kernel time variable to discipline it to the external + clock. + + struct timeval clock_offset; /* HIGHBALL clock offset */ + + The clock_offset variable defines the offset between system + time and the HIGHBALL counters. + + long clock_cpu = 0; /* CPU clock adjust */ + + The clock_cpu variable contains the offset between the system + clock and the HIGHBALL clock for use in disciplining the kernel + time variable. + +6. Architecture Constants + + Following is a list of the important architecture constants that + establish the response and stability of the PLL and provide maximum + bounds on behavior in order to satisfy correctness assertions made in + the protocol specification. Additional definitions are given in the + timex.h header file. + + 6.1. Phase-lock loop (PLL) definitions + + The following defines establish the performance envelope of the + PLL. They establish the maximum phase error (MAXPHASE), maximum + frequency error (MAXFREQ), minimum interval between updates + (MINSEC) and maximum interval between updates (MAXSEC). The intent + of these bounds is to force the PLL to operate within predefined + limits in order to satisfy correctness assertions of the + synchronization protocol. An excursion which exceeds these bounds + is clamped to the bound and operation proceeds normally. In + practice, this can occur only if something has failed or is + operating out of tolerance, but otherwise the PLL continues to + operate in a stable mode. + + MAXPHASE must be set greater than or equal to CLOCK.MAX (128 ms), + as defined in the NTP specification. CLOCK.MAX establishes the + maximum time offset allowed before the system time is reset, + + + +Mills [Page 35] + +RFC 1589 Kernel Model for Precision Timekeeping March 1994 + + + rather than incrementally adjusted. Here, the maximum offset is + clamped to MAXPHASE only in order to prevent overflow errors due + to defective programming. + + MAXFREQ reflects the manufacturing frequency tolerance of the CPU + oscillator plus the maximum slew rate allowed by the protocol. It + should be set to at least the intrinsic frequency tolerance of the + oscillator plus 100 ppm for vernier frequency adjustments. If the + kernel frequency discipline code is installed (PPS_SYNC), the CPU + oscillator frequency is disciplined to an external source, + presumably with negligible frequency error. + + #define MAXPHASE 512000 /* max phase error (us) */ + #ifdef PPS_SYNC + #define MAXFREQ 100 /* max frequency error (ppm) */ + #else + #define MAXFREQ 200 /* max frequency error (ppm) */ + #endif /* PPS_SYNC */ + #define MINSEC 16 /* min interval between updates (s) + */ + #define MAXSEC 1200 /* max interval between updates (s) + */ + + 6.2. Pulse-per-second (PPS) Frequency-lock Loop (FLL) Definitions + + The following defines and declarations are used only if a pulse- + per-second (PPS) signal is available and connected via a modem- + control lead, such as produced by the optional ppsclock feature + incorporated in the serial port driver. They establish the design + parameters of the frequency-lock loop (FLL) used to discipline the + CPU clock oscillator to the PPS oscillator. + + PPS_AVG is the averaging constant used to update the FLL from + frequency samples measured for each calibration interval. + PPS_SHIFT and PPS_SHIFTMAX are the minimum and maximem, + respectively, of the calibration interval represented as a power + of two. The PPS_DISPINC is the initial increment to pps_disp at + each second. + + #define PPS_AVG 2 /* pps averaging constant (shift) */ + #define PPS_SHIFT 2 /* min interval duration (s) (shift) + */ + #define PPS_SHIFTMAX 6 /* max interval duration (s) (shift) + */ + #define PPS_DISPINC 0 /* dispersion increment (us/s) */ + + + + + + +Mills [Page 36] + +RFC 1589 Kernel Model for Precision Timekeeping March 1994 + + + 6.3. External Oscillator Definitions + + The following definitions and declarations are used only if an + external oscillator (HIGHBALL or TPRO) is configured on the + system. + + #define CLOCK_INTERVAL 30 /* CPU clock update interval (s) */ + +7. References + + [1] Mills, D., "Internet time synchronization: the Network Time + Protocol", IEEE Trans. Communications COM-39, 10 (October 1991), + 1482- 1493. Also in: Yang, Z., and T.A. Marsland (Eds.). Global + States and Time in Distributed Systems, IEEE Press, Los Alamitos, + CA, 91-102. + + [2] Mills, D., "Network Time Protocol (Version 3) specification, + implementation and analysis", RFC 1305, University of Delaware, + March 1992, 113 pp. + + [3] Mills, D., "Modelling and analysis of computer network clocks", + Electrical Engineering Department Report 92-5-2, University of + Delaware, May 1992, 29 pp. + + [4] Mills, D., "Simple Network Time Protocol (SNTP)", RFC 1361, + University of Delaware, August 1992, 10 pp. + + [5] Mills, D., "Precision synchronizatin of computer network clocks", + Electrical Engineering Department Report 93-11-1, University of + Delaware, November 1993, 66 pp. + +Security Considerations + + Security issues are not discussed in this memo. + +Author's Address + + David L. Mills + Electrical Engineering Department + University of Delaware + Newark, DE 19716 + + Phone: (302) 831-8247 + EMail: mills@udel.edu + + + + + + + +Mills [Page 37] + |