[PATCH v2 1/1] bsps/i386: Update calibration of TSC to be more accurate
Jan Sommer
jan.sommer at dlr.de
Wed Jun 16 10:17:50 UTC 2021
Closes #4455
---
bsps/i386/pc386/clock/ckinit.c | 71 ++++++++++++++++++----------------
1 file changed, 38 insertions(+), 33 deletions(-)
diff --git a/bsps/i386/pc386/clock/ckinit.c b/bsps/i386/pc386/clock/ckinit.c
index 09afe73cde..2df1818dd3 100644
--- a/bsps/i386/pc386/clock/ckinit.c
+++ b/bsps/i386/pc386/clock/ckinit.c
@@ -104,48 +104,60 @@ static uint32_t pc386_get_timecount_i8254(struct timecounter *tc)
/*
* Calibrate CPU cycles per tick. Interrupts should be disabled.
+ * Will also set the PIT, so call this before registering the
+ * periodic timer for rtems tick generation
*/
static void calibrate_tsc(void)
{
uint64_t begin_time;
- uint8_t then_lsb, then_msb, now_lsb, now_msb;
- uint32_t i;
-
- /*
- * We just reset the timer, so we know we're at the beginning of a tick.
- */
-
- /*
- * Count cycles. Watching the timer introduces a several microsecond
- * uncertaintity, so let it cook for a while and divide by the number of
- * ticks actually executed.
- */
+ uint8_t lsb, msb;
+ uint32_t max_timer_value;
+ uint32_t last_tick, cur_tick;
+ int32_t diff, remaining;
+
+ /* Set the timer to free running mode */
+ outport_byte(TIMER_MODE, TIMER_SEL0 | TIMER_16BIT | TIMER_INTTC);
+ /* Reset the 16 timer reload value, first LSB, then MSB */
+ outport_byte(TIMER_CNTR0, 0);
+ outport_byte(TIMER_CNTR0, 0);
+ /* We use the full 16 bit */
+ max_timer_value = 0xffff;
+ /* Calibrate for 1s, i.e. TIMER_TICK PIT ticks */
+ remaining = TIMER_TICK;
begin_time = rdtsc();
-
- for (i = rtems_clock_get_ticks_per_second() * pc386_isrs_per_tick;
- i != 0; --i ) {
- /* We know we've just completed a tick when timer goes from low to high */
- then_lsb = then_msb = 0xff;
- do {
- READ_8254(now_lsb, now_msb);
- if ((then_msb < now_msb) ||
- ((then_msb == now_msb) && (then_lsb < now_lsb)))
- break;
- then_lsb = now_lsb;
- then_msb = now_msb;
- } while (1);
+ READ_8254(lsb, msb);
+ last_tick = (msb << 8) | lsb;
+ while(remaining > 0) {
+ READ_8254(lsb, msb);
+ cur_tick = (msb << 8) | lsb;
+ /* PIT counts down, so subtract cur from last */
+ diff = last_tick - cur_tick;
+ last_tick = cur_tick;
+ if (diff < 0) {
+ diff += max_timer_value;
+ }
+ remaining -= diff;
}
pc586_tsc_frequency = rdtsc() - begin_time;
#if 0
- printk( "CPU clock at %u MHz\n", (uint32_t)(pc586_tsc_frequency / 1000000));
+ printk( "CPU clock at %u Hz\n", (uint32_t)(pc586_tsc_frequency ));
#endif
}
static void clockOn(void)
{
+
+ /*
+ * First calibrate the TSC. Do this every time we
+ * turn the clock on in case the CPU clock speed has changed.
+ */
+ if ( x86_has_tsc() ) {
+ calibrate_tsc();
+ }
+
rtems_interrupt_lock_context lock_context;
pc386_isrs_per_tick = 1;
pc386_microseconds_per_isr = rtems_configuration_get_microseconds_per_tick();
@@ -171,13 +183,6 @@ static void clockOn(void)
rtems_interrupt_lock_release(&rtems_i386_i8254_access_lock, &lock_context);
bsp_interrupt_vector_enable( BSP_PERIODIC_TIMER );
-
- /*
- * Now calibrate cycles per tick. Do this every time we
- * turn the clock on in case the CPU clock speed has changed.
- */
- if ( x86_has_tsc() )
- calibrate_tsc();
}
bool Clock_isr_enabled = false;
--
2.17.1
More information about the devel
mailing list