9fans - fans of the OS Plan 9 from Bell Labs
 help / color / mirror / Atom feed
From: "Russ Cox" <rsc@plan9.bell-labs.com>
To: 9fans@cse.psu.edu
Subject: Re: [9fans] A quite amusing thing...
Date: Sat, 22 Feb 2003 15:30:42 -0500	[thread overview]
Message-ID: <6befec06592023a83a26e343d54335b2@plan9.bell-labs.com> (raw)
In-Reply-To: <000601c2daa7$17972450$270363d9@makr4j0ty5i9an>

That _is_ fairly amusing.

We've had a form of that code (including the big
comment) in our source tree since July 15, 1994.
But the 1994 code didn't have the for loop to run
until loops got big enough (it assumed 10000 was
enough) and it didn't have all the clock frequency
stuff (which got added later).

So either Plan 9 copied Amoeba back in 1994 (before
the original project was terminated) and has been
secretly tracking the code since then, or Amoeba copied
Plan 9 some time later after the code was changed to
look more like it does today.  I know where my money is.

Also, I downloaded the original Amoeba code and the
pit.c looked nothing like the Plan 9 code as of 1996.

Below you'll find Plan 9's clock.c from 1994, Amoeba's
pit.c from 1996, and Amoeba's pit.c from today.
Note the difference between the last two.  Sure looks like

 * Stefan Bosse (12/1999-7/2000)
 * sbosse@physik.uni-bremen.de
 *
 * -> pit_hw_milli now default routine with 1ms resolution
 * -> cpuspeed measurement and delay loop reference

copied the Plan 9 code.

> Hmm... It is possible too that Amoeba developer's have copied this code
> but I doubt it.

Why?

Russ

/* --rw-rw-r-- M 3014 jmk sys 2766 Jul 15  1994 sys/src/brazil/pc/clock.c */

void
clockinit(void)
{
	ulong x, y;	/* change in counter */
	ulong cycles, loops;

	/*
	 *  set vector for clock interrupts
	 */
	setvec(Clockvec, clock, 0);

	/*
	 *  set clock for 1/HZ seconds
	 */
	outb(Tmode, Load0|Square);
	outb(T0cntr, (Freq/HZ));	/* low byte */
	outb(T0cntr, (Freq/HZ)>>8);	/* high byte */

	/*
	 *  measure time for the loop
	 *
	 *			MOVL	loops,CX
	 *	aaml1:	 	AAM
	 *			LOOP	aaml1
	 *
	 *  the time for the loop should be independent from external
	 *  cache's and memory system since it fits in the execution
	 *  prefetch buffer.
	 *
	 */
	loops = 10000;
	outb(Tmode, Latch0);
	x = inb(T0cntr);
	x |= inb(T0cntr)<<8;
	aamloop(loops);
	outb(Tmode, Latch0);
	y = inb(T0cntr);
	y |= inb(T0cntr)<<8;
	x -= y;

	/*
	 *  counter  goes at twice the frequency, once per transition,
	 *  i.e., twice per the square wave
	 */
	x >>= 1;

	/*
 	 *  figure out clock frequency and a loop multiplier for delay().
	 */
	switch(cputype = x86()){
	case 386:
		cycles = 30;
		break;
	case 486:
		cycles = 24;
		break;
	default:
		cycles = 23;
		break;
	}
	cpufreq = (cycles*loops) * (Freq/x);
	loopconst = (cpufreq/1000)/cycles;	/* AAM+LOOP's for 1 ms */
}


/*	@(#)pit.c	1.4	94/04/06 09:23:12 */
/*
 * Copyright 1994 Vrije Universiteit, The Netherlands.
 * For full copyright and restrictions on use see the file COPYRIGHT in the
 * top level of the Amoeba distribution.
 */

/*
 * pit.c
 *
 * Driver for the 8254 timer (PIT). The i8254 timer has three timer channels
 * of which only one (channel 0) is available for timer interrupts. The other
 * channels are used for the speaker and memory refresh.
 *
 * Author:
 *	Leendert van Doorn
 */
#include <amoeba.h>
#include <assert.h>
INIT_ASSERT
#include <fault.h>
#include <bool.h>
#include "sys/proto.h"
#include "i386_proto.h"
#include "pit.h"

#ifndef PIT_DEBUG
#define	PIT_DEBUG	0
#endif

static int pit_debug;			/* current debug level */

#ifdef notyet
static uint32 pit_hw_milli();
#endif
static void pit_intr();

/*
 * Initialize channel 0 of the i8254A timer
 */
