Getting CPU speed

Question about which tools to use, bugs, the best way to implement a function, etc should go here. Don't forget to see if your question is answered in the wiki first! When in doubt post here.
User avatar
Pype.Clicker
Member
Member
Posts: 5964
Joined: Wed Oct 18, 2006 2:31 am
Location: In a galaxy, far, far away
Contact:

Re:Getting CPU speed

Post by Pype.Clicker »

what value of HZ did you use (just out of curiousity, and how does it relates to some PIT programmed values ?) With PIT running at unmodified speed (18.2Hz), it keeps telling me that my AMD-K6II 300MHz processor runs at 1258MHz (#define HZ 18)...

Code: Select all

/*! waits for a given amount of nanonsecond
 *  we're currently polling the PIT chip, making the 
 * resolution of 0.838 microseconds and worst excessive  
 * delay of resolution * 2.
 */
  void public nanosleep(unsigned count) {
    unsigned usec=1+((count/1000)*1193)/1000;
    byte lo,hi,lo2,hi2;
    
    do {
      lo2=lo=inb(0x40);
      hi2=hi=inb(0x40);
      for (;lo==lo2 && hi==hi2;) {
   lo2=inb(0x40);
   hi2=inb(0x40);
      }
    } while (usec--);
  }
As long as we're in the "speed & timing" area, here's the code used in Clicker for small delays (ATA and the like).
proxy

Re:Getting CPU speed

Post by proxy »

My only issue with that code is that it appears at first glance to be almost identical to how linux does it. In fact some of your variable names and macro names are named _identically_.

your function: calibrate_tsc() appears to be a simple modification of the function by the same name in arch/i386/kernel/timers/common.c, mostely using 64-bit integers instead of 2 32-bit ints and consolodation of error checks.

your function: calculate_bogomips() appears to be an almost verbatum copy of the function init_cpu_khz() from the same file with slightly different variable names and no check if TSC is suported.

You write
PPS: No license attached to this code..
but linux is GPLed...

I have no issue with people using GPLed code, but claiming they wrote it and then putting it into the public domain is simply unacceptable.

proxy
durand
Member
Member
Posts: 193
Joined: Wed Dec 21, 2005 12:00 am
Location: South Africa
Contact:

Re:Getting CPU speed

Post by durand »

oh... that kinda sucks. :-\ I was given this code a while ago and I cleaned it up a bit. I'll go check it out.
durand
Member
Member
Posts: 193
Joined: Wed Dec 21, 2005 12:00 am
Location: South Africa
Contact:

Re:Getting CPU speed

Post by durand »

I make a point of not looking at linux source code because of the whole GPL thing but I had a look at the common.cc code.

You're right about it... it comes from there. Sorry about that. It's the only piece of donated code which I have in my tree at the moment.
User avatar
Candy
Member
Member
Posts: 3882
Joined: Tue Oct 17, 2006 11:33 pm
Location: Eindhoven

Re:Getting CPU speed

Post by Candy »

durand wrote: You're right about it... it comes from there. Sorry about that. It's the only piece of donated code which I have in my tree at the moment.
I was refusing donated code (or other PD code) because of those things... this only confirms my fears...
durand
Member
Member
Posts: 193
Joined: Wed Dec 21, 2005 12:00 am
Location: South Africa
Contact:

Re:Getting CPU speed

Post by durand »

I'm reading through Ralf Brown's port listing, getting a better understand of how the code works. I'll re-implement it with that understanding later. It's a simple method.

The PIT has a fixed MHz in all machines. Using PIT timer 2 (0x42), you start it counting down from a large number. When it reaches zero, it flips some bits on the keyboard controller port (0x61). During this time, you were spinning in a loop and measuring how fast your CPU can do "nothing" again. If you measure tsc before and after the loop, you know that you do X operations in exactly N time. Because the timer 2 has a granularity more fine than the real PIT clock, you don't have to wait the complete second for a timer event. Plus, no interrupts are required.

Anyway, that's the method. Now you all can implement the method yourselves and be sure of the original authors (yourselves) ... It will probably still look like the code I posted because there's only so much variety you can put into source code at this kind of level. (outb, tsc, loop, tsc, divide.. done ).

That mistake was a little embarressing. Thanks go to proxy for spotting it.
durand
Member
Member
Posts: 193
Joined: Wed Dec 21, 2005 12:00 am
Location: South Africa
Contact:

Re:Getting CPU speed

Post by durand »

So, I contacted my friend who gave me the code and he did write the code himself from an online resource. Where the online resource got the information, I do not know. However, I rewrote the routine myself using Ralf Brown's port information.

I've come to the conclusion that there is only one way to write the routine. The entire calibration consists of:

1. PIT Timer 2 initialization
2. Calibration loop checking 0x61 (bit 5)
3. 64 bit divide.

There is no room for experimentation in any of those steps. It literally comes down to about 10-15 lines of code, almost half of which are defined by the PIT programming and tsc loop - which will be the same everywhere. So I suspect any implementation of the code will appear to be the same code but with slight modifications.

The only unfortunate part about the code was the use of variable names and constant names which matched the same names and constants in linux. In fact, the constant names can only be found in the linux kernel if you do a google search. So that did rather suck and casts everything into doubt as to the original source.

Anyway, I've rewritten it and it's a handy, fast, efficient, reliable method. If anyone wants my new code, I can repost it. Alternatively, here's a detailed description of the method from University of San Francisco:

http://www.cs.usfca.edu/~cruse/cs630/lesson23.ppt

And that's enough from me! ...
User avatar
Solar
Member
Member
Posts: 7615
Joined: Thu Nov 16, 2006 12:01 pm
Location: Germany
Contact:

Re:Getting CPU speed

Post by Solar »

Candy wrote:
durand wrote: You're right about it... it comes from there. Sorry about that. It's the only piece of donated code which I have in my tree at the moment.
I was refusing donated code (or other PD code) because of those things... this only confirms my fears...
You have that problem with any kind of code you have not written yourself. Same goes for GPL code you absorb into your GPL project - if the third-party source turns out to be "stolen", you'll have to wind back the change.

One of the reasons I stepped back from doing PDCLib as a community effort, and write it all by myself. And even those few parts that have been taken from other PD sources are heavily edited to conform to my coding style - and to "cover my trails" so to speak.

Isn't software legaleze a beauty? ::)
Every good solution is obvious once you've found it.
User avatar
Pype.Clicker
Member
Member
Posts: 5964
Joined: Wed Oct 18, 2006 2:31 am
Location: In a galaxy, far, far away
Contact:

Re:Getting CPU speed

Post by Pype.Clicker »

Since this thread is now covered by the FAQ (getting CPU speed), and since the method durand posted was working fine (with HZ=100 ;) ), i'm re-posting it (other discussion about code that disappeared from the board makes little sense otherwise, no ?). Be warned this comes out from Linux (bla bla bla ...)

