From: ciemo@bananapc.csd.sgi.com (Dave Ciemiewicz)
Subject: Re: How Accurate Is The System Clock
Organization: Silicon Graphics, Customer Support Division
Distribution: comp
Date: Wed, 23 Dec 1992 23:29:26 GMT

|> In <AJ3U.92Dec22153748@larch.cs.virginia.edu> aj3u@larch.cs.virginia.edu (Asim Jalis) writes:
|> 
|> *I am writing some programs on the SGI that requires millisecond
|> *timing accuracy.  How accurate is the clock on these machines?
|> *with the daemons and the multi-process environment.  If someone
|> *has references for timing measurements or some ideas about how
|> *such timing can be achieved, I would really appreciate hearing
|> *from them.
|> *
|> *Responses can be communicated to me through e-mail.
|> *
|> *
|> *
|> *Asim Jalis.

If you are on a system with an IO3 Power Channel, you can use the cycle
counter from there for counting 62.5 nanosecond ticks to get rather
accurate timings.  The following illustrates the use of this fast clock
for timing purposes.

/*
**  NAME
**      cyclecntr.c - syssgi(SGI_QUERY_CYCLECNTR) example which reads IO3 counter
**
**  DESCRIPTION
**      The following program reads the Power Channel (IO3 ethernet controller)
**      cycle counter.  This counter increments once every 62.5 nanoseconds
**      (actual interval is returned as number of picoseconds via arg1 of
**      syssgi(SGI_QUERY_CYCLECNTR, arg1) call).  The counter is read by using
**      mmap(2) to make the counter accessible in the programs address space.
**
**      The cycle counter on the IO3 board wraps-around to 0 every
**      0xffffffff * 62.5ns = 268s.  This example takes this into account.
**
**  COMPILATION INSTRUCTIONS
**      cc -o cyclecntr cyclecntr.c
**
**  CAVEATS
**      1) This only works on systems with the IO3 subsystem.  On 3.3.x
**         systems, using hinv(1) will return the information as:
**
**              Integral Ethernet controller: Version 3
**
**         On 4.0 systems, hinv(1) will report:
**
**              Integral Ethernet: et0 IO3
**
**
**  AUTHOR
**      Dave Ciemiewicz (ciemo)
**      Silicon Graphics Technical Assistance Center
*/

#include <sys/types.h>
#include <sys/syssgi.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <limits.h>

volatile unsigned int* counter;         /* Pointer to IO3 counter.  Must be */
                                        /* volatile so compiler doesn't */
                                        /* optmized away */

#define MICROSECS_PER_PICOSEC 1.0e-6

#define ITERATIONS 100

unsigned int interval[ITERATIONS];


main()
{
    unsigned long counterTickPicoSecs;
    unsigned int* counterAddr;
    int mmemfd;
    int i;
    unsigned int previousCounter;
    unsigned int currentCounter;

    /*
    ** Establish connection to read Power Channel (IO3) counter
    */
    counterAddr = (unsigned*)syssgi(SGI_QUERY_CYCLECNTR, &counterTickPicoSecs);
    
    if (counterAddr == (unsigned*)-1)
    {
        perror("syssgi SGI_QUERY_CYCLECNTR");
        exit(1);
    }

    if ((mmemfd = open("/dev/mmem", O_RDONLY, 0)) == -1)
    {
        perror("open /dev/mmem");
        exit(1);
    }

    counter = mmap(0, sizeof(*counter), PROT_READ, MAP_PRIVATE, mmemfd,
                (unsigned)counterAddr);
    if (counter == (unsigned int*)-1)
    {
        perror("mmap counter");
        exit(1);
    }

    /*
    **  Try reading counter
    */
    previousCounter = 0;
    for (i=0; i<=ITERATIONS; i++)
    {
        currentCounter = *counter;      /* reads current value from mmapped */
                                        /* IO3 board counter */
        if (i != 0) {
             /*
             **  Compute time interval and take into account counter
             **  wraparound.
             */
             interval[i] = (currentCounter >= previousCounter) ?
                     currentCounter - previousCounter :
                     currentCounter + UINT_MAX - previousCounter;
        }

        previousCounter = currentCounter;
    }


    /*
    **  Print results.  Results are not printed in loop because they increase
    **  interval.
    */
    for (i=1; i<=ITERATIONS; i++)
    {
        printf("Interval %d: ticks %d, time %.4f microsecs\n", i, interval[i],
                 interval[i] * counterTickPicoSecs * MICROSECS_PER_PICOSEC);
    }


    /*
    ** Close down and clean up
    */

    if (munmap((void*)counter, sizeof(*counter)) == -1)
    {
        perror("munmap");
        exit(1);
    }

    if (close(mmemfd) == -1)
    {
        perror("close /dev/mmem");
        exit(1);
    }
}