xref: /plan9/sys/src/9/port/tod.c (revision 922818ba069237696730fb04e2b7b9d16c30f786)
1 #include	"u.h"
2 #include	"../port/lib.h"
3 #include	"mem.h"
4 #include	"dat.h"
5 #include	"fns.h"
6 #include	"../port/error.h"
7 
8 /*
9  * Compute nanosecond epoch time from the fastest ticking clock
10  * on the system.  Converting the time to nanoseconds requires
11  * the following formula
12  *
13  *	t = (((1000000000<<31)/f)*ticks)>>31
14  *
15  *  where
16  *
17  *	'f'		is the clock frequency
18  *	'ticks'		are clock ticks
19  *
20  *  to avoid too much calculation in todget(), we calculate
21  *
22  *	mult = (1000000000<<32)/f
23  *
24  *  each time f is set.  f is normally set by a user level
25  *  program writing to /dev/fastclock.  mul64fract will then
26  *  take that fractional multiplier and a 64 bit integer and
27  *  return the resulting integer product.
28  *
29  *  We assume that the cpu's of a multiprocessor are synchronized.
30  *  This assumption needs to be questioned with each new architecture.
31  */
32 
33 /* frequency of the tod clock */
34 #define TODFREQ		1000000000ULL
35 #define MicroFREQ	1000000ULL
36 
37 struct {
38 	int	init;		/* true if initialized */
39 	ulong	cnt;
40 	Lock;
41 	uvlong	multiplier;	/* ns = off + (multiplier*ticks)>>31 */
42 	uvlong	divider;	/* ticks = (divider*(ns-off))>>31 */
43 	uvlong	umultiplier;	/* µs = (µmultiplier*ticks)>>31 */
44 	uvlong	udivider;	/* ticks = (µdivider*µs)>>31 */
45 	vlong	hz;		/* frequency of fast clock */
46 	vlong	last;		/* last reading of fast clock */
47 	vlong	off;		/* offset from epoch to last */
48 	vlong	lasttime;	/* last return value from todget */
49 	vlong	delta;	/* add 'delta' each slow clock tick from sstart to send */
50 	ulong	sstart;		/* ... */
51 	ulong	send;		/* ... */
52 } tod;
53 
54 static void todfix(void);
55 
56 void
todinit(void)57 todinit(void)
58 {
59 	if(tod.init)
60 		return;
61 	ilock(&tod);
62 	tod.init = 1;			/* prevent reentry via fastticks */
63 	tod.last = fastticks((uvlong *)&tod.hz);
64 	iunlock(&tod);
65 	todsetfreq(tod.hz);
66 	addclock0link(todfix, 100);
67 }
68 
69 /*
70  *  calculate multiplier
71  */
72 void
todsetfreq(vlong f)73 todsetfreq(vlong f)
74 {
75 	if (f <= 0)
76 		panic("todsetfreq: freq %lld <= 0", f);
77 	ilock(&tod);
78 	tod.hz = f;
79 
80 	/* calculate multiplier for time conversion */
81 	tod.multiplier = mk64fract(TODFREQ, f);
82 	tod.divider = mk64fract(f, TODFREQ) + 1;
83 	tod.umultiplier = mk64fract(MicroFREQ, f);
84 	tod.udivider = mk64fract(f, MicroFREQ) + 1;
85 	iunlock(&tod);
86 }
87 
88 /*
89  *  Set the time of day struct
90  */
91 void
todset(vlong t,vlong delta,int n)92 todset(vlong t, vlong delta, int n)
93 {
94 	if(!tod.init)
95 		todinit();
96 
97 	ilock(&tod);
98 	if(t >= 0){
99 		tod.off = t;
100 		tod.last = fastticks(nil);
101 		tod.lasttime = 0;
102 		tod.delta = 0;
103 		tod.sstart = tod.send;
104 	} else {
105 		if(n <= 0)
106 			n = 1;
107 		n *= HZ;
108 		if(delta < 0 && n > -delta)
109 			n = -delta;
110 		if(delta > 0 && n > delta)
111 			n = delta;
112 		if (n == 0) {
113 			iprint("todset: n == 0, delta == %lld\n", delta);
114 			delta = 0;
115 		} else
116 			delta /= n;
117 		tod.sstart = MACHP(0)->ticks;
118 		tod.send = tod.sstart + n;
119 		tod.delta = delta;
120 	}
121 	iunlock(&tod);
122 }
123 
124 /*
125  *  get time of day
126  */
127 vlong
todget(vlong * ticksp)128 todget(vlong *ticksp)
129 {
130 	uvlong x;
131 	vlong ticks, diff;
132 	ulong t;
133 
134 	if(!tod.init)
135 		todinit();
136 
137 	/*
138 	 * we don't want time to pass twixt the measuring of fastticks
139 	 * and grabbing tod.last.  Also none of the vlongs are atomic so
140 	 * we have to look at them inside the lock.
141 	 */
142 	ilock(&tod);
143 	tod.cnt++;
144 	ticks = fastticks(nil);
145 
146 	/* add in correction */
147 	if(tod.sstart != tod.send){
148 		t = MACHP(0)->ticks;
149 		if(t >= tod.send)
150 			t = tod.send;
151 		tod.off = tod.off + tod.delta*(t - tod.sstart);
152 		tod.sstart = t;
153 	}
154 
155 	/* convert to epoch */
156 	diff = ticks - tod.last;
157 	if(diff < 0)
158 		diff = 0;
159 	mul64fract(&x, diff, tod.multiplier);
160 	x += tod.off;
161 
162 	/* time can't go backwards */
163 	if(x < tod.lasttime)
164 		x = tod.lasttime;
165 	else
166 		tod.lasttime = x;
167 
168 	iunlock(&tod);
169 
170 	if(ticksp != nil)
171 		*ticksp = ticks;
172 
173 	return x;
174 }
175 
176 /*
177  *  convert time of day to ticks
178  */
179 uvlong
tod2fastticks(vlong ns)180 tod2fastticks(vlong ns)
181 {
182 	uvlong x;
183 
184 	ilock(&tod);
185 	mul64fract(&x, ns-tod.off, tod.divider);
186 	x += tod.last;
187 	iunlock(&tod);
188 	return x;
189 }
190 
191 /*
192  *  called regularly to avoid calculation overflows
193  */
194 static void
todfix(void)195 todfix(void)
196 {
197 	vlong ticks, diff;
198 	uvlong x;
199 
200 	ticks = fastticks(nil);
201 
202 	diff = ticks - tod.last;
203 	if(diff > tod.hz){
204 		ilock(&tod);
205 
206 		/* convert to epoch */
207 		mul64fract(&x, diff, tod.multiplier);
208 if(x > 30000000000ULL) iprint("todfix %llud\n", x);
209 		x += tod.off;
210 
211 		/* protect against overflows */
212 		tod.last = ticks;
213 		tod.off = x;
214 
215 		iunlock(&tod);
216 	}
217 }
218 
219 long
seconds(void)220 seconds(void)
221 {
222 	return (vlong)todget(nil) / TODFREQ;
223 }
224 
225 uvlong
fastticks2us(uvlong ticks)226 fastticks2us(uvlong ticks)
227 {
228 	uvlong res;
229 
230 	if(!tod.init)
231 		todinit();
232 	mul64fract(&res, ticks, tod.umultiplier);
233 	return res;
234 }
235 
236 uvlong
us2fastticks(uvlong us)237 us2fastticks(uvlong us)
238 {
239 	uvlong res;
240 
241 	if(!tod.init)
242 		todinit();
243 	mul64fract(&res, us, tod.udivider);
244 	return res;
245 }
246 
247 /*
248  *  convert milliseconds to fast ticks
249  */
250 uvlong
ms2fastticks(ulong ms)251 ms2fastticks(ulong ms)
252 {
253 	if(!tod.init)
254 		todinit();
255 	return (tod.hz*ms)/1000ULL;
256 }
257 
258 /*
259  *  convert nanoseconds to fast ticks
260  */
261 uvlong
ns2fastticks(uvlong ns)262 ns2fastticks(uvlong ns)
263 {
264 	uvlong res;
265 
266 	if(!tod.init)
267 		todinit();
268 	mul64fract(&res, ns, tod.divider);
269 	return res;
270 }
271 
272 /*
273  *  convert fast ticks to ns
274  */
275 uvlong
fastticks2ns(uvlong ticks)276 fastticks2ns(uvlong ticks)
277 {
278 	uvlong res;
279 
280 	if(!tod.init)
281 		todinit();
282 	mul64fract(&res, ticks, tod.multiplier);
283 	return res;
284 }
285 
286 /*
287  * Make a 64 bit fixed point number that has a decimal point
288  * to the left of the low order 32 bits.  This is used with
289  * mul64fract for converting twixt nanoseconds and fastticks.
290  *
291  *	multiplier = (to<<32)/from
292  */
293 uvlong
mk64fract(uvlong to,uvlong from)294 mk64fract(uvlong to, uvlong from)
295 {
296 /*
297 	int shift;
298 
299 	if(to == 0ULL)
300 		return 0ULL;
301 
302 	shift = 0;
303 	while(shift < 32 && to < (1ULL<<(32+24))){
304 		to <<= 8;
305 		shift += 8;
306 	}
307 	while(shift < 32 && to < (1ULL<<(32+31))){
308 		to <<= 1;
309 		shift += 1;
310 	}
311 
312 	return (to/from)<<(32-shift);
313  */
314 	return (to<<32) / from;
315 }
316