https://git.reactos.org/?p=reactos.git;a=commitdiff;h=3e1454c739ca7f59d4ce6…
commit 3e1454c739ca7f59d4ce686a5b6a8aa9c22d0e7e
Author: Timo Kreuzer <timo.kreuzer(a)reactos.org>
AuthorDate: Fri Jul 16 11:42:45 2021 +0200
Commit: Timo Kreuzer <timo.kreuzer(a)reactos.org>
CommitDate: Mon Jul 19 23:05:03 2021 +0200
[HAL/APIC] Make the real time clock more precise
The interval time is now calculated in 0.1ns precision and a running fraction on the
100ns interval is updated on each clock interrupt.
Also adjust minimum, maximum and default clock rate to what Windows uses.
---
hal/halx86/apic/rtctimer.c | 99 +++++++++++++++++++++++++++-------------------
1 file changed, 59 insertions(+), 40 deletions(-)
diff --git a/hal/halx86/apic/rtctimer.c b/hal/halx86/apic/rtctimer.c
index 4c0b27dbc06..50b50248366 100644
--- a/hal/halx86/apic/rtctimer.c
+++ b/hal/halx86/apic/rtctimer.c
@@ -18,56 +18,62 @@
/* GLOBALS ********************************************************************/
-BOOLEAN HalpClockSetMSRate;
-UCHAR HalpNextMSRate;
-UCHAR HalpCurrentRate = 9; /* Initial rate 9: 128 Hz / 7.8 ms */
-ULONG HalpCurrentTimeIncrement;
-static UCHAR RtcMinimumClockRate = 8; /* Minimum rate 8: 256 Hz / 3.9 ms */
-static UCHAR RtcMaximumClockRate = 12; /* Maximum rate 12: 16 Hz / 62.5 ms */
+static const UCHAR RtcMinimumClockRate = 6; /* Minimum rate 6: 1024 Hz / 0.97 ms */
+static const UCHAR RtcMaximumClockRate = 10; /* Maximum rate 10: 64 Hz / 15.6 ms */
+static UCHAR HalpCurrentClockRate = 10; /* Initial rate 10: 64 Hz / 15.6 ms */
+static ULONG HalpCurrentTimeIncrement;
+static ULONG HalpMinimumTimeIncrement;
+static ULONG HalpMaximumTimeIncrement;
+static ULONG HalpCurrentFractionalIncrement;
+static ULONG HalpRunningFraction;
+static BOOLEAN HalpSetClockRate;
+static UCHAR HalpNextClockRate;
/*!
\brief Converts the CMOS RTC rate into the time increment in 100ns intervals.
- Rate Freqency Interval (ms) Result
- -------------------------------------
+ Rate Frequency Interval (ms) Precise increment (0.1ns)
+ ------------------------------------------------------
0 disabled
- 1 32768 0.03052 305
- 2 16384 0.06103 610
- 3 8192 0.12207 1221
- 4 4096 0.24414 2441
- 5 2048 0.48828 4883
- 6 1024 0.97656 9766
- 7 512 1.95313 19531
- 8 256 3.90625 39063
- 9 128 7.8125 78125
- 10 64 15.6250 156250
- 11 32 31.25 312500
- 12 16 62.5 625000
- 13 8 125 1250000
- 14 4 250 2500000
- 15 2 500 5000000
+ 1 32768 0.03052 305,175
+ 2 16384 0.06103 610,351
+ 3 8192 0.12207 1,220,703
+ 4 4096 0.24414 2,441,406
+ 5 2048 0.48828 4,882,812
+ 6 1024 0.97656 9,765,625 <- minimum
+ 7 512 1.95313 19,531,250
+ 8 256 3.90625 39,062,500
+ 9 128 7.8125 78,125,000
+ 10 64 15.6250 156,250,000 <- maximum / default
+ 11 32 31.25 312,500,000
+ 12 16 62.5 625,000,000
+ 13 8 125 1,250,000,000
+ 14 4 250 2,500,000,000
+ 15 2 500 5,000,000,000
*/
FORCEINLINE
ULONG
-RtcClockRateToIncrement(UCHAR Rate)
+RtcClockRateToPreciseIncrement(UCHAR Rate)
{
/* Calculate frequency */
- ULONG Freqency = 32768 >> (Rate - 1);
+ ULONG Frequency = 32768 >> (Rate - 1);
- /* Calculate interval in 100ns interval: Interval = (1 / Frequency) * 10000000
- This formula will round properly, instead of truncating. */
- return (10000000 + (Freqency/2)) / Freqency;
+ /* Calculate interval in 0.1ns interval: Interval = (1 / Frequency) * 10,000,000,000
*/
+ return 10000000000ULL / Frequency;
}
VOID
RtcSetClockRate(UCHAR ClockRate)
{
UCHAR RegisterA;
+ ULONG PreciseIncrement;
/* Update the global values */
- HalpCurrentRate = ClockRate;
- HalpCurrentTimeIncrement = RtcClockRateToIncrement(ClockRate);
+ HalpCurrentClockRate = ClockRate;
+ PreciseIncrement = RtcClockRateToPreciseIncrement(ClockRate);
+ HalpCurrentTimeIncrement = PreciseIncrement / 1000;
+ HalpCurrentFractionalIncrement = PreciseIncrement % 1000;
/* Acquire CMOS lock */
HalpAcquireCmosSpinLock();
@@ -113,14 +119,17 @@ HalpInitializeClock(VOID)
HalpReleaseCmosSpinLock();
/* Set initial rate */
- RtcSetClockRate(HalpCurrentRate);
+ RtcSetClockRate(HalpCurrentClockRate);
/* Restore interrupt state */
__writeeflags(EFlags);
+ /* Calculate minumum and maximum increment */
+ HalpMinimumTimeIncrement = RtcClockRateToPreciseIncrement(RtcMinimumClockRate) /
1000;
+ HalpMaximumTimeIncrement = RtcClockRateToPreciseIncrement(RtcMaximumClockRate) /
1000;
+
/* Notify the kernel about the maximum and minimum increment */
- KeSetTimeIncrement(RtcClockRateToIncrement(RtcMaximumClockRate),
- RtcClockRateToIncrement(RtcMinimumClockRate));
+ KeSetTimeIncrement(HalpMaximumTimeIncrement, HalpMinimumTimeIncrement);
/* Enable the timer interrupt */
HalEnableSystemInterrupt(APIC_CLOCK_VECTOR, CLOCK_LEVEL, Latched);
@@ -155,14 +164,22 @@ HalpClockInterruptHandler(IN PKTRAP_FRAME TrapFrame)
/* Save increment */
LastIncrement = HalpCurrentTimeIncrement;
+ /* Check if the running fraction has accounted for 100 ns */
+ HalpRunningFraction += HalpCurrentFractionalIncrement;
+ if (HalpRunningFraction >= 1000)
+ {
+ LastIncrement++;
+ HalpRunningFraction -= 1000;
+ }
+
/* Check if someone changed the time rate */
- if (HalpClockSetMSRate)
+ if (HalpSetClockRate)
{
/* Set new clock rate */
- RtcSetClockRate(HalpNextMSRate);
+ RtcSetClockRate(HalpNextClockRate);
/* We're done */
- HalpClockSetMSRate = FALSE;
+ HalpSetClockRate = FALSE;
}
/* Update the system time -- on x86 the kernel will exit this trap */
@@ -174,18 +191,20 @@ NTAPI
HalSetTimeIncrement(IN ULONG Increment)
{
UCHAR Rate;
+ ULONG CurrentIncrement;
/* Lookup largest value below given Increment */
for (Rate = RtcMinimumClockRate; Rate <= RtcMaximumClockRate; Rate++)
{
/* Check if this is the largest rate possible */
- if (RtcClockRateToIncrement(Rate + 1) > Increment) break;
+ CurrentIncrement = RtcClockRateToPreciseIncrement(Rate + 1) / 1000;
+ if (Increment > CurrentIncrement) break;
}
/* Set the rate and tell HAL we want to change it */
- HalpNextMSRate = Rate;
- HalpClockSetMSRate = TRUE;
+ HalpNextClockRate = Rate;
+ HalpSetClockRate = TRUE;
/* Return the real increment */
- return RtcClockRateToIncrement(Rate);
+ return RtcClockRateToPreciseIncrement(Rate) / 1000;
}