void
pit_init()
{
    register uint32 counter = (PIT_FREQ * PIT_INTERVAL) / 1000L;

#ifndef NDEBUG
    if ((pit_debug = kernel_option("pit")) == 0)
	pit_debug = PIT_DEBUG;
    if (pit_debug > 1)
	printf("pit_init(), pit_interval = 0x%x (%d), counter = %x\n",
	    PIT_INTERVAL, PIT_INTERVAL, counter);
#endif

    /* set timer to run continuously */
    assert(counter <= 0xFFFF);
    out_byte(PIT_MODE, PIT_MODE3);
    out_byte(PIT_CH0, (int) (counter & 0xFF));
    out_byte(PIT_CH0, (int) ((counter >> 8) & 0xFF));

#ifdef notyet
    /* I still have to thoroughly test this */
    set_hw_milli(pit_hw_milli);
#endif

    setirq(PIT_IRQ, pit_intr);
    pic_enable(PIT_IRQ);
}

/*
 * Read i8254's channel 0 counter. The counter decrements at twice the
 * timer frequency (one full cycle for each half of a square wave).
 */
uint16
pit_channel0()
{
    register uint16 counter;

    out_byte(PIT_MODE, PIT_LC);
    counter = in_byte(PIT_CH0), counter |= (in_byte(PIT_CH0) << 8);
    return counter;
}

/*
 * Delay for at least ``msec'' milli seconds
 */
void
pit_delay(msec)
    int msec;
{
    register uint16 current, previous, diff;
    register uint32 total;

    /*
     * The counter decrements at twice the timer frequency
     * (one full cycle for each half of a square wave).
     */
    diff = 100; /* just in case */
    total = (uint32) msec * (2 * PIT_FREQ / 1000);
    previous = pit_channel0();
    for (;;) {
	current = pit_channel0();
	if (current < previous)
	    diff = previous - current;
	if (diff >= total)
	    break;
	total -= diff;
	previous = current;
    }
}

#ifdef notyet
/*
 * Return number of actual milli-seconds that have passed
 */
static uint32
pit_hw_milli()
{
    extern uint32 milli_uptime;
    register uint32 milli;
    register int flags;
    uint16 counter;
    int status;

    flags = get_flags(); disable();
    out_byte(PIT_MODE, PIT_RB);
    out_byte(PIT_MODE, 0xC2);
    status = in_byte(PIT_CH0);
    counter = in_byte(PIT_CH0), counter |= (in_byte(PIT_CH0) << 8);
    milli = milli_uptime + (PIT_INTERVAL / 2) *
	(counter / (PIT_FREQ * PIT_INTERVAL) / 1000L);
    if ((status & 0x80) == 0) milli += PIT_INTERVAL/2;
    set_flags(flags);
    return milli;
}
#endif

/*
 * The actual clock interrupt
 */
/* ARGSUSED */
static void
pit_intr(reason, frame)
    int reason;
    struct fault *frame;
{
    void sweeper_run();
    void flp_motoroff();
    extern int motortime;

#ifdef MCA
    /* ps/2 clock needs to be told to stop interrupting */
    out_byte(0x61, in_byte(0x61) | 0x80);
#endif

    enqueue(sweeper_run, (long) PIT_INTERVAL);

#if (defined(ISA) || defined(MCA)) && !defined(NOFLOPPY)
    /* stop running floppy motor */
    if (motortime != 0 && --motortime == 0)
	flp_motoroff();
#endif
}


/*
 * This file is part of the FIREBALL AMOEBA System.
 *
 *
 * Last modified:
 *		18/02/01
 *
 * Stefan Bosse (12/1999-7/2000)
 * sbosse@physik.uni-bremen.de
 *
 * -> pit_hw_milli now default routine with 1ms resolution
 * -> cpuspeed measurement and delay loop reference
 *
 *
 *
 * FIREBALL AMOEBA is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; version 2.
 *
 * The FIREBALL AMOEBA is distributed in the hope that it will be usefull,
 * but WITHOUT ANY WARRANTY; without even implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * General Public License for more details.
 *
 * Original Copyright: Vrije Universiteit, The Netherlands.
 */




/*
 * pit.c
 *
 * Driver for the 8254 timer (PIT). The i8254 timer has three timer channels
 * of which only one (channel 0) is available for timer interrupts. The other
 * channels are used for the speaker and memory refresh.
 *
 * Author:
 *	Leendert van Doorn
 */


#include <amoeba.h>
#include <assert.h>
INIT_ASSERT
#include <fault.h>
#include <bool.h>
#include "sys/proto.h"
#include "i386_proto.h"
#include <irq.h>
#include "pit.h"
#include <cpu.h>

#ifndef PIT_DEBUG
#define	PIT_DEBUG	0
#endif

#ifdef STATISTICS
static unsigned long pit_hw_milli_count=0;
static unsigned long pit_hw_milli_wrap1=0;
static unsigned long pit_hw_milli_wrap2=0;
static unsigned long pit_delay_count=0;
#endif

static int pit_debug;			/* current debug level */
static int initialized=0;

