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