Code: Select all

#define HZ 100
#define LATCH ( ( 1193180 + HZ / 2 ) / HZ )
#define CALIBRATE_LATCH ( 5 * LATCH )
#define CALIBRATE_TIME ( 5 * 1000020 / HZ )


static inline unsigned long long rdtsc()
{
  unsigned long long  x;
  asm volatile (".byte 0x0f,0x31" : "=A" (x));
  return x;
}

static unsigned long calibrate_tsc()
{
  unsigned long long diff;
  unsigned long count, endlow, endhigh;
   
  outb( 0x61, ( inb( 0x61 ) & ~0x02 ) | 0x01 );
  outb( 0x43, 0xb0 );
  outb( 0x42, CALIBRATE_LATCH & 0xff );
  outb( 0x42, (CALIBRATE_LATCH >> 8) & 0xff );
  diff = rdtsc();
  count = 0;
  do
  {
    count++;
  } while ( ( inb( 0x61 ) & 0x20 ) == 0 );
 
  diff = rdtsc() - diff;
 
  if ( count <= 1 ) return 0;
 
  if ( (diff > (0x100000000ULL)) || ( diff <= CALIBRATE_TIME) ) return 0;
//  if ((diff > (0xffffffff) || diff <= CALIBRATE_TIME)) return 0;
 
  __asm__ ( "divl %2"
      : "=a" ( endlow ),
        "=d" ( endhigh )
      : "r" ( (diff & 0xFFFFFFFFUL) ),
        "0" ( 0 ),
        "1" ( CALIBRATE_TIME ) );
 
  return endlow;
}
btw, iirc, that specific timer is the one we can use for PC speaker sound output, right ?
durand
Member
Member
Posts: 193
Joined: Wed Dec 21, 2005 12:00 am
Location: South Africa
Contact:

Re:Getting CPU speed

Post by durand »

Yes, the timer is hooked into the PC speaker. Timer 2's status is reflected in the keyboard controller. That's why it's better to use than the other timers... those need IRQ's or direct PIT polling. This is my redone code.

Code: Select all

static inline uint32_t udiv( uint64_t dividend, uint32_t divisor, uint32_t *rem )
{
    uint32_t quotient;
    uint32_t remainder;

    asm volatile
            (  "mov %2, %%edx\n"
               "mov %3, %%eax\n"
               "mov %4, %%ecx\n"
               "divl %%ecx\n"
               "mov %%eax, %0\n"
               "mov %%edx, %1\n"

              : "=g" (quotient), "=g" (remainder)
              : "g" ( ((uint32_t)((dividend) >> 32) & 0xFFFFFFFF) ),
                "g" (  (uint32_t) (dividend         & 0xFFFFFFFF)  ),
                "g" ( divisor )

              : "eax", "edx", "ebx"
              );

    *rem = remainder;
    return quotient;
}

Code: Select all

// From Ralf Brown's list 

#define COUNTER_2               (1<<7)
#define RW0_15                  (3<<4)
#define MODE_ZERO_DETECTION     0
#define BINARY_COUNTER          0

// Keyboard Controller Flags

#define KBD_SPEAKER_DATA_STATUS     (1<<1)
#define KBD_CLOCK_GATE_TO_SPEAKER   (1<<0)
#define KBD_TIMER2_CONDITION        (1<<5)

// PIT defines and variables

