Home | History | Annotate | Line # | Download | only in util
      1  1.5  christos /*	$NetBSD: kern.c,v 1.5 2020/05/25 20:47:37 christos Exp $	*/
      2  1.1    kardel 
      3  1.1    kardel /*
      4  1.1    kardel  * This program simulates a first-order, type-II phase-lock loop using
      5  1.1    kardel  * actual code segments from modified kernel distributions for SunOS,
      6  1.1    kardel  * Ultrix and OSF/1 kernels. These segments do not use any licensed code.
      7  1.1    kardel  */
      8  1.1    kardel 
      9  1.1    kardel #ifdef HAVE_CONFIG_H
     10  1.1    kardel # include <config.h>
     11  1.1    kardel #endif
     12  1.1    kardel 
     13  1.1    kardel #include <stdio.h>
     14  1.1    kardel #include <ctype.h>
     15  1.1    kardel #include <math.h>
     16  1.1    kardel #include <sys/time.h>
     17  1.1    kardel 
     18  1.1    kardel #ifdef HAVE_TIMEX_H
     19  1.1    kardel # include "timex.h"
     20  1.1    kardel #endif
     21  1.1    kardel 
     22  1.1    kardel /*
     23  1.1    kardel  * Phase-lock loop definitions
     24  1.1    kardel  */
     25  1.1    kardel #define HZ 100			/* timer interrupt frequency (Hz) */
     26  1.1    kardel #define MAXPHASE 512000		/* max phase error (us) */
     27  1.1    kardel #define MAXFREQ 200		/* max frequency error (ppm) */
     28  1.1    kardel #define TAU 2			/* time constant (shift 0 - 6) */
     29  1.1    kardel #define POLL 16			/* interval between updates (s) */
     30  1.1    kardel #define MAXSEC 1200		/* max interval between updates (s) */
     31  1.1    kardel 
     32  1.1    kardel /*
     33  1.1    kardel  * Function declarations
     34  1.1    kardel  */
     35  1.1    kardel void hardupdate();
     36  1.1    kardel void hardclock();
     37  1.1    kardel void second_overflow();
     38  1.1    kardel 
     39  1.1    kardel /*
     40  1.1    kardel  * Kernel variables
     41  1.1    kardel  */
     42  1.1    kardel int tick;			/* timer interrupt period (us) */
     43  1.1    kardel int fixtick;			/* amortization constant (ppm) */
     44  1.1    kardel struct timeval timex;		/* ripoff of kernel time variable */
     45  1.1    kardel 
     46  1.1    kardel /*
     47  1.1    kardel  * Phase-lock loop variables
     48  1.1    kardel  */
     49  1.1    kardel int time_status = TIME_BAD;	/* clock synchronization status */
     50  1.1    kardel long time_offset = 0;		/* time adjustment (us) */
     51  1.1    kardel long time_constant = 0;		/* pll time constant */
     52  1.1    kardel long time_tolerance = MAXFREQ;	/* frequency tolerance (ppm) */
     53  1.1    kardel long time_precision = 1000000 / HZ; /* clock precision (us) */
     54  1.1    kardel long time_maxerror = MAXPHASE;	/* maximum error (us) */
     55  1.1    kardel long time_esterror = MAXPHASE;	/* estimated error (us) */
     56  1.1    kardel long time_phase = 0;		/* phase offset (scaled us) */
     57  1.1    kardel long time_freq = 0;		/* frequency offset (scaled ppm) */
     58  1.1    kardel long time_adj = 0;		/* tick adjust (scaled 1 / HZ) */
     59  1.1    kardel long time_reftime = 0;		/* time at last adjustment (s) */
     60  1.1    kardel 
     61  1.1    kardel /*
     62  1.1    kardel  * Simulation variables
     63  1.1    kardel  */
     64  1.1    kardel double timey = 0;		/* simulation time (us) */
     65  1.1    kardel long timez = 0;			/* current error (us) */
     66  1.1    kardel long poll_interval = 0;		/* poll counter */
     67  1.1    kardel 
     68  1.1    kardel /*
     69  1.1    kardel  * Simulation test program
     70  1.1    kardel  */
     71  1.1    kardel int
     72  1.1    kardel main(
     73  1.1    kardel 	int argc,
     74  1.1    kardel 	char *argv[]
     75  1.1    kardel 	)
     76  1.1    kardel {
     77  1.1    kardel 	tick = 1000000 / HZ;
     78  1.1    kardel 	fixtick = 1000000 % HZ;
     79  1.1    kardel 	timex.tv_sec = 0;
     80  1.1    kardel 	timex.tv_usec = MAXPHASE;
     81  1.1    kardel 	time_freq = 0;
     82  1.1    kardel 	time_constant = TAU;
     83  1.1    kardel 	printf("tick %d us, fixtick %d us\n", tick, fixtick);
     84  1.1    kardel 	printf("      time    offset      freq   _offset     _freq      _adj\n");
     85  1.1    kardel 
     86  1.1    kardel 	/*
     87  1.1    kardel 	 * Grind the loop until ^C
     88  1.1    kardel 	 */
     89  1.1    kardel 	while (1) {
     90  1.1    kardel 		timey += (double)(1000000) / HZ;
     91  1.1    kardel 		if (timey >= 1000000)
     92  1.1    kardel 		    timey -= 1000000;
     93  1.1    kardel 		hardclock();
     94  1.1    kardel 		if (timex.tv_usec >= 1000000) {
     95  1.1    kardel 			timex.tv_usec -= 1000000;
     96  1.1    kardel 			timex.tv_sec++;
     97  1.1    kardel 			second_overflow();
     98  1.1    kardel 			poll_interval++;
     99  1.1    kardel 			if (!(poll_interval % POLL)) {
    100  1.1    kardel 				timez = (long)timey - timex.tv_usec;
    101  1.1    kardel 				if (timez > 500000)
    102  1.1    kardel 				    timez -= 1000000;
    103  1.1    kardel 				if (timez < -500000)
    104  1.1    kardel 				    timez += 1000000;
    105  1.1    kardel 				hardupdate(timez);
    106  1.1    kardel 				printf("%10li%10li%10.2f  %08lx  %08lx  %08lx\n",
    107  1.1    kardel 				       timex.tv_sec, timez,
    108  1.1    kardel 				       (double)time_freq / (1 << SHIFT_KF),
    109  1.1    kardel 				       time_offset, time_freq, time_adj);
    110  1.1    kardel 			}
    111  1.1    kardel 		}
    112  1.1    kardel 	}
    113  1.1    kardel }
    114  1.1    kardel 
    115  1.1    kardel /*
    116  1.1    kardel  * This routine simulates the ntp_adjtime() call
    117  1.1    kardel  *
    118  1.1    kardel  * For default SHIFT_UPDATE = 12, offset is limited to +-512 ms, the
    119  1.1    kardel  * maximum interval between updates is 4096 s and the maximum frequency
    120  1.1    kardel  * offset is +-31.25 ms/s.
    121  1.1    kardel  */
    122  1.1    kardel void
    123  1.1    kardel hardupdate(
    124  1.1    kardel 	long offset
    125  1.1    kardel 	)
    126  1.1    kardel {
    127  1.1    kardel 	long ltemp, mtemp;
    128  1.1    kardel 
    129  1.1    kardel 	time_offset = offset << SHIFT_UPDATE;
    130  1.1    kardel 	mtemp = timex.tv_sec - time_reftime;
    131  1.1    kardel 	time_reftime = timex.tv_sec;
    132  1.1    kardel 	if (mtemp > MAXSEC)
    133  1.1    kardel 	    mtemp = 0;
    134  1.1    kardel 
    135  1.1    kardel 	/* ugly multiply should be replaced */
    136  1.1    kardel 	if (offset < 0)
    137  1.1    kardel 	    time_freq -= (-offset * mtemp) >>
    138  1.1    kardel 		    (time_constant + time_constant);
    139  1.1    kardel 	else
    140  1.1    kardel 	    time_freq += (offset * mtemp) >>
    141  1.1    kardel 		    (time_constant + time_constant);
    142  1.1    kardel 	ltemp = time_tolerance << SHIFT_KF;
    143  1.1    kardel 	if (time_freq > ltemp)
    144  1.1    kardel 	    time_freq = ltemp;
    145  1.1    kardel 	else if (time_freq < -ltemp)
    146  1.1    kardel 	    time_freq = -ltemp;
    147  1.1    kardel 	if (time_status == TIME_BAD)
    148  1.1    kardel 	    time_status = TIME_OK;
    149  1.1    kardel }
    150  1.1    kardel 
    151  1.1    kardel /*
    152  1.1    kardel  * This routine simulates the timer interrupt
    153  1.1    kardel  */
    154  1.1    kardel void
    155  1.1    kardel hardclock(void)
    156  1.1    kardel {
    157  1.1    kardel 	int ltemp, time_update;
    158  1.1    kardel 
    159  1.1    kardel 	time_update = tick;	/* computed by adjtime() */
    160  1.1    kardel 	time_phase += time_adj;
    161  1.1    kardel 	if (time_phase < -FINEUSEC) {
    162  1.1    kardel 		ltemp = -time_phase >> SHIFT_SCALE;
    163  1.1    kardel 		time_phase += ltemp << SHIFT_SCALE;
    164  1.1    kardel 		time_update -= ltemp;
    165  1.1    kardel 	}
    166  1.1    kardel 	else if (time_phase > FINEUSEC) {
    167  1.1    kardel 		ltemp = time_phase >> SHIFT_SCALE;
    168  1.1    kardel 		time_phase -= ltemp << SHIFT_SCALE;
    169  1.1    kardel 		time_update += ltemp;
    170  1.1    kardel 	}
    171  1.1    kardel 	timex.tv_usec += time_update;
    172  1.1    kardel }
    173  1.1    kardel 
    174  1.1    kardel /*
    175  1.1    kardel  * This routine simulates the overflow of the microsecond field
    176  1.1    kardel  *
    177  1.1    kardel  * With SHIFT_SCALE = 23, the maximum frequency adjustment is +-256 us
    178  1.1    kardel  * per tick, or 25.6 ms/s at a clock frequency of 100 Hz. The time
    179  1.1    kardel  * contribution is shifted right a minimum of two bits, while the frequency
    180  1.1    kardel  * contribution is a right shift. Thus, overflow is prevented if the
    181  1.1    kardel  * frequency contribution is limited to half the maximum or 15.625 ms/s.
    182  1.1    kardel  */
    183  1.1    kardel void
    184  1.1    kardel second_overflow(void)
    185  1.1    kardel {
    186  1.1    kardel 	int ltemp;
    187  1.1    kardel 
    188  1.1    kardel 	time_maxerror += time_tolerance;
    189  1.1    kardel 	if (time_offset < 0) {
    190  1.1    kardel 		ltemp = -time_offset >>
    191  1.1    kardel 			(SHIFT_KG + time_constant);
    192  1.1    kardel 		time_offset += ltemp;
    193  1.1    kardel 		time_adj = -(ltemp <<
    194  1.1    kardel 			     (SHIFT_SCALE - SHIFT_HZ - SHIFT_UPDATE));
    195  1.1    kardel 	} else {
    196  1.1    kardel 		ltemp = time_offset >>
    197  1.1    kardel 			(SHIFT_KG + time_constant);
    198  1.1    kardel 		time_offset -= ltemp;
    199  1.1    kardel 		time_adj = ltemp <<
    200  1.1    kardel 			(SHIFT_SCALE - SHIFT_HZ - SHIFT_UPDATE);
    201  1.1    kardel 	}
    202  1.1    kardel 	if (time_freq < 0)
    203  1.1    kardel 	    time_adj -= -time_freq >> (SHIFT_KF + SHIFT_HZ - SHIFT_SCALE);
    204  1.1    kardel 	else
    205  1.1    kardel 	    time_adj += time_freq >> (SHIFT_KF + SHIFT_HZ - SHIFT_SCALE);
    206  1.1    kardel 	time_adj += fixtick << (SHIFT_SCALE - SHIFT_HZ);
    207  1.1    kardel 
    208  1.1    kardel 	/* ugly divide should be replaced */
    209  1.1    kardel 	if (timex.tv_sec % 86400 == 0) {
    210  1.1    kardel 		switch (time_status) {
    211  1.1    kardel 
    212  1.1    kardel 		    case TIME_INS:
    213  1.1    kardel 			timex.tv_sec--; /* !! */
    214  1.1    kardel 			time_status = TIME_OOP;
    215  1.1    kardel 			break;
    216  1.1    kardel 
    217  1.1    kardel 		    case TIME_DEL:
    218  1.1    kardel 			timex.tv_sec++;
    219  1.1    kardel 			time_status = TIME_OK;
    220  1.1    kardel 			break;
    221  1.1    kardel 
    222  1.1    kardel 		    case TIME_OOP:
    223  1.1    kardel 			time_status = TIME_OK;
    224  1.1    kardel 			break;
    225  1.1    kardel 		}
    226  1.1    kardel 	}
    227  1.1    kardel }
    228