bsps/i386: Update calibration of TSC to be more accurate

Closes #4455
This commit is contained in:
Jan Sommer
2021-06-11 09:35:30 +02:00
parent 3ee19b7ac3
commit 93f9645595

View File

@@ -104,48 +104,60 @@ static uint32_t pc386_get_timecount_i8254(struct timecounter *tc)
/* /*
* Calibrate CPU cycles per tick. Interrupts should be disabled. * 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) static void calibrate_tsc(void)
{ {
uint64_t begin_time; uint64_t begin_time;
uint8_t then_lsb, then_msb, now_lsb, now_msb; uint8_t lsb, msb;
uint32_t i; uint32_t max_timer_value;
uint32_t last_tick, cur_tick;
int32_t diff, remaining;
/* /* Set the timer to free running mode */
* We just reset the timer, so we know we're at the beginning of a tick. 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);
* Count cycles. Watching the timer introduces a several microsecond /* We use the full 16 bit */
* uncertaintity, so let it cook for a while and divide by the number of max_timer_value = 0xffff;
* ticks actually executed. /* Calibrate for 1s, i.e. TIMER_TICK PIT ticks */
*/ remaining = TIMER_TICK;
begin_time = rdtsc(); begin_time = rdtsc();
READ_8254(lsb, msb);
for (i = rtems_clock_get_ticks_per_second() * pc386_isrs_per_tick; last_tick = (msb << 8) | lsb;
i != 0; --i ) { while(remaining > 0) {
/* We know we've just completed a tick when timer goes from low to high */ READ_8254(lsb, msb);
then_lsb = then_msb = 0xff; cur_tick = (msb << 8) | lsb;
do { /* PIT counts down, so subtract cur from last */
READ_8254(now_lsb, now_msb); diff = last_tick - cur_tick;
if ((then_msb < now_msb) || last_tick = cur_tick;
((then_msb == now_msb) && (then_lsb < now_lsb))) if (diff < 0) {
break; diff += max_timer_value;
then_lsb = now_lsb; }
then_msb = now_msb; remaining -= diff;
} while (1);
} }
pc586_tsc_frequency = rdtsc() - begin_time; pc586_tsc_frequency = rdtsc() - begin_time;
#if 0 #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 #endif
} }
static void clockOn(void) 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; rtems_interrupt_lock_context lock_context;
pc386_isrs_per_tick = 1; pc386_isrs_per_tick = 1;
pc386_microseconds_per_isr = rtems_configuration_get_microseconds_per_tick(); 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); rtems_interrupt_lock_release(&rtems_i386_i8254_access_lock, &lock_context);
bsp_interrupt_vector_enable( BSP_PERIODIC_TIMER ); 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; bool Clock_isr_enabled = false;