xref: /plan9-contrib/sys/src/9k/port/tod.c (revision 406c76facc4b13aa2a55454bf4091aab9f03da22)
19ef1f84bSDavid du Colombier #include	"u.h"
29ef1f84bSDavid du Colombier #include	"../port/lib.h"
39ef1f84bSDavid du Colombier #include	"mem.h"
49ef1f84bSDavid du Colombier #include	"dat.h"
59ef1f84bSDavid du Colombier #include	"fns.h"
69ef1f84bSDavid du Colombier 
79ef1f84bSDavid du Colombier /*
89ef1f84bSDavid du Colombier  * Compute nanosecond epoch time from the fastest ticking clock
99ef1f84bSDavid du Colombier  * on the system.  Converting the time to nanoseconds requires
109ef1f84bSDavid du Colombier  * the following formula
119ef1f84bSDavid du Colombier  *
129ef1f84bSDavid du Colombier  *	t = (((1000000000<<31)/f)*ticks)>>31
139ef1f84bSDavid du Colombier  *
149ef1f84bSDavid du Colombier  *  where
159ef1f84bSDavid du Colombier  *
169ef1f84bSDavid du Colombier  *	'f'		is the clock frequency
179ef1f84bSDavid du Colombier  *	'ticks'		are clock ticks
189ef1f84bSDavid du Colombier  *
199ef1f84bSDavid du Colombier  *  to avoid too much calculation in todget(), we calculate
209ef1f84bSDavid du Colombier  *
219ef1f84bSDavid du Colombier  *	mult = (1000000000<<32)/f
229ef1f84bSDavid du Colombier  *
239ef1f84bSDavid du Colombier  *  each time f is set.  f is normally set by a user level
249ef1f84bSDavid du Colombier  *  program writing to /dev/fastclock.  mul64fract will then
259ef1f84bSDavid du Colombier  *  take that fractional multiplier and a 64 bit integer and
269ef1f84bSDavid du Colombier  *  return the resulting integer product.
279ef1f84bSDavid du Colombier  *
289ef1f84bSDavid du Colombier  *  We assume that the cpu's of a multiprocessor are synchronized.
299ef1f84bSDavid du Colombier  *  This assumption needs to be questioned with each new architecture.
309ef1f84bSDavid du Colombier  */
319ef1f84bSDavid du Colombier 
329ef1f84bSDavid du Colombier /* frequency of the tod clock */
339ef1f84bSDavid du Colombier #define TODFREQ		1000000000ULL
349ef1f84bSDavid du Colombier #define MicroFREQ	1000000ULL
359ef1f84bSDavid du Colombier 
369ef1f84bSDavid du Colombier struct {
379ef1f84bSDavid du Colombier 	int	init;		/* true if initialized */
389ef1f84bSDavid du Colombier 	ulong	cnt;
399ef1f84bSDavid du Colombier 	Lock;
409ef1f84bSDavid du Colombier 	uvlong	multiplier;	/* ns = off + (multiplier*ticks)>>31 */
419ef1f84bSDavid du Colombier 	uvlong	divider;	/* ticks = (divider*(ns-off))>>31 */
429ef1f84bSDavid du Colombier 	uvlong	umultiplier;	/* µs = (µmultiplier*ticks)>>31 */
439ef1f84bSDavid du Colombier 	uvlong	udivider;	/* ticks = (µdivider*µs)>>31 */
449ef1f84bSDavid du Colombier 	vlong	hz;		/* frequency of fast clock */
459ef1f84bSDavid du Colombier 	vlong	last;		/* last reading of fast clock */
469ef1f84bSDavid du Colombier 	vlong	off;		/* offset from epoch to last */
479ef1f84bSDavid du Colombier 	vlong	lasttime;	/* last return value from todget */
489ef1f84bSDavid du Colombier 	vlong	delta;	/* add 'delta' each slow clock tick from sstart to send */
499ef1f84bSDavid du Colombier 	ulong	sstart;		/* ... */
509ef1f84bSDavid du Colombier 	ulong	send;		/* ... */
519ef1f84bSDavid du Colombier } tod;
529ef1f84bSDavid du Colombier 
539ef1f84bSDavid du Colombier static void todfix(void);
549ef1f84bSDavid du Colombier 
559ef1f84bSDavid du Colombier void
todinit(void)569ef1f84bSDavid du Colombier todinit(void)
579ef1f84bSDavid du Colombier {
589ef1f84bSDavid du Colombier 	if(tod.init)
599ef1f84bSDavid du Colombier 		return;
609ef1f84bSDavid du Colombier 	ilock(&tod);
61*406c76faSDavid du Colombier 	tod.init = 1;			/* prevent reentry via fastticks */
629ef1f84bSDavid du Colombier 	tod.last = fastticks((uvlong *)&tod.hz);
639ef1f84bSDavid du Colombier 	iunlock(&tod);
649ef1f84bSDavid du Colombier 	todsetfreq(tod.hz);
659ef1f84bSDavid du Colombier 	addclock0link(todfix, 100);
669ef1f84bSDavid du Colombier }
679ef1f84bSDavid du Colombier 
689ef1f84bSDavid du Colombier /*
699ef1f84bSDavid du Colombier  *  calculate multiplier
709ef1f84bSDavid du Colombier  */
719ef1f84bSDavid du Colombier void
todsetfreq(vlong f)729ef1f84bSDavid du Colombier todsetfreq(vlong f)
739ef1f84bSDavid du Colombier {
74*406c76faSDavid du Colombier 	if (f <= 0)
75*406c76faSDavid du Colombier 		panic("todsetfreq: freq %lld <= 0", f);
769ef1f84bSDavid du Colombier 	ilock(&tod);
779ef1f84bSDavid du Colombier 	tod.hz = f;
789ef1f84bSDavid du Colombier 
799ef1f84bSDavid du Colombier 	/* calculate multiplier for time conversion */
809ef1f84bSDavid du Colombier 	tod.multiplier = mk64fract(TODFREQ, f);
819ef1f84bSDavid du Colombier 	tod.divider = mk64fract(f, TODFREQ) + 1;
829ef1f84bSDavid du Colombier 	tod.umultiplier = mk64fract(MicroFREQ, f);
839ef1f84bSDavid du Colombier 	tod.udivider = mk64fract(f, MicroFREQ) + 1;
849ef1f84bSDavid du Colombier 	iunlock(&tod);
859ef1f84bSDavid du Colombier }
869ef1f84bSDavid du Colombier 
879ef1f84bSDavid du Colombier /*
889ef1f84bSDavid du Colombier  *  Set the time of day struct
899ef1f84bSDavid du Colombier  */
909ef1f84bSDavid du Colombier void
todset(vlong t,vlong delta,int n)919ef1f84bSDavid du Colombier todset(vlong t, vlong delta, int n)
929ef1f84bSDavid du Colombier {
939ef1f84bSDavid du Colombier 	if(!tod.init)
949ef1f84bSDavid du Colombier 		todinit();
959ef1f84bSDavid du Colombier 
969ef1f84bSDavid du Colombier 	ilock(&tod);
979ef1f84bSDavid du Colombier 	if(t >= 0){
989ef1f84bSDavid du Colombier 		tod.off = t;
999ef1f84bSDavid du Colombier 		tod.last = fastticks(nil);
1009ef1f84bSDavid du Colombier 		tod.lasttime = 0;
1019ef1f84bSDavid du Colombier 		tod.delta = 0;
1029ef1f84bSDavid du Colombier 		tod.sstart = tod.send;
1039ef1f84bSDavid du Colombier 	} else {
1049ef1f84bSDavid du Colombier 		if(n <= 0)
1059ef1f84bSDavid du Colombier 			n = 1;
1069ef1f84bSDavid du Colombier 		n *= HZ;
1079ef1f84bSDavid du Colombier 		if(delta < 0 && n > -delta)
1089ef1f84bSDavid du Colombier 			n = -delta;
1099ef1f84bSDavid du Colombier 		if(delta > 0 && n > delta)
1109ef1f84bSDavid du Colombier 			n = delta;
111*406c76faSDavid du Colombier 		if (n == 0) {
112*406c76faSDavid du Colombier 			iprint("todset: n == 0, delta == %lld\n", delta);
113*406c76faSDavid du Colombier 			delta = 0;
114*406c76faSDavid du Colombier 		} else
115*406c76faSDavid du Colombier 			delta /= n;
1169ef1f84bSDavid du Colombier 		tod.sstart = sys->ticks;
1179ef1f84bSDavid du Colombier 		tod.send = tod.sstart + n;
1189ef1f84bSDavid du Colombier 		tod.delta = delta;
1199ef1f84bSDavid du Colombier 	}
1209ef1f84bSDavid du Colombier 	iunlock(&tod);
1219ef1f84bSDavid du Colombier }
1229ef1f84bSDavid du Colombier 
1239ef1f84bSDavid du Colombier /*
1249ef1f84bSDavid du Colombier  *  get time of day
1259ef1f84bSDavid du Colombier  */
1269ef1f84bSDavid du Colombier vlong
todget(vlong * ticksp)1279ef1f84bSDavid du Colombier todget(vlong *ticksp)
1289ef1f84bSDavid du Colombier {
1299ef1f84bSDavid du Colombier 	uvlong x;
1309ef1f84bSDavid du Colombier 	vlong ticks, diff;
1319ef1f84bSDavid du Colombier 	ulong t;
1329ef1f84bSDavid du Colombier 
1339ef1f84bSDavid du Colombier 	if(!tod.init)
1349ef1f84bSDavid du Colombier 		todinit();
1359ef1f84bSDavid du Colombier 
1369ef1f84bSDavid du Colombier 	/*
1379ef1f84bSDavid du Colombier 	 * we don't want time to pass twixt the measuring of fastticks
1389ef1f84bSDavid du Colombier 	 * and grabbing tod.last.  Also none of the vlongs are atomic so
1399ef1f84bSDavid du Colombier 	 * we have to look at them inside the lock.
1409ef1f84bSDavid du Colombier 	 */
1419ef1f84bSDavid du Colombier 	ilock(&tod);
1429ef1f84bSDavid du Colombier 	tod.cnt++;
1439ef1f84bSDavid du Colombier 	ticks = fastticks(nil);
1449ef1f84bSDavid du Colombier 
1459ef1f84bSDavid du Colombier 	/* add in correction */
1469ef1f84bSDavid du Colombier 	if(tod.sstart != tod.send){
1479ef1f84bSDavid du Colombier 		t = sys->ticks;
1489ef1f84bSDavid du Colombier 		if(t >= tod.send)
1499ef1f84bSDavid du Colombier 			t = tod.send;
1509ef1f84bSDavid du Colombier 		tod.off = tod.off + tod.delta*(t - tod.sstart);
1519ef1f84bSDavid du Colombier 		tod.sstart = t;
1529ef1f84bSDavid du Colombier 	}
1539ef1f84bSDavid du Colombier 
1549ef1f84bSDavid du Colombier 	/* convert to epoch */
1559ef1f84bSDavid du Colombier 	diff = ticks - tod.last;
1569ef1f84bSDavid du Colombier 	if(diff < 0)
1579ef1f84bSDavid du Colombier 		diff = 0;
1589ef1f84bSDavid du Colombier 	mul64fract(&x, diff, tod.multiplier);
1599ef1f84bSDavid du Colombier 	x += tod.off;
1609ef1f84bSDavid du Colombier 
1619ef1f84bSDavid du Colombier 	/* time can't go backwards */
1629ef1f84bSDavid du Colombier 	if(x < tod.lasttime)
1639ef1f84bSDavid du Colombier 		x = tod.lasttime;
1649ef1f84bSDavid du Colombier 	else
1659ef1f84bSDavid du Colombier 		tod.lasttime = x;
1669ef1f84bSDavid du Colombier 
1679ef1f84bSDavid du Colombier 	iunlock(&tod);
1689ef1f84bSDavid du Colombier 
1699ef1f84bSDavid du Colombier 	if(ticksp != nil)
1709ef1f84bSDavid du Colombier 		*ticksp = ticks;
1719ef1f84bSDavid du Colombier 
1729ef1f84bSDavid du Colombier 	return x;
1739ef1f84bSDavid du Colombier }
1749ef1f84bSDavid du Colombier 
1759ef1f84bSDavid du Colombier /*
1769ef1f84bSDavid du Colombier  *  convert time of day to ticks
1779ef1f84bSDavid du Colombier  */
1789ef1f84bSDavid du Colombier uvlong
tod2fastticks(vlong ns)1799ef1f84bSDavid du Colombier tod2fastticks(vlong ns)
1809ef1f84bSDavid du Colombier {
1819ef1f84bSDavid du Colombier 	uvlong x;
1829ef1f84bSDavid du Colombier 
1839ef1f84bSDavid du Colombier 	ilock(&tod);
1849ef1f84bSDavid du Colombier 	mul64fract(&x, ns-tod.off, tod.divider);
1859ef1f84bSDavid du Colombier 	x += tod.last;
1869ef1f84bSDavid du Colombier 	iunlock(&tod);
1879ef1f84bSDavid du Colombier 	return x;
1889ef1f84bSDavid du Colombier }
1899ef1f84bSDavid du Colombier 
1909ef1f84bSDavid du Colombier /*
1919ef1f84bSDavid du Colombier  *  called regularly to avoid calculation overflows
1929ef1f84bSDavid du Colombier  */
1939ef1f84bSDavid du Colombier static void
todfix(void)1949ef1f84bSDavid du Colombier todfix(void)
1959ef1f84bSDavid du Colombier {
1969ef1f84bSDavid du Colombier 	vlong ticks, diff;
1979ef1f84bSDavid du Colombier 	uvlong x;
1989ef1f84bSDavid du Colombier 
1999ef1f84bSDavid du Colombier 	ticks = fastticks(nil);
2009ef1f84bSDavid du Colombier 
2019ef1f84bSDavid du Colombier 	diff = ticks - tod.last;
2029ef1f84bSDavid du Colombier 	if(diff > tod.hz){
2039ef1f84bSDavid du Colombier 		ilock(&tod);
2049ef1f84bSDavid du Colombier 
2059ef1f84bSDavid du Colombier 		/* convert to epoch */
2069ef1f84bSDavid du Colombier 		mul64fract(&x, diff, tod.multiplier);
207*406c76faSDavid du Colombier if(x > 30000000000ULL) iprint("todfix %llud\n", x);
2089ef1f84bSDavid du Colombier 		x += tod.off;
2099ef1f84bSDavid du Colombier 
2109ef1f84bSDavid du Colombier 		/* protect against overflows */
2119ef1f84bSDavid du Colombier 		tod.last = ticks;
2129ef1f84bSDavid du Colombier 		tod.off = x;
2139ef1f84bSDavid du Colombier 
2149ef1f84bSDavid du Colombier 		iunlock(&tod);
2159ef1f84bSDavid du Colombier 	}
2169ef1f84bSDavid du Colombier }
2179ef1f84bSDavid du Colombier 
2189ef1f84bSDavid du Colombier long
seconds(void)2199ef1f84bSDavid du Colombier seconds(void)
2209ef1f84bSDavid du Colombier {
221*406c76faSDavid du Colombier 	return (vlong)todget(nil) / TODFREQ;
2229ef1f84bSDavid du Colombier }
2239ef1f84bSDavid du Colombier 
2249ef1f84bSDavid du Colombier uvlong
fastticks2us(uvlong ticks)2259ef1f84bSDavid du Colombier fastticks2us(uvlong ticks)
2269ef1f84bSDavid du Colombier {
2279ef1f84bSDavid du Colombier 	uvlong res;
2289ef1f84bSDavid du Colombier 
2299ef1f84bSDavid du Colombier 	if(!tod.init)
2309ef1f84bSDavid du Colombier 		todinit();
2319ef1f84bSDavid du Colombier 	mul64fract(&res, ticks, tod.umultiplier);
2329ef1f84bSDavid du Colombier 	return res;
2339ef1f84bSDavid du Colombier }
2349ef1f84bSDavid du Colombier 
2359ef1f84bSDavid du Colombier uvlong
us2fastticks(uvlong us)2369ef1f84bSDavid du Colombier us2fastticks(uvlong us)
2379ef1f84bSDavid du Colombier {
2389ef1f84bSDavid du Colombier 	uvlong res;
2399ef1f84bSDavid du Colombier 
2409ef1f84bSDavid du Colombier 	if(!tod.init)
2419ef1f84bSDavid du Colombier 		todinit();
2429ef1f84bSDavid du Colombier 	mul64fract(&res, us, tod.udivider);
2439ef1f84bSDavid du Colombier 	return res;
2449ef1f84bSDavid du Colombier }
2459ef1f84bSDavid du Colombier 
2469ef1f84bSDavid du Colombier /*
2479ef1f84bSDavid du Colombier  *  convert milliseconds to fast ticks
2489ef1f84bSDavid du Colombier  */
2499ef1f84bSDavid du Colombier uvlong
ms2fastticks(ulong ms)2509ef1f84bSDavid du Colombier ms2fastticks(ulong ms)
2519ef1f84bSDavid du Colombier {
2529ef1f84bSDavid du Colombier 	if(!tod.init)
2539ef1f84bSDavid du Colombier 		todinit();
2549ef1f84bSDavid du Colombier 	return (tod.hz*ms)/1000ULL;
2559ef1f84bSDavid du Colombier }
2569ef1f84bSDavid du Colombier 
2579ef1f84bSDavid du Colombier /*
2589ef1f84bSDavid du Colombier  *  convert nanoseconds to fast ticks
2599ef1f84bSDavid du Colombier  */
2609ef1f84bSDavid du Colombier uvlong
ns2fastticks(uvlong ns)2619ef1f84bSDavid du Colombier ns2fastticks(uvlong ns)
2629ef1f84bSDavid du Colombier {
2639ef1f84bSDavid du Colombier 	uvlong res;
2649ef1f84bSDavid du Colombier 
2659ef1f84bSDavid du Colombier 	if(!tod.init)
2669ef1f84bSDavid du Colombier 		todinit();
2679ef1f84bSDavid du Colombier 	mul64fract(&res, ns, tod.divider);
2689ef1f84bSDavid du Colombier 	return res;
2699ef1f84bSDavid du Colombier }
2709ef1f84bSDavid du Colombier 
2719ef1f84bSDavid du Colombier /*
2729ef1f84bSDavid du Colombier  *  convert fast ticks to ns
2739ef1f84bSDavid du Colombier  */
2749ef1f84bSDavid du Colombier uvlong
fastticks2ns(uvlong ticks)2759ef1f84bSDavid du Colombier fastticks2ns(uvlong ticks)
2769ef1f84bSDavid du Colombier {
2779ef1f84bSDavid du Colombier 	uvlong res;
2789ef1f84bSDavid du Colombier 
2799ef1f84bSDavid du Colombier 	if(!tod.init)
2809ef1f84bSDavid du Colombier 		todinit();
2819ef1f84bSDavid du Colombier 	mul64fract(&res, ticks, tod.multiplier);
2829ef1f84bSDavid du Colombier 	return res;
2839ef1f84bSDavid du Colombier }
2849ef1f84bSDavid du Colombier 
2859ef1f84bSDavid du Colombier /*
2869ef1f84bSDavid du Colombier  * Make a 64 bit fixed point number that has a decimal point
2879ef1f84bSDavid du Colombier  * to the left of the low order 32 bits.  This is used with
2889ef1f84bSDavid du Colombier  * mul64fract for converting twixt nanoseconds and fastticks.
2899ef1f84bSDavid du Colombier  *
2909ef1f84bSDavid du Colombier  *	multiplier = (to<<32)/from
2919ef1f84bSDavid du Colombier  */
2929ef1f84bSDavid du Colombier uvlong
mk64fract(uvlong to,uvlong from)2939ef1f84bSDavid du Colombier mk64fract(uvlong to, uvlong from)
2949ef1f84bSDavid du Colombier {
2959ef1f84bSDavid du Colombier /*
2969ef1f84bSDavid du Colombier 	int shift;
2979ef1f84bSDavid du Colombier 
2989ef1f84bSDavid du Colombier 	if(to == 0ULL)
2999ef1f84bSDavid du Colombier 		return 0ULL;
3009ef1f84bSDavid du Colombier 
3019ef1f84bSDavid du Colombier 	shift = 0;
3029ef1f84bSDavid du Colombier 	while(shift < 32 && to < (1ULL<<(32+24))){
3039ef1f84bSDavid du Colombier 		to <<= 8;
3049ef1f84bSDavid du Colombier 		shift += 8;
3059ef1f84bSDavid du Colombier 	}
3069ef1f84bSDavid du Colombier 	while(shift < 32 && to < (1ULL<<(32+31))){
3079ef1f84bSDavid du Colombier 		to <<= 1;
3089ef1f84bSDavid du Colombier 		shift += 1;
3099ef1f84bSDavid du Colombier 	}
3109ef1f84bSDavid du Colombier 
3119ef1f84bSDavid du Colombier 	return (to/from)<<(32-shift);
3129ef1f84bSDavid du Colombier  */
3139ef1f84bSDavid du Colombier 	return (to<<32) / from;
3149ef1f84bSDavid du Colombier }
315