Added some function to read the tsc with start/stop semantics
authorKevin Klues <klueska@cs.berkeley.edu>
Thu, 21 May 2009 17:29:31 +0000 (10:29 -0700)
committerKevin Klues <klueska@cs.berkeley.edu>
Thu, 21 May 2009 17:29:31 +0000 (10:29 -0700)
In addition to these functions, there is a training function that figures out what the correct
overhead of making the calls is so that it can be factored out before returning the time
difference.  This has not been tested on real hardware yet, and is pretty flaky in my
bochs-inside-vmware environment.  Logic seems sound though...

kern/apic.c

index 2d2bcad..4e79d26 100644 (file)
@@ -10,6 +10,7 @@
 #include <kern/apic.h>
 
 system_timing_t system_timing = {0, 0, 0xffff, 0};
+uint64_t timing_overhead = 0;
 
 /*
  * Remaps the Programmable Interrupt Controller to use IRQs 32-47
@@ -91,6 +92,55 @@ uint32_t lapic_get_default_id(void)
        return (ebx & 0xFF000000) >> 24;
 }
 
+uint64_t read_tsc_serialized() __attribute__((noinline)) 
+{
+       cpuid(0, 0, 0, 0, 0);
+       return read_tsc();
+}
+
+uint64_t start_timing() __attribute__((noinline)) 
+{
+       return read_tsc_serialized();
+}
+
+uint64_t stop_timing(uint64_t val) __attribute__((noinline)) 
+{
+       return (read_tsc_serialized() - val - timing_overhead);
+}
+
+void train_timing() 
+{
+       uint16_t num_runs = 0;
+       uint16_t num_times_zero = 0;
+       uint64_t cum_overhead = 0;
+       
+       //Do this while 1/3 of all runs have not equaled 0 yet
+       do {
+               /* Run 100 times, counting how many out of that 100 
+                * were equal to 0, adjusting the global timing_overhead
+                * in the process.
+                */
+               for(int i=0; i<100; i++) {
+                       uint64_t time = start_timing();
+                       uint64_t diff = stop_timing(time);
+                       
+                       /* In case diff was negative, I want to add its absolute value
+                        * to the cumulative error, otherwise, just diff itself
+                        */
+                       if((int64_t)diff < 0)
+                               diff = (uint64_t)(~0) - diff; 
+                       cum_overhead += diff;
+
+                       //TODO: Consider using diff < 2  instead of 
+                       // strictly == 0 in case forcing a 0 restriction is too strict 
+                       if(diff == 0) num_times_zero++;
+                       num_runs++;
+                       timing_overhead = (cum_overhead/num_runs);
+                       printk("Timing Differences: %llu\n", diff);
+               }
+       } while(num_runs/num_times_zero > 2);
+}
+
 // timer init calibrates both tsc timer and lapic timer using PIT
 void timer_init(void){
        uint64_t tscval[2];
@@ -114,6 +164,7 @@ void timer_init(void){
        system_timing.bus_freq = (timercount[0] - timercount[1])*128;
                
        cprintf("Bus Frequency: %llu\n", system_timing.bus_freq);
+       train_timing();
 }
 
 void pit_set_timer(uint32_t divisor, uint32_t mode)