# README,v 3.1 1993/07/06 01:09:46 jbj Exp PPSCLOCK 1.3 BETA Release Please send bugs and comments to ppsclock@ee.lbl.gov. This is SunOS 4 tty STREAMS module that can be used to interface the one-pulse-per-second output of an accurate external clock (e.g., a GPS receiver) to NTP. This module records the value of the Unix clock each time the 1pps pulse happens. The recorded value can then be read by an application using a CIOGETEV ioctl. We have been running this module with xntp3, SunOS 4.1.2 and a Magnavox MX4200 GPS receiver (a very nice, relatively inexpensive GPS box for use with NTP) and have been happy with the results: The time from when the GPS sends the 1pps pulse to when this code grabs the Unix time is an almost constant 15us on a SparcStation 1+ and 9us on a SparcStation 2 (there is conditionally compiled code in the driver that may help if you want to measure this interval yourself -- see below). Since the GPS is typically connected via an RS-232 serial port & since the SparcStation has too few serial ports, this module gets the 1pps signal via the same port used to get the GPS data. We do this by connecting the GPS 1pps output to `Carrier Detect' (RS-232 pin 8) on serial cable between the GPS & the Sparc. Since `Carrier Detect' transitions cause a high priority zs `service' interrupt on the Sparc (which is separate from the zs `data' interrupt used to collect ascii data from the GPS), this is a cheap way to turn the 1pps pulse into a Sparc interrupt. However, since `Carrier Detect' now has a non-standard meaning on the clock serial line, *YOU MUST SET "SOFT CARRIER" ON THE CLOCK SERIAL PORT* (i.e., the port you push this streams module onto). But note that this module changes the meaning of the `Carrier Detect' interrupt *only* on the port(s) it's pushed on; other serial ports continue to work as they always have & the special interpretation of `Carrier Detect' will go away whenever this steams module is popped (e.g., if xntpd exits). In order to use the 1pps pulse to correct both the Unix clock offset and frequency, the time between when the external clock signals 1pps and when this module records the Unix clock should be as small as possible and should be repeatable. The totally braindead AT&T STREAMS code makes it very difficult to achieve these objectives: The allocb/wput overhead adds about 700us of latency on a SS-1+ and the worthless stream 'scheduler' combined with doing everything at a relatively low interrupt priority adds about +-400us of jitter. So, to get a high quality time stamp, this module uses STREAMS *only* to get the ioctl request. It gets the 1pps signal by avoiding STREAMS code entirely and inserting itself directly into the hardware interrupt service path for the 1pps pulse. It does this by being *very* chummy with Sun's `zs' serial driver -- it basically steals the modem control interrupt from the zs. So *THIS IS NOT A GENERIC STREAMS MODULE*. It is designed to work with Sun's zs driver under SunOS 4. Expect it to break under Solaris-2. Expect it to not run on anything but a Sun. Also note that for our GPS clock, `1 second' seems to occur at the leading edge of the pulse so that's when we grab the timestamp. If your clock indicates 1pps on the trailing edge you should change the line: if ((s0 & ZSRR0_CD) != 0) { in ppsclock_intr to: if ((s0 & ZSRR0_CD) == 0) { to grab the time stamp on the trailing edge of the pulse. One final note about running this code on a Sun Sparcstation 1; we have found that you need revision 1.3 or higher proms with the Sun 4/60; this is because 1.2 and earlier roms listen to BOTH the A and B serial ports while selftesting; if you have something like a GPS receiver talking once a second at 4800 baud to a serial port, even if it's not the console serial port, the system will see a "break" and abort booting up. This makes it impossible to automatically reboot after a crash. Measuring Performance --------------------- We have frequently found the need for some non-invasive means to measure internal system performance numbers such as interrupt latencies. The following trick works pretty well: The SparcStation has a front panel LED that the system turns on at boot time then never changes. There is a register (the 'auxio' register) that the kernel can write to change the state of this LED & it costs almost exactly 1us to turn the LED off then on again. So, for example, you can measure the interrupt latency to when we grab unix time stamp by attaching a scope probe to the LED (it's easiest to stick the probe into the LED's connector on the motherboard) then trigger the scope off the appropriate edge of the 1pps pulse from the GPS. If you want to measure the amount of time some system routine takes, you can turn the LED off before calling the routine and on afterwards so the pulse width on the scope is a direct measure of the routine's execution time. If you want to play with this, there's code in ppsclock.c (conditional on the define PPSCLOCKLED) that will toggle the LED in the hardware interrupt handler for the 1pps intr. The time from whichever edge of the 1pps pulse you chose to stamp to when the LED goes off is the interrupt latency & the LED off time is a measure of the cost to call Sun's routine uniqtime (which reads the Unix time to 1us accuracy). By the way, if you try this & think the cost of calling Sun's `uniqtime' is appalling (42-85us/call -- we thought this was appalling) this distribution includes a replacement for it, microtime.s, that runs 10 times faster than Sun's code (~3us/call) & gives you a more accurate time. (If you install this, it will improve anything that gets high-res unix kernel time, including the gettimeofday system call that xntp uses). INSTALLATION ------------ These are some (sketchy) notes on installation of the SunOS 4 pps clock streams module. This directory contains: README - this file RELEASE - version of this releasethis CHANGES - description of differences between releases magnavox.ps - PostScript schematic (Magnavox rs422/rs232 converter) b-and-b.ps - PostScript schematic (B&B rs422/rs232 converter) Makefile - compilation rules ppstest - ppsclock test program (works with Magnavox MX4200 GPS -- you can probably use this as sample code illustrating the use of this streams module if you have some other kind of clock). sys - SunOS 4 kernel modules sys/genassym - genassym program for object-only (non-source) sites KERNEL CONFIGURATION -------------------- (1) Copy sys/sundev/ppsclock.c to /sys/sundev. (2) Copy sys/sys/ppsclock.h to /sys/sys (and /usr/include/sys if it is separate directory). (3) If you want to use the fast microtime module, copy sys/sun4c/microtime.s to /sys/sun4c. (4) Use sys/sun/str_conf.c.patch to patch /sys/sun/str_conf.c. Alternately, manually install the following lines in the appropriate places: #include "zs.h" [...] #if NZS > 0 #include "sys/ppsclock.h" extern struct streamtab ppsclockinfo; #endif [...] #if NZS > 0 { PPSCLOCKSTR, &ppsclockinfo }, #endif (5) Use sun4c/conf/files.patch to patch /sys/sun4c/conf/files. Alternately, manually install the following line in the appropriate place: sundev/ppsclock.c optional zs device-driver In addition, if you want to use the fast microtime module, use sun4c/conf/files.microtime.patch to patch /sys/sun4c/conf/files. Alternately, manually install the following line in the appropriate place: sun4c/microtime.s standard If you are using SunOS 4.1.3 and wish to support the sun4m architecture, apply the corresponding patches from the sun4m tree. (6) If you want to use the fast microtime module, and have full SunOS 4 source, use /sys/os/kern_clock.c.patch to patch /sys/os/kern_clock.c. Alternately, edit /sys/os/kern_clock.c and bracket the code for uniqtime() with the following two lines: #if !defined(sun4c) && !defined(sun4m) #endif If you DO NOT have full SunOS 4 source but still want to use the fast microtime, change the symbol table entry in for _uniqtime to _Uniqtime as follows: hell 1 % cd /sys/sun4c/OBJ # or /sys/sun4m hell 2 % mv kern_clock.o kern_clock.o.virgin hell 2 % cp kern_clock.o.virgin kern_clock.o hell 3 % chmod +w kern_clock.o hell 5 % strings -o -a kern_clock.o | grep -w _uniqtime 7892 _uniqtime hell 6 % adb -w kern_clock.o ?m 0 0xffffffff 0 0t7892?s _dk_ndrive+0x12cc: _uniqtime .?x _dk_ndrive+0x12cc: 5f75 .?w5f55 _dk_ndrive+0x12cc: 0x5f75 = 0x5f55 .?s _dk_ndrive+0x12cc: _Uniqtime ^D (7) If you DO NOT have full SunOS 4 source, you are missing the source to genassym. Copy the "mini" genassym source from genassym/genassym.c to /sys/sun4c (and into /sys/sun4m if you have SunOS 4.1.3 and wish to support the sun4m architecture). Next, use sun4c/conf/Makefile.src.patch to patch /sys/sun4c/conf/Makefile.src (and possibly /sys/sun4m/conf/Makefile.src). Alternately, manually install the following rules in the prototype Makefile(s). assym.s: ${MACHINE}/genassym.c ${CC} -E ${CPPOPTS} ${MACHINE}/genassym.c > ./a.out.c cc ${COPTS} ./a.out.c ./a.out >assym.s rm -f ./a.out ./a.out.c (8) Config, build, and boot the new kernel. (9) If you have an MX4200, build the test program in the ppstest directory and run it. It waits for a message at 4800 baud and then prints both the message and the time stamp of the last "carrier detect" transition.