kern.c revision 1.5 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