1
+ #include <stdbool.h>
1
2
#include <time.h>
2
3
3
4
#include "utils.h"
19
20
#endif
20
21
#endif
21
22
23
+ bool boot_complete = false;
24
+ static double ticks_increment ;
25
+ static double boot_ticks ;
26
+
22
27
/* Calculate "x * n / d" without unnecessary overflow or loss of precision.
23
28
*
24
29
* Reference:
@@ -32,35 +37,93 @@ static inline uint64_t mult_frac(uint64_t x, uint64_t n, uint64_t d)
32
37
return q * n + r * n / d ;
33
38
}
34
39
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 ()
42
50
{
43
51
#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
+
47
56
#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
+
53
65
#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 ;
55
69
#endif
56
70
}
57
71
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
+
58
121
uint64_t semu_timer_get (semu_timer_t * timer )
59
122
{
60
- return semu_timer_clocksource (timer -> freq ) - timer -> begin ;
123
+ return semu_timer_clocksource (timer ) - timer -> begin ;
61
124
}
62
125
63
126
void semu_timer_rebase (semu_timer_t * timer , uint64_t time )
64
127
{
65
- timer -> begin = semu_timer_clocksource (timer -> freq ) - time ;
128
+ timer -> begin = semu_timer_clocksource (timer ) - time ;
66
129
}
0 commit comments