#ifdef PIT_HW_MILLI
static uint32 pit_hw_milli();
#endif
static void pit_intr();

/*
 * milli_uptime will be incremented in sweeper_run, but it's delayed
 * because of enqueue() handling.
 * milli_uptime should be handled ***here***.
 * Hack: Local we use timer_ticks for pit_hw_milli(), incremented in low
 * level ISR below...
 */
unsigned long timer_ticks=0;

/* for time monotony checking; we have a serious problem with
 * this faulty timer hardware
 */
static unsigned long last_time=0;

/*
 * Initialize channel 0 of the i8254A timer
 */
void
pit_init()
{
    register uint32 counter = (PIT_FREQ * PIT_INTERVAL) / 1000L;


    if(!initialized)
    {

	initialized=1;
#ifndef NDEBUG
	if ((pit_debug = kernel_option("pit")) == 0)
		pit_debug = PIT_DEBUG;
	if (pit_debug > 1)
		printf("pit_init(), pit_interval = 0x%x (%d), counter = %x\n",
		       PIT_INTERVAL, PIT_INTERVAL, counter);
#endif

	/* set timer to run continuously */
	assert(counter <= 0xFFFF);

	if(check_region(PIT_CH0,4)<0)
		panic("Some fool allocated timer ports");
	request_region(PIT_CH0,4,"PIT");

	out_byte(PIT_MODE, PIT_MODE2);

	out_byte(PIT_CH0, (int) (counter & 0xFF));
	out_byte(PIT_CH0, (int) ((counter >> 8) & 0xFF));

#ifdef PIT_HW_MILLI
	/* I still have to thoroughly test this */
	/* SB: still timer wrap problems; but time monotonie is guranteed */
	set_hw_milli(pit_hw_milli);
#endif

	if(request_irq(PIT_IRQ,
		       pit_intr,
		       SA_NORMAL,
		       "PIT",
		       (void *)NULL)!=0)
		panic("Some fool allocated timer irq");

	enable_irq(PIT_IRQ);
	/*pic_enable(PIT_IRQ);*/
    }
}

/*
 * Read i8254's channel 0 counter. The counter decrements at twice the
 * timer frequency (one full cycle for each half of a square wave).
 */
uint16
pit_channel0()
{
    register uint16 counter;
    unsigned long flags;

    save_flags(flags);cli();
    out_byte(PIT_MODE, PIT_LC);
    counter = in_byte(PIT_CH0), counter |= (in_byte(PIT_CH0) << 8);
    restore_flags(flags);
    return counter;
}

/*
 * Delay for at least ``msec'' milli seconds
 */
void
pit_delay(msec)
    int msec;
{
    register uint16 current, previous, diff;
    register uint32 total;


#ifdef STATISTICS
    pit_delay_count++;
#endif
    /*
     * The counter decrements at twice the timer frequency
     * (one full cycle for each half of a square wave).
     */
    diff = 100; /* just in case */
    total = (uint32) msec * (PIT_FREQ / 1000);
    previous = pit_channel0();
    for (;;) {
	current = pit_channel0();
	if (current < previous)
	    diff = previous - current;
	if (diff >= total)
	    break;
	total -= diff;
	previous = current;
    }
}


#ifdef PIT_HW_MILLI
/*
 * Return number of actual milli-seconds that have passed
 */
static uint32
pit_hw_milli()
{
    extern uint32 milli_uptime;
    register uint32 milli;
    uint16 counter,counter2;
    int status,status2;
    unsigned long flags;
    unsigned long ticks=timer_ticks;



#ifdef STATISTICS
    pit_hw_milli_count++;
#endif

    save_flags(flags);cli();
    /* Select timer0 and latch counter value. */
    out_byte(PIT_MODE,PIT_LC);
    counter = in_byte(PIT_CH0), counter |= (in_byte(PIT_CH0) << 8);
    restore_flags(flags);

    milli = timer_ticks*PIT_INTERVAL +
	(1000*(((PIT_FREQ*PIT_INTERVAL)/1000) - counter )/(PIT_FREQ) );


    /* check time monotony */
    if(milli<last_time)
    {
#ifdef STATISTICS
	pit_hw_milli_wrap1++;
#endif
	if(ticks<timer_ticks)
	{
		milli=timer_ticks*PIT_INTERVAL;
#ifdef STATISTICS
		pit_hw_milli_wrap2++;
#endif
	}
	else
		milli=last_time;
    }


    last_time=milli;


    if(ticks<timer_ticks) /* Between start and here pit_intr was called */
	return (timer_ticks*PIT_INTERVAL);
    else
	return milli;
}
#endif

/*
 * The actual clock interrupt
 */
