xref: /inferno-os/os/port/tod.c (revision 7ef44d652ae9e5e1f5b3465d73684e4a54de73c0)
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 /* 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 
35 struct {
36 	int		init;		// true if initialized
37 	ulong	cnt;
38 	Lock;
39 	uvlong	multiplier;	// t = off + (multiplier*ticks)>>31
40 	uvlong	divider;	// ticks = (divider*(ticks-off))>>31
41 	vlong	hz;		// frequency of fast clock
42 	vlong	last;		// last reading of fast clock
43 	vlong	off;		// offset from epoch to last
44 	vlong	lasttime;	// last return value from todget
45 	vlong	delta;		// add 'delta' each slow clock tick from sstart to send
46 	ulong	sstart;		// ...
47 	ulong	send;		// ...
48 } tod;
49 
50 void
51 todinit(void)
52 {
53 	if(tod.init)
54 		return;
55 	ilock(&tod);
56 	tod.last = fastticks((uvlong*)&tod.hz);
57 	iunlock(&tod);
58 	todsetfreq(tod.hz);
59 	tod.init = 1;
60 	addclock0link(todfix, 100);
61 }
62 
63 /*
64  *  calculate multiplier
65  */
66 void
67 todsetfreq(vlong f)
68 {
69 	ilock(&tod);
70 	tod.hz = f;
71 
72 	/* calculate multiplier for time conversion */
73 	tod.multiplier = mk64fract(TODFREQ, f);
74 	tod.divider = mk64fract(f, TODFREQ);
75 
76 	iunlock(&tod);
77 }
78 
79 /*
80  *  Set the time of day struct
81  */
82 void
83 todset(vlong t, vlong delta, int n)
84 {
85 	if(!tod.init)
86 		todinit();
87 
88 	ilock(&tod);
89 	if(t >= 0){
90 		tod.off = t;
91 		tod.last = fastticks(nil);
92 		tod.lasttime = 0;
93 		tod.delta = 0;
94 		tod.sstart = tod.send;
95 	} else {
96 		if(n <= 0)
97 			n = 1;
98 		n *= HZ;
99 		if(delta < 0 && n > -delta)
100 			n = -delta;
101 		if(delta > 0 && n > delta)
102 			n = delta;
103 		delta = delta/n;
104 		tod.sstart = MACHP(0)->ticks;
105 		tod.send = tod.sstart + n;
106 		tod.delta = delta;
107 	}
108 	iunlock(&tod);
109 }
110 
111 /*
112  *  get time of day
113  */
114 vlong
115 todget(vlong *ticksp)
116 {
117 	uvlong x;
118 	vlong ticks, diff;
119 	ulong t;
120 
121 	if(!tod.init)
122 		todinit();
123 
124 	// we don't want time to pass twixt the measuring of fastticks
125 	// and grabbing tod.last.  Also none of the vlongs are atomic so
126 	// we have to look at them inside the lock.
127 	ilock(&tod);
128 	tod.cnt++;
129 	ticks = fastticks(nil);
130 
131 	// add in correction
132 	if(tod.sstart != tod.send){
133 		t = MACHP(0)->ticks;
134 		if(t >= tod.send)
135 			t = tod.send;
136 		tod.off = tod.off + tod.delta*(t - tod.sstart);
137 		tod.sstart = t;
138 	}
139 
140 	// convert to epoch
141 	diff = ticks - tod.last;
142 	if(diff < 0)
143 		diff = 0;
144 	mul64fract(&x, diff, tod.multiplier);
145 	x += tod.off;
146 
147 	// time can't go backwards
148 	if(x < tod.lasttime)
149 		x = tod.lasttime;
150 	else
151 		tod.lasttime = x;
152 
153 	iunlock(&tod);
154 
155 	if(ticksp != nil)
156 		*ticksp = ticks;
157 
158 	return x;
159 }
160 
161 /*
162  *  convert time of day to ticks
163  */
164 uvlong
165 tod2fastticks(vlong ns)
166 {
167 	uvlong x;
168 
169 	ilock(&tod);
170 	mul64fract(&x, ns-tod.off, tod.divider);
171 	x += tod.last;
172 	iunlock(&tod);
173 	return x;
174 }
175 
176 /*
177  *  called regularly to avoid calculation overflows
178  */
179 void
180 todfix(void)
181 {
182 	vlong ticks, diff;
183 	uvlong x;
184 
185 	ticks = fastticks(nil);
186 
187 	diff = ticks - tod.last;
188 	if(diff > tod.hz){
189 		ilock(&tod);
190 
191 		// convert to epoch
192 		mul64fract(&x, diff, tod.multiplier);
193 if(x > 30000000000ULL) print("todfix %llud\n", x);
194 		x += tod.off;
195 
196 		// protect against overflows
197 		tod.last = ticks;
198 		tod.off = x;
199 
200 		iunlock(&tod);
201 	}
202 }
203 
204 long
205 tseconds(void)
206 {
207 	vlong x;
208 	int i;
209 
210 	x = todget(nil);
211 	x = x/TODFREQ;
212 	i = x;
213 	return i;
214 }
215 
216 //  convert milliseconds to fast ticks
217 //
218 uvlong
219 ms2fastticks(ulong ms)
220 {
221 	if(!tod.init)
222 		todinit();
223 	return (tod.hz*ms)/1000ULL;
224 }
225 
226 /*
227  *  convert nanoseconds to fast ticks
228  */
229 uvlong
230 ns2fastticks(uvlong ns)
231 {
232 	uvlong res;
233 
234 	if(!tod.init)
235 		todinit();
236 	mul64fract(&res, ns, tod.divider);
237 	return res;
238 }
239 
240 /*
241  *  convert fast ticks to ns
242  */
243 uvlong
244 fastticks2ns(uvlong ticks)
245 {
246 	uvlong res;
247 
248 	if(!tod.init)
249 		todinit();
250 	mul64fract(&res, ticks, tod.multiplier);
251 	return res;
252 }
253 
254 /*
255  * Make a 64 bit fixed point number that has a decimal point
256  * to the left of the low order 32 bits.  This is used with
257  * mul64fract for converting twixt nanoseconds and fastticks.
258  *
259  *	multiplier = (to<<32)/from
260  */
261 uvlong
262 mk64fract(uvlong to, uvlong from)
263 {
264 /*
265 	int shift;
266 
267 	if(to == 0ULL)
268 		return 0ULL;
269 
270 	shift = 0;
271 	while(shift < 32 && to < (1ULL<<(32+24))){
272 		to <<= 8;
273 		shift += 8;
274 	}
275 	while(shift < 32 && to < (1ULL<<(32+31))){
276 		to <<= 1;
277 		shift += 1;
278 	}
279 
280 	return (to/from)<<(32-shift);
281 */
282 	return (to<<32)/from;
283 }
284