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