/* ARGSUSED */
#ifdef __KERNEL__	/* Work with Linux-ISR	*/
static void
pit_intr(reason,dev_idt,frame)
    int reason;
    void *dev_idt;
    struct fault *frame;
#else
static void
pit_intr(reason,frame)
    int reason;
    struct fault *frame;
#endif
{
    void sweeper_run();
    void flp_motoroff();
    extern int motortime;


    timer_ticks++;

#ifdef MCA
    /* ps/2 clock needs to be told to stop interrupting */
    out_byte(0x61, in_byte(0x61) | 0x80);
#endif

    enqueue(sweeper_run, (long) PIT_INTERVAL);

#if (defined(ISA) || defined(MCA)) && !defined(NOFLOPPY)
    /* stop running floppy motor */
    if (motortime != 0 && --motortime == 0)
	flp_motoroff();
#endif
}

#ifdef STATISTICS
int
pit_stat(begin,end)
char	*begin;
char	*end;
{
	char *p;
	int	i;

	p=bprintf(begin,end,"**** Hardware Timer statistics *****\n");
	p=bprintf(p,end,"pit_hw_milli() calls: %d\n",
		  pit_hw_milli_count);
	p=bprintf(p,end,"pit_hw_milli time wrap check failed: %d\n",
		  pit_hw_milli_wrap1);
	p=bprintf(p,end,"pit_hw_milli dirty ticks increment: %d\n",
		  pit_hw_milli_wrap2);

	p=bprintf(p,end,"pit_delay() calls: %d\n",
		  pit_delay_count);

	return p-begin;
}
#endif

/* For cpu speed measurement. We need timer access. */

void
cpuspeed(int aalcycles, int havecycleclock)
{
        int cpufreq, loops, incr, x, y;
	unsigned long ax1,dx1,ax2,dx2,a,b;

	pit_init();

	/* find biggest loop that doesn't wrap */
	incr = 16000000/(aalcycles*HZ*2);
	x = 2000;
	for(loops = incr; loops < 64*1024; loops += incr) {

		/*
		 *  measure time for the loop
		 *
		 *			MOVL	loops,CX
		 *	aaml1:	 	AAM
		 *			LOOP	aaml1
		 *
		 *  the time for the loop should be independent of external
		 *  cache and memory system since it fits in the execution
		 *  prefetch buffer.
		 *
		 */

		/* Beware of counter reset's (wraps)... */

		/* Read the cpu internal clock if available */
		if(havecycleclock)
			rdmsr(0x10, &ax1,&dx1);
		x = (int)pit_channel0();

		aamloop(loops);

		if(havecycleclock)
			rdmsr(0x10, &ax2,&dx2);

		y = (int)pit_channel0();

		x -= y;

		if(x < 0)
			x += PIT_FREQ/PIT_HZ;

		if(x > PIT_FREQ/(3*PIT_HZ))
			break;
	}

	/*
 	 *  figure out clock frequency and a loop multiplier for delay().
	 *  n.b. counter goes up by 2*PIT_FREQ
	 */
	cpufreq = loops*((aalcycles*PIT_FREQ)/x);
	cpu.loopconst = (cpufreq/1000)/aalcycles; /* AAM+LOOP's for 1ms */

	/* check aalcycle value */
	if(kernel_option("cud") == 1)
		printf("cpuspeed: aalcycle MHz=%d\n",
			(cpufreq + cpufreq/200)/1000000);

	if(havecycleclock){


		if(dx2 == dx1)
			b = (ax2-ax1);
		else	/* counter wrap */
			b = (ax2+0xffffffff-ax1);

		b /= x;
		b *= PIT_FREQ;


		/*
		 *  round to the nearest megahz
		 */
		cpu.cpumhz = (b+500000)/1000000L;
		cpu.cpuhz = b;
	} else {
		/*
		 *  add in possible 0.5% error and convert to MHz
		 */
		cpu.cpumhz = (cpufreq + cpufreq/200)/1000000;
		cpu.cpuhz = cpufreq;
	}


}



  reply	other threads:[~2003-02-22 20:30 UTC|newest]

Thread overview: 12+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2003-02-22 19:17 Maksim Radziwill
2003-02-22 20:30 ` Russ Cox [this message]
2003-02-22 20:51 ` David Presotto
2003-02-23  2:16 ` Skip Tavakkolian
2003-02-23  3:53 ` John Packer
2003-02-23  6:10   ` northern snowfall
2003-02-23  8:30     ` John Packer
2003-02-23 20:26       ` northern snowfall
2003-02-23 21:28         ` John Packer
2003-02-23 22:00           ` northern snowfall
2003-02-22 20:34 philw
2003-02-22 20:40 ` Russ Cox

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=6befec06592023a83a26e343d54335b2@plan9.bell-labs.com \
    --to=rsc@plan9.bell-labs.com \
    --cc=9fans@cse.psu.edu \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).