1+ #include <stdbool.h>
12#include <time.h>
23
34#include "utils.h"
1920#endif
2021#endif
2122
23+ bool boot_complete = false;
24+ static double ticks_increment ;
25+ static double boot_ticks ;
26+
2227/* Calculate "x * n / d" without unnecessary overflow or loss of precision.
2328 *
2429 * Reference:
@@ -32,35 +37,93 @@ static inline uint64_t mult_frac(uint64_t x, uint64_t n, uint64_t d)
3237 return q * n + r * n / d ;
3338}
3439
35- void semu_timer_init (semu_timer_t * timer , uint64_t freq )
36- {
37- timer -> freq = freq ;
38- semu_timer_rebase (timer , 0 );
39- }
40-
41- static uint64_t semu_timer_clocksource (uint64_t freq )
40+ /* High-precision time measurement:
41+ * - POSIX systems: clock_gettime() for nanosecond precision
42+ * - macOS: mach_absolute_time() with timebase conversion
43+ * - Other platforms: time(0) with conversion to nanoseconds as fallback
44+ *
45+ * The platform-specific timing logic is now clearly separated: POSIX and macOS
46+ * implementations provide high-precision measurements, while the fallback path
47+ * uses time(0) for a coarser but portable approach.
48+ */
49+ static inline uint64_t host_time_ns ()
4250{
4351#if defined(HAVE_POSIX_TIMER )
44- struct timespec t ;
45- clock_gettime (CLOCKID , & t );
46- return t .tv_sec * freq + mult_frac (t .tv_nsec , freq , 1e9 );
52+ struct timespec ts ;
53+ clock_gettime (CLOCKID , & ts );
54+ return (uint64_t ) ts .tv_sec * 1e9 + (uint64_t ) ts .tv_nsec ;
55+
4756#elif defined(HAVE_MACH_TIMER )
48- static mach_timebase_info_data_t t ;
49- if (t .denom == 0 )
50- (void ) mach_timebase_info (& t );
51- return mult_frac (mult_frac (mach_absolute_time (), t .numer , t .denom ), freq ,
52- 1e9 );
57+ static mach_timebase_info_data_t ts = {0 };
58+ if (ts .denom == 0 )
59+ (void ) mach_timebase_info (& ts );
60+
61+ uint64_t now = mach_absolute_time ();
62+ /* convert to nanoseconds: (now * t.numer / t.denom) */
63+ return mult_frac (now , ts .numer , (uint64_t ) ts .denom );
64+
5365#else
54- return time (0 ) * freq ;
66+ /* Fallback to non-HRT calls time(0) in seconds => convert to ns. */
67+ time_t now_sec = time (0 );
68+ return (uint64_t ) now_sec * 1e9 ;
5569#endif
5670}
5771
72+ /* The function that returns the "emulator time" in ticks.
73+ *
74+ * Before the boot process is completed, the emulator manually manages the
75+ * growth of ticks to suppress RCU CPU stall warnings. After the boot process is
76+ * completed, the emulator switches back to the real-time timer, using an offset
77+ * bridging to ensure that the ticks of both timers remain consistent.
78+ */
79+ static uint64_t semu_timer_clocksource (semu_timer_t * timer )
80+ {
81+ /* After boot process complete, the timer will switch to real time. Thus,
82+ * there is an offset between the real time and the emulator time.
83+ *
84+ * After switching to real time, the correct way to update time is to
85+ * calculate the increment of time. Then add it to the emulator time.
86+ */
87+ static int64_t offset = 0 ;
88+ static bool first_switch = true;
89+
90+ if (!boot_complete ) {
91+ boot_ticks += ticks_increment ;
92+ return (uint64_t ) boot_ticks ;
93+ }
94+
95+ uint64_t real_ticks = mult_frac (host_time_ns (), timer -> freq , 1e9 );
96+ if (first_switch ) {
97+ first_switch = false;
98+
99+ /* Calculate the offset between the real time and the emulator time */
100+ offset = (int64_t ) (real_ticks - boot_ticks );
101+ }
102+ return (uint64_t ) ((int64_t ) real_ticks - offset );
103+ }
104+
105+ void semu_timer_init (semu_timer_t * timer , uint64_t freq , int n_harts )
106+ {
107+ timer -> freq = freq ;
108+ timer -> begin = mult_frac (host_time_ns (), timer -> freq , 1e9 );
109+ boot_ticks = timer -> begin ; /* Initialize the fake ticks for boot process */
110+
111+ /* According to statistics, the number of times 'semu_timer_clocksource'
112+ * called is approximately 'SMP count * 2.15 * 1e8'. By the time the boot
113+ * process is completed, the emulator will have a total of 'boot seconds *
114+ * frequency' ticks. Therefore, each time, '(boot seconds * frequency) /
115+ * (2.15 * 1e8 * SMP count)' ticks need to be added.
116+ */
117+ ticks_increment =
118+ (SEMU_BOOT_TARGET_TIME * CLOCK_FREQ ) / (2.15 * 1e8 * n_harts );
119+ }
120+
58121uint64_t semu_timer_get (semu_timer_t * timer )
59122{
60- return semu_timer_clocksource (timer -> freq ) - timer -> begin ;
123+ return semu_timer_clocksource (timer ) - timer -> begin ;
61124}
62125
63126void semu_timer_rebase (semu_timer_t * timer , uint64_t time )
64127{
65- timer -> begin = semu_timer_clocksource (timer -> freq ) - time ;
128+ timer -> begin = semu_timer_clocksource (timer ) - time ;
66129}
0 commit comments