[SeaBIOS] [PATCH 04/10] Improve accuracy of internal timers.

Kevin O'Connor kevin at koconnor.net
Sun Jul 21 01:49:46 CEST 2013


The TICKS_PER_DAY setting is a bios standard and needs to be 1573040
for compatibility.  However, there are actually ~1573042.24 ticks in a
day.  So, only use TICKS_PER_DAY when working with the BDA
timer_counter - not when calculating any internal times.

The PIT hz is actually 143181800 / 12 (~1193181.667).  This can be
accurately encoded as PMTIMER hz / 3.  Because the PIT hz is usually
multiplied and divided by other numbers, we can use the PMTIMER hz and
defer the division by 3 to improve accuracy.

When doing division for delay time calculations, always round up the
division so the delay is never less than the requested time.

Signed-off-by: Kevin O'Connor <kevin at koconnor.net>
---
 src/biosvar.h |  3 +++
 src/clock.c   |  2 +-
 src/pit.h     |  7 -------
 src/timer.c   | 38 +++++++++++++++++++++-----------------
 4 files changed, 25 insertions(+), 25 deletions(-)

diff --git a/src/biosvar.h b/src/biosvar.h
index bbb196a..e49a10a 100644
--- a/src/biosvar.h
+++ b/src/biosvar.h
@@ -132,6 +132,9 @@ struct bios_data_area_s {
 #define FMS_DOUBLE_STEPPING         (1<<5)
 #define FMS_DATA_RATE_MASK          (0xc0)
 
+// Limit of BDA timer_counter field
+#define TICKS_PER_DAY 1573040
+
 // Accessor functions
 #define GET_BDA(var) \
     GET_FARVAR(SEG_BDA, ((struct bios_data_area_s *)0)->var)
diff --git a/src/clock.c b/src/clock.c
index 2f2ca07..4b33bb8 100644
--- a/src/clock.c
+++ b/src/clock.c
@@ -95,7 +95,7 @@ clock_setup(void)
     u32 minutes = bcd2bin(inb_cmos(CMOS_RTC_MINUTES));
     u32 hours = bcd2bin(inb_cmos(CMOS_RTC_HOURS));
     u32 ticks = ticks_from_ms(((hours * 60 + minutes) * 60 + seconds) * 1000);
-    SET_BDA(timer_counter, ticks);
+    SET_BDA(timer_counter, ticks % TICKS_PER_DAY);
 
     // Setup Century storage
     if (CONFIG_QEMU) {
diff --git a/src/pit.h b/src/pit.h
index 7b5e5e8..098f270 100644
--- a/src/pit.h
+++ b/src/pit.h
@@ -2,13 +2,6 @@
 #ifndef __PIT_H
 #define __PIT_H
 
-/* PM Timer ticks per second (HZ) */
-#define PM_TIMER_FREQUENCY  3579545
-
-#define PIT_TICK_RATE 1193180   // Underlying HZ of PIT
-#define PIT_TICK_INTERVAL 65536 // Default interval for 18.2Hz timer
-#define TICKS_PER_DAY (u32)((u64)60*60*24*PIT_TICK_RATE / PIT_TICK_INTERVAL)
-
 // Bits for PORT_PIT_MODE
 #define PM_SEL_TIMER0   (0<<6)
 #define PM_SEL_TIMER1   (1<<6)
diff --git a/src/timer.c b/src/timer.c
index 0fd0194..d659e94 100644
--- a/src/timer.c
+++ b/src/timer.c
@@ -15,6 +15,10 @@
 #define PPCB_SPKR   (1<<1)
 #define PPCB_T2OUT  (1<<5)
 
+#define PMTIMER_HZ 3579545      // Underlying Hz of the PM Timer
+#define PMTIMER_TO_PIT 3        // Ratio of pmtimer rate to pit rate
+#define PIT_TICK_INTERVAL 65536 // Default interval for 18.2Hz timer
+
 
 /****************************************************************
  * TSC timer
@@ -44,8 +48,8 @@ timer_setup(void)
         cpuid(1, &eax, &ebx, &ecx, &cpuid_features);
 
     if (!(cpuid_features & CPUID_TSC)) {
-        SET_GLOBAL(no_tsc, 1);
-        SET_GLOBAL(cpu_khz, PIT_TICK_RATE / 1000);
+        no_tsc = 1;
+        cpu_khz = DIV_ROUND_UP(PMTIMER_HZ, 1000 * PMTIMER_TO_PIT);
         dprintf(3, "386/486 class CPU. Using TSC emulation\n");
         return;
     }
@@ -72,10 +76,10 @@ timer_setup(void)
     u64 diff = end - start;
     dprintf(6, "tsc calibrate start=%u end=%u diff=%u\n"
             , (u32)start, (u32)end, (u32)diff);
-    u32 hz = diff * PIT_TICK_RATE / CALIBRATE_COUNT;
-    SET_GLOBAL(cpu_khz, hz / 1000);
+    u32 t = DIV_ROUND_UP(diff * PMTIMER_HZ, CALIBRATE_COUNT);
+    cpu_khz = DIV_ROUND_UP(t, 1000 * PMTIMER_TO_PIT);
 
-    dprintf(1, "CPU Mhz=%u\n", hz / 1000000);
+    dprintf(1, "CPU Mhz=%u\n", t / (1000000 * PMTIMER_TO_PIT));
 }
 
 /* TSC emulation timekeepers */
@@ -103,10 +107,9 @@ void pmtimer_setup(u16 ioport)
 {
     if (!CONFIG_PMTIMER)
         return;
-    u32 khz = PM_TIMER_FREQUENCY / 1000;
-    dprintf(1, "Using pmtimer, ioport 0x%x, freq %d kHz\n", ioport, khz);
-    SET_GLOBAL(pmtimer_ioport, ioport);
-    SET_GLOBAL(cpu_khz, khz);
+    dprintf(1, "Using pmtimer, ioport 0x%x\n", ioport);
+    pmtimer_ioport = ioport;
+    cpu_khz = DIV_ROUND_UP(PMTIMER_HZ, 1000);
 }
 
 static u64 pmtimer_get(void)
@@ -160,20 +163,20 @@ tscsleep(u64 diff)
 }
 
 void ndelay(u32 count) {
-    tscdelay(count * GET_GLOBAL(cpu_khz) / 1000000);
+    tscdelay(DIV_ROUND_UP(count * GET_GLOBAL(cpu_khz), 1000000));
 }
 void udelay(u32 count) {
-    tscdelay(count * GET_GLOBAL(cpu_khz) / 1000);
+    tscdelay(DIV_ROUND_UP(count * GET_GLOBAL(cpu_khz), 1000));
 }
 void mdelay(u32 count) {
     tscdelay(count * GET_GLOBAL(cpu_khz));
 }
 
 void nsleep(u32 count) {
-    tscsleep(count * GET_GLOBAL(cpu_khz) / 1000000);
+    tscsleep(DIV_ROUND_UP(count * GET_GLOBAL(cpu_khz), 1000000));
 }
 void usleep(u32 count) {
-    tscsleep(count * GET_GLOBAL(cpu_khz) / 1000);
+    tscsleep(DIV_ROUND_UP(count * GET_GLOBAL(cpu_khz), 1000));
 }
 void msleep(u32 count) {
     tscsleep(count * GET_GLOBAL(cpu_khz));
@@ -190,7 +193,7 @@ u64
 calc_future_tsc_usec(u32 usecs)
 {
     u32 khz = GET_GLOBAL(cpu_khz);
-    return get_tsc() + ((u64)(khz/1000) * usecs);
+    return get_tsc() + ((u64)DIV_ROUND_UP(khz, 1000) * usecs);
 }
 
 
@@ -202,15 +205,16 @@ calc_future_tsc_usec(u32 usecs)
 u32
 ticks_to_ms(u32 ticks)
 {
-    return DIV_ROUND_UP(PIT_TICK_INTERVAL * 1000 * ticks, PIT_TICK_RATE);
+    u32 t = PIT_TICK_INTERVAL * 1000 * PMTIMER_TO_PIT * ticks;
+    return DIV_ROUND_UP(t, PMTIMER_HZ);
 }
 
 // Return the number of timer irqs in 'ms' number of milliseconds.
 u32
 ticks_from_ms(u32 ms)
 {
-    u32 kticks = DIV_ROUND_UP((u64)ms * PIT_TICK_RATE, PIT_TICK_INTERVAL);
-    return DIV_ROUND_UP(kticks, 1000);
+    u32 t = DIV_ROUND_UP((u64)ms * PMTIMER_HZ, PIT_TICK_INTERVAL);
+    return DIV_ROUND_UP(t, 1000 * PMTIMER_TO_PIT);
 }
 
 // Calculate the timer value at 'count' number of full timer ticks in
-- 
1.7.11.7




More information about the SeaBIOS mailing list