#define HZ          100
#define PIT_HZ      1193180         // PIT clock HZ

#define START_COUNT     ( 5 * PIT_HZ / HZ )
#define COUNT_TIME      ( 5 * 1000000 / HZ )

static unsigned long calibrate_tsc()
{
    uint64_t diff, datime;
    uint32_t rem;
    uint32_t temp;
    unsigned long count = 0;

    // Turn the speaker off and clear status
    outb( 0x61, (inb(0x61) & ~(KBD_SPEAKER_DATA_STATUS) ) | KBD_CLOCK_GATE_TO_SPEAKER );
    outb( 0x43, (COUNTER_2 | RW0_15 | MODE_ZERO_DETECTION | BINARY_COUNTER) );
    outb( 0x42, (START_COUNT & 0xff) );
    outb( 0x42, (START_COUNT >> 8) & 0xff );

    // Wait until timer 2 fires
    diff = rdtsc();
    while ( ( inb( 0x61 ) & KBD_TIMER2_CONDITION ) == 0 ) count++;
    diff = rdtsc() - diff;

    if ( (diff > (1ull<<32)) || ( diff <= COUNT_TIME) ) return 0;
                            // Ensure division won't cause exception.

        // Scale to preserve accuracy, otherwise we only get KHz equivalents
    datime = COUNT_TIME;
    datime = datime << 32;

    temp = udiv( datime, (uint32_t)(diff & 0xFFFFFFFF), &rem );
    return udiv( (1000000ull <<32), temp, &rem );
}
There's not much difference.

Now, I did write this myself and if it bears resemblance to anything, it's because there's no other way to do it. I could go crazy and call my variables "bob", "sam" and "sue". I could implement my own division algorithms instead of using the CPU's native abilities.. and I could remove the safety checks so that the algorithm fails with an exception instead of gracefully.

So, I challenge anyone to write it differently. That would be interesting.
User avatar
Pype.Clicker
Member
Member
Posts: 5964
Joined: Wed Oct 18, 2006 2:31 am
Location: In a galaxy, far, far away
Contact:

Re:Getting CPU speed

Post by Pype.Clicker »

hm. for some unknown reason, it still doesn't want to report 300MHz on my AMD cpu. i instead get 14MHz reported or something similar. Investigations under progress ...
durand
Member
Member
Posts: 193
Joined: Wed Dec 21, 2005 12:00 am
Location: South Africa
Contact:

Re:Getting CPU speed

Post by durand »

That sounds right. You're missing a step. Don't forget that the value returned by calibrate_tsc is the TIME/OPS. So you need to invert it to represent speed. (OPS/TIME). ie: 1 / ( (TIME/OPS) ).

Since 1 / (TIME/OPS) will result in 0 because of the nature of integer division, increase the numerator by a large factor to maintain accuracy and to expose the decimals components. Conventiently, the calculate_tsc returned a number biased by 2^32. So, using 2^32 again to remove it will correct the number back into something normal.

Your speed will be (1<<32) / tsc.
So (1<<32) / 14000000 = +- 300 MHz

Durand.

PS. This reminds me of fixed point math back in school when you needed to squeeze everything out of a 386 just to get a rotating cube. I used to precalculate fixed-point arrays for sin and cos over 360 degrees.

PPS. Hmm.. this step could be put into the calibrate_tsc loop. So I did in the post above.
proxy

Re:Getting CPU speed

Post by proxy »

nice that you are trying to redo it yourself, i think this has many benefits.

firstly you will understand the code even better than you did before, secondly you wont be stealing from anyone.

I did notive one minior difference in your code though from a function point of view.

In the linux version count is always at least one because it's structure is:

do { ++count; } while(condition);


but yours may leave it at 0, in fact, yours is always one less than what the linux version may come up with.

because you do something liek this:

while(condition) count++;

Ya never know, may make a difference in some corner cases...

proxy
User avatar
Candy
Member
Member
Posts: 3882
Joined: Tue Oct 17, 2006 11:33 pm
Location: Eindhoven

Re:Getting CPU speed

Post by Candy »

proxy wrote: I did notive one minior difference in your code though from a function point of view.

In the linux version count is always at least one because it's structure is:

do { ++count; } while(condition);


but yours may leave it at 0, in fact, yours is always one less than what the linux version may come up with.

because you do something liek this:

while(condition) count++;

Ya never know, may make a difference in some corner cases...
That'll crash on computers on which condition is true before a single count. Say, very slow computers, such as bochs.

You could initialize count on one as well... would do the same.
distantvoices
Member
Member
Posts: 1600
Joined: Wed Oct 18, 2006 11:59 am
Location: Vienna/Austria
Contact:

Re:Getting CPU speed

Post by distantvoices »

@proxy:
nice that you are trying to redo it yourself, i think this has many benefits.

firstly you will understand the code even better than you did before, secondly you wont be stealing from anyone.
you may as well give it a rest, ok?

For me the method works, as already stated. ;-) Thanks to durand & pype for not throwing it away because of ... possible crossing some line of gpl blabla...
... the osdever formerly known as beyond infinity ...
BlueillusionOS iso image
Post Reply