## diffname port/tod.c 1999/0219
## diff -e /dev/null /n/emeliedump/1999/0219/sys/src/brazil/port/tod.c
0a
#include "u.h"
#include "../port/lib.h"
#include "mem.h"
#include "dat.h"
#include "fns.h"
#include "../port/error.h"
// frequency of the tod clock
#define TODFREQ 1000000000LL
enum
{
Log2mult= 22, // multiplier should have 22 bits
Log2todfreq= 30, // number of significant bits of TODFREQ
};
static vlong logtab[40];
struct {
Lock;
int shift; // time = (ticks*multiplier)>>shift
vlong multiplier; // ...
vlong hz; // frequency of fast clock
vlong last; // last reading of fast clock
vlong off; // offset from epoch to last
vlong lasttime; // last return value from gettod
vlong delta; // amount to add to bias each slow clock tick
int n; // number of times to add in delta
int i; // number of times we've added in delta
} tod;
int
log2(vlong x)
{
int i;
for(i = 0; i < nelem(logtab); i++){
if(x < logtab[i])
break;
}
return i+8;
}
void
todinit(void)
{
vlong v;
int i;
v = 1LL<<8;
for(i = 0; i < nelem(logtab); i++){
logtab[i] = v;
v <<= 1;
}
fastticks((uvlong*)&tod.hz);
todsetfreq(tod.hz);
addclock0link(todfix);
}
//
// This routine makes sure that the multiplier has
// at least Log2mult bits to guarantee that precision.
//
void
todsetfreq(vlong f)
{
// this ensures that the multiplier has 22 bits
ilock(&tod);
tod.hz = f;
tod.shift = Log2mult - Log2todfreq + log2(f);
tod.multiplier = (TODFREQ<<tod.shift)/f;
iunlock(&tod);
}
//
// Set the time of day struct
//
void
todset(vlong t, vlong delta, int n)
{
ilock(&tod);
tod.i = tod.n = 0;
if(t >= 0){
tod.off = t;
tod.last = fastticks(nil);
tod.lasttime = 0;
} else {
tod.delta = delta;
tod.n = n;
}
iunlock(&tod);
}
//
// get time of day
//
vlong
todget(void)
{
vlong ticks, x, diff;
ilock(&tod);
if(tod.hz == 0)
ticks = fastticks((uvlong*)&tod.hz);
else
ticks = fastticks(nil);
diff = ticks - tod.last;
// convert to epoch
x = (diff*tod.multiplier)>>tod.shift;
x += tod.off;
// protect against overflows
if(diff > (1LL<<(63-Log2mult))){
tod.last = ticks;
tod.off = x;
}
/* time can't go backwards */
if(x < tod.lasttime)
x = tod.lasttime;
tod.lasttime = x;
iunlock(&tod);
return x;
}
//
// called every clock tick
//
void
todfix(void)
{
if((MACHP(0)->ticks % HZ) != 0)
return;
ilock(&tod);
if(tod.n > tod.i++)
tod.off += tod.delta;
iunlock(&tod);
}
long
seconds(void)
{
vlong x;
int i;
x = todget();
x /= TODFREQ;
i = x;
return i;
}
.
## diffname port/tod.c 1999/0225
## diff -e /n/emeliedump/1999/0219/sys/src/brazil/port/tod.c /n/emeliedump/1999/0225/sys/src/brazil/port/tod.c
141a
// once a minute, make sure we don't overflow
if((MACHP(0)->ticks % (60*HZ)) == 0)
todget();
.
137a
// once a second apply correction
.
116c
if(diff > tod.maxdiff){
.
112c
x = ((diff>>tod.s2)*tod.multiplier)>>(tod.s1-tod.s2);
.
73a
//print("hz %lld mult %lld s1 %d s2 %d maxdiff %lld\n", tod.hz, tod.multiplier, tod.s1, tod.s2, tod.maxdiff);
.
71,72c
lf = log2(f);
tod.s2 = lf - 25;
if(tod.s2 < 0)
tod.s2 = 0;
tod.s1 = 2*lf - tod.s2 - 30;
if(tod.s1 < 0)
tod.s1 = 0;
if(tod.s1 > 33)
tod.s1 = 33;
tod.multiplier = (TODFREQ<<tod.s1)/f;
tod.maxdiff = 1LL<<(10 + lf);
.
67a
int lf;
.
23a
int s2; // ...
vlong maxdiff; // max diff between ticks and last to avoid overflow
.
22c
int s1; // time = ((ticks>>s2)*multiplier)>>(s1-s2)
.
12,17d
7a
// compute nanosecond epoch time from the fastest ticking clock
// on the system. converting the time to nanoseconds requires
// the following formula
//
// t = (((1000000000<<s1)/f)*(ticks>>s2))>>(s1-s2)
//
// where
//
// 'f' is the clock frequency
// 'ticks' are clock ticks
// 's1' and 's2' are shift ammounts to avoid 64 bit
// overflows in the calculations
//
// to avoid too much calculation in gettod(), we calculate
//
// mult = (1000000000<<s1)/f
//
// each time f is set. f is normally set by a user level
// program writing to /dev/fastclock.
//
// To calculate s1 and s2, we have to avoid overflowing our
// signed 64 bit calculations. Also we wish to accomodate
// 15 minutes of ticks. This gives us the following
// constraints:
//
// 1) log2(1000000000<<s1) <= 63
// or s1 <= 33
// 2) accomodate 15 minutes of ticks without overflow
// or log2(((1000000000<<s1)/f)*((15*60*f)>>s2)) <= 63
// or log2(mult) + 12 + log2(f) - s2 <= 63
// or log2(mult) + log2(f) - 51 <= s2
//
// by definition
//
// 3) log2(mult) = log2(1000000000) + s1 - log2(f)
// or log2(mult) = 30 + s1 - log2(f)
//
// To balance the accuracy of the multiplier and the sampled
// ticks we set
//
// 4) log2(mult) = log2(f>>s2)
// or log2(mult) = log2(f) - s2
//
// Combining 2) and 4) we get
//
// 5) log2(f) - s2 + log2(f) - 51 <= s2
// or 2*log2(f) - 51 <= 2*s2
// or log2(f) - 25 <= s2
//
// Combining 3) and 4)
//
// 6) 30 + s1 - log2(f) = log2(f) - s2
// or s1 = 2*log2(f) - s2 - 30
//
// Since shifting ticks left doesn't increase accuracy, and
// shifting 1000000000 right loses accuracy
//
// 7) s2 >= 0
// 8) s1 >= 0
//
// As an example, that gives us the following
//
// for f = 100, log2(f) = 7
//
// s2 = 0
// s1 = 0
//
// for f = 267000000, log2(f) = 28
//
// s2 = 3
// s1 = 23
//
// for f = 2000000000, log2(f) = 31
//
// s2 = 6
// s1 = 26
//
// for f = 8000000000, log2(f) = 33
//
// s2 = 8
// s1 = 28
.
## diffname port/tod.c 1999/0227
## diff -e /n/emeliedump/1999/0225/sys/src/brazil/port/tod.c /n/emeliedump/1999/0227/sys/src/brazil/port/tod.c
225,233d
199a
// add in correction
if(tod.sstart < tod.send){
t = MACHP(0)->ticks;
if(t >= tod.send)
t = tod.send;
tod.off += tod.delta*(t - tod.sstart);
tod.sstart = t;
}
.
190a
ulong t;
.
179d
177a
if(n <= 0)
n = 1;
n *= HZ;
if(delta < 0 && n > -delta)
n = -delta;
if(delta > 0 && n > delta)
n = delta;
delta /= n;
tod.sstart = MACHP(0)->ticks;
tod.send = tod.sstart + n;
.
172c
tod.sstart = tod.send = 0;
.
162d
105,107c
vlong delta; // add 'delta' each slow clock tick from sstart to send
ulong sstart; // ...
ulong send; // ...
.
## diffname port/tod.c 1999/0421
## diff -e /n/emeliedump/1999/0227/sys/src/brazil/port/tod.c /n/emeliedump/1999/0421/sys/src/brazil/port/tod.c
222,226c
// protect against overflows (gettod is called at least once a second)
tod.last = ticks;
tod.off = x;
.
160d
157,158c
if(tod.s1 > 32)
tod.s1 = 32;
.
151c
tod.s2 = lf - 28;
.
100d
87,88c
// s2 = 5
// s1 = 31
.
82,83c
// s2 = 3
// s1 = 29
.
77,78c
// s2 = 0
// s1 = 26
.
53,55c
// 5) log2(f) - s2 + log2(f) - 57 <= s2
// or 2*log2(f) - 57 <= 2*s2
// or log2(f) - 28 <= s2
.
34,38c
// or s1 <= 32
// 2) accomodate 1 minute of ticks without overflow
// or log2(((1000000000<<s1)/f)*((60*f)>>s2)) <= 63
// or log2(mult) + 6 + log2(f) - s2 <= 63
// or log2(mult) + log2(f) - 57 <= s2
.
## diffname port/tod.c 1999/0508
## diff -e /n/emeliedump/1999/0421/sys/src/brazil/port/tod.c /n/emeliedump/1999/0508/sys/src/brazil/port/tod.c
239,240c
// once a second, make sure we don't overflow
if((MACHP(0)->ticks % HZ) == 0)
.
224c
// time can't go backwards
.
216,217c
// convert to epoch, make sure calculation is unsigned
x = (((uvlong)diff) * ((uvlong)tod.multiplier)) >> 31;
.
196c
uvlong x;
vlong ticks, diff;
.
149,158c
tod.multiplier = (TODFREQ<<31)/f;
.
146c
// the shift is an attempt to maintain precision
// during the caculations. the number of bits in
// the multiplier should be log(TODFREQ) + 31 - log(f).
//
// Freq bits
// 167 MHZ 34
// 267 MHZ 33
// 500 MHZ 32
//
// in all cases, we need to call todget() at least once
// a second to keep the subsequent calculations from
// overflowing.
.
138,139c
// calculate multiplier
.
124,131d
109,120d
93,94d
89a
.
27,88d
23c
// mult = (1000000000<<31)/f
.
18,19d
12c
// t = (((1000000000<<31)/f)*ticks)>>31
.
## diffname port/tod.c 1999/0509
## diff -e /n/emeliedump/1999/0508/sys/src/brazil/port/tod.c /n/emeliedump/1999/0509/sys/src/brazil/port/tod.c
58,59d
## diffname port/tod.c 1999/0517
## diff -e /n/emeliedump/1999/0509/sys/src/brazil/port/tod.c /n/emeliedump/1999/0517/sys/src/brazil/port/tod.c
157a
}
.
156c
if(MACHP(0)->ticks - last >= HZ){
last = MACHP(0)->ticks;
.
154a
static ulong last;
.
136,138c
// protect against overflows
if(diff > tod.hz){
tod.last = ticks;
tod.off = x;
}
.
132,133c
// convert to epoch
x = (diff * tod.multiplier) >> 31;
.
124c
if(tod.sstart != tod.send){
.
88a
tod.delta = 0;
tod.sstart = tod.send;
.
84d
58,70d
32,34c
vlong multiplier; // t = off + (multiplier*ticks)>>31
.
## diffname port/tod.c 1999/0531
## diff -e /n/emeliedump/1999/0517/sys/src/brazil/port/tod.c /n/emeliedump/1999/0531/sys/src/brazil/port/tod.c
158c
x = todget(nil);
.
148c
todget(nil);
.
132a
if(ticksp != nil)
*ticksp = ticks;
.
95c
todget(vlong *ticksp)
.
## diffname port/tod.c 1999/0806
## diff -e /n/emeliedump/1999/0531/sys/src/brazil/port/tod.c /n/emeliedump/1999/0806/sys/src/brazil/port/tod.c
162c
x = x/TODFREQ;
.
120c
x = x + tod.off;
.
114c
tod.off = tod.off + tod.delta*(t - tod.sstart);
.
83c
delta = delta/n;
.
## diffname port/tod.c 1999/0810
## diff -e /n/emeliedump/1999/0806/sys/src/brazil/port/tod.c /n/emeliedump/1999/0810/sys/src/brazil/port/tod.c
136d
131c
else
tod.lasttime = x;
.
122,125c
if(m->machno == 0){
ilock(&tod);
// add in correction
if(tod.sstart != tod.send){
t = MACHP(0)->ticks;
if(t >= tod.send)
t = tod.send;
tod.off = tod.off + tod.delta*(t - tod.sstart);
tod.sstart = t;
}
// protect against overflows
if(diff > tod.hz){
tod.last = ticks;
tod.off = x;
}
iunlock(&tod);
.
109,117d
101,102d
## diffname port/tod.c 2000/0621
## diff -e /n/emeliedump/1999/0810/sys/src/brazil/port/tod.c /n/emeliedump/2000/0621/sys/src/9/port/tod.c
155d
151,153c
ticks = fastticks(nil);
diff = ticks - tod.last;
if(diff > tod.hz)
.
149c
vlong ticks, diff;
.
144c
// called every clock tick to avoid calculation overflows
.
## diffname port/tod.c 2000/0625
## diff -e /n/emeliedump/2000/0621/sys/src/9/port/tod.c /n/emeliedump/2000/0625/sys/src/9/port/tod.c
135a
iunlock(&tod);
.
128d
112d
104a
/* since 64 bit ops are not atomix, we have to lock around them */
ilock(&tod);
.
## diffname port/tod.c 2000/0626
## diff -e /n/emeliedump/2000/0625/sys/src/9/port/tod.c /n/emeliedump/2000/0626/sys/src/9/port/tod.c
156,157c
if(diff > tod.hz){
ilock(&tod);
// convert to epoch
diff = ticks - tod.last;
x = (diff * tod.multiplier) >> 31;
x = x + tod.off;
// protect against overflows
tod.last = ticks;
tod.off = x;
iunlock(&tod);
}
.
151a
uvlong x;
.
136a
.
114,131d
110a
diff = ticks - tod.last;
.
109a
// add in correction
if(tod.sstart != tod.send){
t = MACHP(0)->ticks;
if(t >= tod.send)
t = tod.send;
tod.off = tod.off + tod.delta*(t - tod.sstart);
tod.sstart = t;
}
.
108c
tod.cnt++;
.
106c
// since 64 bit loads are not atomic, we have to lock around them
.
30a
ulong cnt;
.
24a
//
// We assume that the cpu's of a multiprocessor are synchronized.
// This assumption needs to be questioned with each new architecture.
.
## diffname port/tod.c 2000/1215
## diff -e /n/emeliedump/2000/0626/sys/src/9/port/tod.c /n/emeliedump/2000/1215/sys/src/9/port/tod.c
158,159c
x = diff * tod.multiplier;
x = x >> 31;
.
49c
ilock(&tod);
tod.last = fastticks((uvlong*)&tod.hz);
iunlock(&tod);
.
40c
vlong lasttime; // last return value from todget
.
19c
// to avoid too much calculation in todget(), we calculate
.
## diffname port/tod.c 2002/0410
## diff -e /n/emeliedump/2000/1215/sys/src/9/port/tod.c /n/emeliedump/2002/0410/sys/src/9/port/tod.c
181a
}
// convert milliseconds to fast ticks
//
uvlong
ms2fastticks(ulong ms)
{
if(tod.hz == 0)
fastticks((uvlong*)&tod.hz);
return (tod.hz*ms)/1000ULL;
.
64a
.
63a
/* calculate multiplier for time conversion */
.
31c
#define TODFREQ 1000000000ULL
.
## diffname port/tod.c 2002/0411
## diff -e /n/emeliedump/2002/0410/sys/src/9/port/tod.c /n/emeliedump/2002/0411/sys/src/9/port/tod.c
41,43c
vlong delta; // add 'delta' each slow clock tick from sstart to send
ulong sstart; // ...
ulong send; // ...
.
## diffname port/tod.c 2002/0928
## diff -e /n/emeliedump/2002/0411/sys/src/9/port/tod.c /n/emeliedump/2002/0928/sys/src/9/port/tod.c
148c
// called regularly to avoid calculation overflows
.
53c
addclock0link(todfix, 100);
.
|