xref: /plan9/sys/src/9/port/tod.c (revision 922818ba069237696730fb04e2b7b9d16c30f786)
17dd7cddfSDavid du Colombier #include	"u.h"
27dd7cddfSDavid du Colombier #include	"../port/lib.h"
37dd7cddfSDavid du Colombier #include	"mem.h"
47dd7cddfSDavid du Colombier #include	"dat.h"
57dd7cddfSDavid du Colombier #include	"fns.h"
67dd7cddfSDavid du Colombier #include	"../port/error.h"
77dd7cddfSDavid du Colombier 
8fc567a6bSDavid du Colombier /*
9fc567a6bSDavid du Colombier  * Compute nanosecond epoch time from the fastest ticking clock
10fc567a6bSDavid du Colombier  * on the system.  Converting the time to nanoseconds requires
11e288d156SDavid du Colombier  * the following formula
12e288d156SDavid du Colombier  *
13e288d156SDavid du Colombier  *	t = (((1000000000<<31)/f)*ticks)>>31
14e288d156SDavid du Colombier  *
15e288d156SDavid du Colombier  *  where
16e288d156SDavid du Colombier  *
17e288d156SDavid du Colombier  *	'f'		is the clock frequency
18e288d156SDavid du Colombier  *	'ticks'		are clock ticks
19e288d156SDavid du Colombier  *
20e288d156SDavid du Colombier  *  to avoid too much calculation in todget(), we calculate
21e288d156SDavid du Colombier  *
22e288d156SDavid du Colombier  *	mult = (1000000000<<32)/f
23e288d156SDavid du Colombier  *
24e288d156SDavid du Colombier  *  each time f is set.  f is normally set by a user level
25e288d156SDavid du Colombier  *  program writing to /dev/fastclock.  mul64fract will then
26e288d156SDavid du Colombier  *  take that fractional multiplier and a 64 bit integer and
27e288d156SDavid du Colombier  *  return the resulting integer product.
28e288d156SDavid du Colombier  *
29e288d156SDavid du Colombier  *  We assume that the cpu's of a multiprocessor are synchronized.
30e288d156SDavid du Colombier  *  This assumption needs to be questioned with each new architecture.
31e288d156SDavid du Colombier  */
327dd7cddfSDavid du Colombier 
33e288d156SDavid du Colombier /* frequency of the tod clock */
349a747e4fSDavid du Colombier #define TODFREQ		1000000000ULL
352cca75a1SDavid du Colombier #define MicroFREQ	1000000ULL
367dd7cddfSDavid du Colombier 
377dd7cddfSDavid du Colombier struct {
38ea58ad6fSDavid du Colombier 	int	init;		/* true if initialized */
3959cc4ca5SDavid du Colombier 	ulong	cnt;
407dd7cddfSDavid du Colombier 	Lock;
41ea58ad6fSDavid du Colombier 	uvlong	multiplier;	/* ns = off + (multiplier*ticks)>>31 */
42ea58ad6fSDavid du Colombier 	uvlong	divider;	/* ticks = (divider*(ns-off))>>31 */
43ea58ad6fSDavid du Colombier 	uvlong	umultiplier;	/* µs = (µmultiplier*ticks)>>31 */
44ea58ad6fSDavid du Colombier 	uvlong	udivider;	/* ticks = (µdivider*µs)>>31 */
45ea58ad6fSDavid du Colombier 	vlong	hz;		/* frequency of fast clock */
46ea58ad6fSDavid du Colombier 	vlong	last;		/* last reading of fast clock */
47ea58ad6fSDavid du Colombier 	vlong	off;		/* offset from epoch to last */
48ea58ad6fSDavid du Colombier 	vlong	lasttime;	/* last return value from todget */
49ea58ad6fSDavid du Colombier 	vlong	delta;	/* add 'delta' each slow clock tick from sstart to send */
50ea58ad6fSDavid du Colombier 	ulong	sstart;		/* ... */
51ea58ad6fSDavid du Colombier 	ulong	send;		/* ... */
527dd7cddfSDavid du Colombier } tod;
537dd7cddfSDavid du Colombier 
54fc567a6bSDavid du Colombier static void todfix(void);
55fc567a6bSDavid du Colombier 
567dd7cddfSDavid du Colombier void
todinit(void)577dd7cddfSDavid du Colombier todinit(void)
587dd7cddfSDavid du Colombier {
59e288d156SDavid du Colombier 	if(tod.init)
60e288d156SDavid du Colombier 		return;
6180ee5cbfSDavid du Colombier 	ilock(&tod);
62*922818baSDavid du Colombier 	tod.init = 1;			/* prevent reentry via fastticks */
6380ee5cbfSDavid du Colombier 	tod.last = fastticks((uvlong *)&tod.hz);
6480ee5cbfSDavid du Colombier 	iunlock(&tod);
657dd7cddfSDavid du Colombier 	todsetfreq(tod.hz);
66d9306527SDavid du Colombier 	addclock0link(todfix, 100);
677dd7cddfSDavid du Colombier }
687dd7cddfSDavid du Colombier 
69e288d156SDavid du Colombier /*
70e288d156SDavid du Colombier  *  calculate multiplier
71e288d156SDavid du Colombier  */
727dd7cddfSDavid du Colombier void
todsetfreq(vlong f)737dd7cddfSDavid du Colombier todsetfreq(vlong f)
747dd7cddfSDavid du Colombier {
75*922818baSDavid du Colombier 	if (f <= 0)
76*922818baSDavid du Colombier 		panic("todsetfreq: freq %lld <= 0", f);
777dd7cddfSDavid du Colombier 	ilock(&tod);
787dd7cddfSDavid du Colombier 	tod.hz = f;
799a747e4fSDavid du Colombier 
809a747e4fSDavid du Colombier 	/* calculate multiplier for time conversion */
81e288d156SDavid du Colombier 	tod.multiplier = mk64fract(TODFREQ, f);
82ea58ad6fSDavid du Colombier 	tod.divider = mk64fract(f, TODFREQ) + 1;
832cca75a1SDavid du Colombier 	tod.umultiplier = mk64fract(MicroFREQ, f);
84ea58ad6fSDavid du Colombier 	tod.udivider = mk64fract(f, MicroFREQ) + 1;
857dd7cddfSDavid du Colombier 	iunlock(&tod);
867dd7cddfSDavid du Colombier }
877dd7cddfSDavid du Colombier 
88e288d156SDavid du Colombier /*
89e288d156SDavid du Colombier  *  Set the time of day struct
90e288d156SDavid du Colombier  */
917dd7cddfSDavid du Colombier void
todset(vlong t,vlong delta,int n)927dd7cddfSDavid du Colombier todset(vlong t, vlong delta, int n)
937dd7cddfSDavid du Colombier {
94e288d156SDavid du Colombier 	if(!tod.init)
95e288d156SDavid du Colombier 		todinit();
96e288d156SDavid du Colombier 
977dd7cddfSDavid du Colombier 	ilock(&tod);
987dd7cddfSDavid du Colombier 	if(t >= 0){
997dd7cddfSDavid du Colombier 		tod.off = t;
1007dd7cddfSDavid du Colombier 		tod.last = fastticks(nil);
1017dd7cddfSDavid du Colombier 		tod.lasttime = 0;
1027dd7cddfSDavid du Colombier 		tod.delta = 0;
1037dd7cddfSDavid du Colombier 		tod.sstart = tod.send;
1047dd7cddfSDavid du Colombier 	} else {
1057dd7cddfSDavid du Colombier 		if(n <= 0)
1067dd7cddfSDavid du Colombier 			n = 1;
1077dd7cddfSDavid du Colombier 		n *= HZ;
1087dd7cddfSDavid du Colombier 		if(delta < 0 && n > -delta)
1097dd7cddfSDavid du Colombier 			n = -delta;
1107dd7cddfSDavid du Colombier 		if(delta > 0 && n > delta)
1117dd7cddfSDavid du Colombier 			n = delta;
112*922818baSDavid du Colombier 		if (n == 0) {
113*922818baSDavid du Colombier 			iprint("todset: n == 0, delta == %lld\n", delta);
114*922818baSDavid du Colombier 			delta = 0;
115*922818baSDavid du Colombier 		} else
116*922818baSDavid du Colombier 			delta /= n;
1177dd7cddfSDavid du Colombier 		tod.sstart = MACHP(0)->ticks;
1187dd7cddfSDavid du Colombier 		tod.send = tod.sstart + n;
1197dd7cddfSDavid du Colombier 		tod.delta = delta;
1207dd7cddfSDavid du Colombier 	}
1217dd7cddfSDavid du Colombier 	iunlock(&tod);
1227dd7cddfSDavid du Colombier }
1237dd7cddfSDavid du Colombier 
124e288d156SDavid du Colombier /*
125e288d156SDavid du Colombier  *  get time of day
126e288d156SDavid du Colombier  */
1277dd7cddfSDavid du Colombier vlong
todget(vlong * ticksp)1287dd7cddfSDavid du Colombier todget(vlong *ticksp)
1297dd7cddfSDavid du Colombier {
1307dd7cddfSDavid du Colombier 	uvlong x;
1317dd7cddfSDavid du Colombier 	vlong ticks, diff;
1327dd7cddfSDavid du Colombier 	ulong t;
1337dd7cddfSDavid du Colombier 
134e288d156SDavid du Colombier 	if(!tod.init)
135e288d156SDavid du Colombier 		todinit();
1367dd7cddfSDavid du Colombier 
137ea58ad6fSDavid du Colombier 	/*
138ea58ad6fSDavid du Colombier 	 * we don't want time to pass twixt the measuring of fastticks
139ea58ad6fSDavid du Colombier 	 * and grabbing tod.last.  Also none of the vlongs are atomic so
140ea58ad6fSDavid du Colombier 	 * we have to look at them inside the lock.
141ea58ad6fSDavid du Colombier 	 */
1427dd7cddfSDavid du Colombier 	ilock(&tod);
14359cc4ca5SDavid du Colombier 	tod.cnt++;
144e288d156SDavid du Colombier 	ticks = fastticks(nil);
1457dd7cddfSDavid du Colombier 
146ea58ad6fSDavid du Colombier 	/* add in correction */
1477dd7cddfSDavid du Colombier 	if(tod.sstart != tod.send){
1487dd7cddfSDavid du Colombier 		t = MACHP(0)->ticks;
1497dd7cddfSDavid du Colombier 		if(t >= tod.send)
1507dd7cddfSDavid du Colombier 			t = tod.send;
1517dd7cddfSDavid du Colombier 		tod.off = tod.off + tod.delta*(t - tod.sstart);
1527dd7cddfSDavid du Colombier 		tod.sstart = t;
1537dd7cddfSDavid du Colombier 	}
1547dd7cddfSDavid du Colombier 
155ea58ad6fSDavid du Colombier 	/* convert to epoch */
15659cc4ca5SDavid du Colombier 	diff = ticks - tod.last;
157e288d156SDavid du Colombier 	if(diff < 0)
158e288d156SDavid du Colombier 		diff = 0;
159e288d156SDavid du Colombier 	mul64fract(&x, diff, tod.multiplier);
160e288d156SDavid du Colombier 	x += tod.off;
1617dd7cddfSDavid du Colombier 
162ea58ad6fSDavid du Colombier 	/* time can't go backwards */
1637dd7cddfSDavid du Colombier 	if(x < tod.lasttime)
1647dd7cddfSDavid du Colombier 		x = tod.lasttime;
1657dd7cddfSDavid du Colombier 	else
1667dd7cddfSDavid du Colombier 		tod.lasttime = x;
1677dd7cddfSDavid du Colombier 
16859cc4ca5SDavid du Colombier 	iunlock(&tod);
16959cc4ca5SDavid du Colombier 
1707dd7cddfSDavid du Colombier 	if(ticksp != nil)
1717dd7cddfSDavid du Colombier 		*ticksp = ticks;
1727dd7cddfSDavid du Colombier 
1737dd7cddfSDavid du Colombier 	return x;
1747dd7cddfSDavid du Colombier }
1757dd7cddfSDavid du Colombier 
176e288d156SDavid du Colombier /*
177e288d156SDavid du Colombier  *  convert time of day to ticks
178e288d156SDavid du Colombier  */
179e288d156SDavid du Colombier uvlong
tod2fastticks(vlong ns)180e288d156SDavid du Colombier tod2fastticks(vlong ns)
181e288d156SDavid du Colombier {
182e288d156SDavid du Colombier 	uvlong x;
183e288d156SDavid du Colombier 
184e288d156SDavid du Colombier 	ilock(&tod);
185e288d156SDavid du Colombier 	mul64fract(&x, ns-tod.off, tod.divider);
186e288d156SDavid du Colombier 	x += tod.last;
187e288d156SDavid du Colombier 	iunlock(&tod);
188e288d156SDavid du Colombier 	return x;
189e288d156SDavid du Colombier }
190e288d156SDavid du Colombier 
191e288d156SDavid du Colombier /*
192e288d156SDavid du Colombier  *  called regularly to avoid calculation overflows
193e288d156SDavid du Colombier  */
194fc567a6bSDavid du Colombier static void
todfix(void)1957dd7cddfSDavid du Colombier todfix(void)
1967dd7cddfSDavid du Colombier {
19759cc4ca5SDavid du Colombier 	vlong ticks, diff;
19859cc4ca5SDavid du Colombier 	uvlong x;
1997dd7cddfSDavid du Colombier 
20059cc4ca5SDavid du Colombier 	ticks = fastticks(nil);
20159cc4ca5SDavid du Colombier 
20259cc4ca5SDavid du Colombier 	diff = ticks - tod.last;
20359cc4ca5SDavid du Colombier 	if(diff > tod.hz){
20459cc4ca5SDavid du Colombier 		ilock(&tod);
20559cc4ca5SDavid du Colombier 
206ea58ad6fSDavid du Colombier 		/* convert to epoch */
207e288d156SDavid du Colombier 		mul64fract(&x, diff, tod.multiplier);
208*922818baSDavid du Colombier if(x > 30000000000ULL) iprint("todfix %llud\n", x);
209e288d156SDavid du Colombier 		x += tod.off;
21059cc4ca5SDavid du Colombier 
211ea58ad6fSDavid du Colombier 		/* protect against overflows */
21259cc4ca5SDavid du Colombier 		tod.last = ticks;
21359cc4ca5SDavid du Colombier 		tod.off = x;
21459cc4ca5SDavid du Colombier 
21559cc4ca5SDavid du Colombier 		iunlock(&tod);
2167dd7cddfSDavid du Colombier 	}
2177dd7cddfSDavid du Colombier }
2187dd7cddfSDavid du Colombier 
2197dd7cddfSDavid du Colombier long
seconds(void)2207dd7cddfSDavid du Colombier seconds(void)
2217dd7cddfSDavid du Colombier {
222*922818baSDavid du Colombier 	return (vlong)todget(nil) / TODFREQ;
2237dd7cddfSDavid du Colombier }
2249a747e4fSDavid du Colombier 
2252cca75a1SDavid du Colombier uvlong
fastticks2us(uvlong ticks)2262cca75a1SDavid du Colombier fastticks2us(uvlong ticks)
2272cca75a1SDavid du Colombier {
2282cca75a1SDavid du Colombier 	uvlong res;
2292cca75a1SDavid du Colombier 
2302cca75a1SDavid du Colombier 	if(!tod.init)
2312cca75a1SDavid du Colombier 		todinit();
2322cca75a1SDavid du Colombier 	mul64fract(&res, ticks, tod.umultiplier);
2332cca75a1SDavid du Colombier 	return res;
2342cca75a1SDavid du Colombier }
2352cca75a1SDavid du Colombier 
2362cca75a1SDavid du Colombier uvlong
us2fastticks(uvlong us)2372cca75a1SDavid du Colombier us2fastticks(uvlong us)
2382cca75a1SDavid du Colombier {
2392cca75a1SDavid du Colombier 	uvlong res;
2402cca75a1SDavid du Colombier 
2412cca75a1SDavid du Colombier 	if(!tod.init)
2422cca75a1SDavid du Colombier 		todinit();
2432cca75a1SDavid du Colombier 	mul64fract(&res, us, tod.udivider);
2442cca75a1SDavid du Colombier 	return res;
2452cca75a1SDavid du Colombier }
2462cca75a1SDavid du Colombier 
247fc567a6bSDavid du Colombier /*
248fc567a6bSDavid du Colombier  *  convert milliseconds to fast ticks
249fc567a6bSDavid du Colombier  */
2509a747e4fSDavid du Colombier uvlong
ms2fastticks(ulong ms)2519a747e4fSDavid du Colombier ms2fastticks(ulong ms)
2529a747e4fSDavid du Colombier {
253e288d156SDavid du Colombier 	if(!tod.init)
254e288d156SDavid du Colombier 		todinit();
2559a747e4fSDavid du Colombier 	return (tod.hz*ms)/1000ULL;
2569a747e4fSDavid du Colombier }
257e288d156SDavid du Colombier 
258e288d156SDavid du Colombier /*
259e288d156SDavid du Colombier  *  convert nanoseconds to fast ticks
260e288d156SDavid du Colombier  */
261e288d156SDavid du Colombier uvlong
ns2fastticks(uvlong ns)262e288d156SDavid du Colombier ns2fastticks(uvlong ns)
263e288d156SDavid du Colombier {
264e288d156SDavid du Colombier 	uvlong res;
265e288d156SDavid du Colombier 
266e288d156SDavid du Colombier 	if(!tod.init)
267e288d156SDavid du Colombier 		todinit();
268e288d156SDavid du Colombier 	mul64fract(&res, ns, tod.divider);
269e288d156SDavid du Colombier 	return res;
270e288d156SDavid du Colombier }
271e288d156SDavid du Colombier 
272e288d156SDavid du Colombier /*
273e288d156SDavid du Colombier  *  convert fast ticks to ns
274e288d156SDavid du Colombier  */
275e288d156SDavid du Colombier uvlong
fastticks2ns(uvlong ticks)276e288d156SDavid du Colombier fastticks2ns(uvlong ticks)
277e288d156SDavid du Colombier {
278e288d156SDavid du Colombier 	uvlong res;
279e288d156SDavid du Colombier 
280e288d156SDavid du Colombier 	if(!tod.init)
281e288d156SDavid du Colombier 		todinit();
282e288d156SDavid du Colombier 	mul64fract(&res, ticks, tod.multiplier);
283e288d156SDavid du Colombier 	return res;
284e288d156SDavid du Colombier }
285e288d156SDavid du Colombier 
286e288d156SDavid du Colombier /*
287e288d156SDavid du Colombier  * Make a 64 bit fixed point number that has a decimal point
288e288d156SDavid du Colombier  * to the left of the low order 32 bits.  This is used with
289e288d156SDavid du Colombier  * mul64fract for converting twixt nanoseconds and fastticks.
290e288d156SDavid du Colombier  *
291e288d156SDavid du Colombier  *	multiplier = (to<<32)/from
292e288d156SDavid du Colombier  */
293e288d156SDavid du Colombier uvlong
mk64fract(uvlong to,uvlong from)294e288d156SDavid du Colombier mk64fract(uvlong to, uvlong from)
295e288d156SDavid du Colombier {
296e288d156SDavid du Colombier /*
297e288d156SDavid du Colombier 	int shift;
298e288d156SDavid du Colombier 
299e288d156SDavid du Colombier 	if(to == 0ULL)
300e288d156SDavid du Colombier 		return 0ULL;
301e288d156SDavid du Colombier 
302e288d156SDavid du Colombier 	shift = 0;
303e288d156SDavid du Colombier 	while(shift < 32 && to < (1ULL<<(32+24))){
304e288d156SDavid du Colombier 		to <<= 8;
305e288d156SDavid du Colombier 		shift += 8;
306e288d156SDavid du Colombier 	}
307e288d156SDavid du Colombier 	while(shift < 32 && to < (1ULL<<(32+31))){
308e288d156SDavid du Colombier 		to <<= 1;
309e288d156SDavid du Colombier 		shift += 1;
310e288d156SDavid du Colombier 	}
311e288d156SDavid du Colombier 
312e288d156SDavid du Colombier 	return (to/from)<<(32-shift);
313e288d156SDavid du Colombier  */
314e288d156SDavid du Colombier 	return (to<<32) / from;
315e288d156SDavid du Colombier }
316