Subject: Re: high-res timers
To: None <Chris_G_Demetriou@balvenie.pdl.cs.cmu.edu>
From: None <gdt@BBN.COM>
List: port-alpha
Date: 12/29/1995 08:48:29
Chris,

  We're in the process of getting NetBSD/Alpha running on AXPpci33s.
Being a time weenie, I'll be looking at cycle-counter based clocks.  I
implemented high-resolution timers in user space under IRIX, and I'll
take a look at your timer code as soon as time permits (probably
within a month or two, but no promises).

I'll summarize the way I did the IRIX code here in case you or anyone
else cares.   I can send patches to xntp3.4v for this code for anyone who
would like to test it out.  I'll be sending a slightly cleaned up patch
that is against the latest xntp3 to the ntp list fairly soon.

The points of the IRIX user-space cycle counter are

1) to get around outright bugs in the kernel timekeeping (non-issue for
   netbsd).

2) provide high-speed/resolution access to the clock from xntpd
3) allow users high-speed/resolution access to the same clock ntp uses

The implementation mapped the cycle counter into user space in the ntp
daemon.
The daemon maintained a user-space clock based on the cycle counter,
which defined time as

f(epoch-state, cycle counter)

where epoch-state consisted  of

(timeval, cycle-value, cycles, usec)
	
Every time xntpd needed time, it read the cycle counter and marched
timeval/cycle-value in the epoch-state forward as far as possible
without rounding (given the relationship of how many cycles equals how
many usec), just not marching forward the leftover parts.

Once per second, xntpd writes the epoch-state to shared memory.

User programs can map the counter and epoch-state and compute the
current time.  On a 200 Mhz R4000 this can be done 600K times per
second, which is faster than gettimeofday().

So, if you are doing something like the following in microtime

diff = newcycle - oldcycle
diff_usec = (diff / cycles) * usec

diff = (diff_usec /usec ) * cycles

time += diff_usec
oldcycle += diff

then that sounds great.
If there aren't an integer number of cycles per usec things get worse.
I actually have a (cycles, usec) pair which can be things like
(1000, 21). On a 174.4 Mhz sgi it can be (1000000, 45792).

With cycle/usec ratios that have sufficiently large denominators, one
should add code to round this when computing the current time (but not
change the epoch-state).

In addition to calls to provide time, it would probably be good to
provide calls to return a pointer to the counter, or return its value,
and also to export a cycles/usec pair.

As for interface, the .h file for users to compile against follows.

Executive summary: init call, gettimeofday replacement, and a
gettimespec call that uses the posix(?) timespec with ns instead of us.

#ifndef _libntpif_h_
#define _libntpif_h_
/*
 * $Id$
 *
 * libntpif.h
 * Copyright (c) 1995 BBN STD
 *
 * Gregory D. Troxel
 * 28 April 1995
 *
 * This file defines the interface to precise time as computed by NTP
 * optionally via the cycle counter.
 * The points of the scheme are
 *  1) have accurate time available (requires active synchronization)
 *  2) have low overhead of getting time (requires cycle counter)
 *  3) avoid OS bugs in maintaining system clock (requires cycle counter)
 *  4) fall back to gettimeofday()/etc. if NTP daemon not running
 */

/*
 * These structures are the return value of the routine to read time.
 *
 * This MUST be the same as the struct timeval used by the NTP daemon.
 */
typedef struct ntpif_timeval_s
{
  long tv_sec;
  long tv_usec;
} NTPIF_TIMEVAL;

/*
 * POSIX.4-style time structure.
 * For now, may be simple 'nsec = 1000 * usec'.
 */
typedef struct ntpif_timespec_s
{
  long sec;
  long long nsec;
} NTPIF_TIMESPEC;

/*
 * Call to initialize ntpif timing.
 * This routine may be called any number of times.
 * If the link to the daemon does not exist, it attempts to create it.
 * Then, if the link is up, a sanity check is performed.
 * If the sanity check fails, the link is declared down.
 * The return value indicates the current status.
 * This routine should execute fairly quickly, but there are no
 * hard realtime constraints.
 */
typedef enum
{
    NTPIF_INIT_OK =		0, /* all is well */
    NTPIF_INIT_CYCLE =		1, /* couldn't map cycle counter */
    NTPIF_INIT_NTP =		2, /* couldn't map NTP segment */
    NTPIF_INIT_SANITY =		3, /* sanity check ntp/cycle failed */
    NTPIF_INIT_GENERIC =	9 /* something else wrong */
} NTPIF_INIT_ENUM;
extern NTPIF_INIT_ENUM ntpif_init();

typedef enum
{
    NTPIF_GET_OK =		0, /* all is well; value is NTP-derived */
    NTPIF_GET_CYCLE =		1, /* fell back to tick counter */
    NTPIF_GET_FALLBACK =	2  /* fell back to gettimeofday() */
} NTPIF_GET_ENUM;

/*
 * Fill in *tvp or *tsp with the current time.
 * Return values are NTPIF_GET_*
 */
extern NTPIF_GET_ENUM  ntpif_get_timeval( /* NTPIF_TIMEVAL *tvp */ );
extern NTPIF_GET_ENUM ntpif_get_timespec( /* NTPIF_TIMESPEC *tsp */ );

#endif /* _libntpif_h_ */


        Greg Troxel <gdt@bbn.com>  +1 617 873 2494