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