1use std::time::Duration;
2
3use shadow_shim_helper_rs::{emulated_time::EmulatedTime, simulation_time::SimulationTime};
4
5pub struct Cpu {
9 simulated_frequency: u64,
10 native_frequency: u64,
11 threshold: Option<SimulationTime>,
12 precision: Option<SimulationTime>,
13 now: EmulatedTime,
14 time_cpu_available: EmulatedTime,
15}
16
17impl Cpu {
18 pub fn new(
24 simulated_frequency: u64,
25 native_frequency: u64,
26 threshold: Option<SimulationTime>,
27 precision: Option<SimulationTime>,
28 ) -> Self {
29 if let Some(precision) = precision {
30 assert!(precision > SimulationTime::ZERO)
31 }
32
33 Self {
34 simulated_frequency,
35 native_frequency,
36 threshold,
37 precision,
38 now: EmulatedTime::MIN,
39 time_cpu_available: EmulatedTime::MIN,
40 }
41 }
42
43 pub fn update_time(&mut self, now: EmulatedTime) {
45 self.now = now;
46 }
47
48 pub fn add_delay(&mut self, native_delay: Duration) {
50 let cycles = native_delay
53 .as_nanos()
54 .checked_mul(self.native_frequency as u128)
55 .unwrap();
56 let simulated_delay_nanos = cycles / (self.simulated_frequency as u128);
57 let mut adjusted_delay =
60 SimulationTime::from_nanos(simulated_delay_nanos.try_into().unwrap());
61
62 if let Some(precision) = self.precision {
64 let remainder = adjusted_delay % precision;
65
66 adjusted_delay -= remainder;
68
69 let half_precision = precision / 2;
71 if remainder >= half_precision {
72 adjusted_delay += precision;
74 }
75 }
76
77 self.time_cpu_available += adjusted_delay;
78 }
79
80 pub fn delay(&self) -> SimulationTime {
82 let Some(threshold) = self.threshold else {
83 return SimulationTime::ZERO;
84 };
85 let Some(built_up_delay) = self.time_cpu_available.checked_duration_since(&self.now) else {
86 return SimulationTime::ZERO;
87 };
88 if built_up_delay > threshold {
89 built_up_delay
90 } else {
91 SimulationTime::ZERO
92 }
93 }
94}
95
96#[cfg(test)]
97mod tests {
98 use super::*;
99
100 const MHZ: u64 = 1_000_000;
101
102 #[test]
103 fn no_threshold_never_delays() {
104 let mut cpu = Cpu::new(1000 * MHZ, 1000 * MHZ, None, None);
105 assert_eq!(cpu.delay(), SimulationTime::ZERO);
106
107 cpu.add_delay(Duration::from_secs(1));
108 assert_eq!(cpu.delay(), SimulationTime::ZERO);
109 }
110
111 #[test]
112 fn basic_delay() {
113 let mut cpu = Cpu::new(
114 1000 * MHZ,
115 1000 * MHZ,
116 Some(SimulationTime::NANOSECOND),
117 None,
118 );
119 assert_eq!(cpu.delay(), SimulationTime::ZERO);
120
121 cpu.update_time(EmulatedTime::UNIX_EPOCH);
123
124 cpu.add_delay(Duration::from_secs(1));
126
127 assert_eq!(cpu.delay(), SimulationTime::SECOND);
129
130 cpu.update_time(EmulatedTime::UNIX_EPOCH + SimulationTime::from_millis(100));
132 assert_eq!(cpu.delay(), SimulationTime::from_millis(900));
133
134 cpu.update_time(EmulatedTime::UNIX_EPOCH + SimulationTime::from_secs(1));
136 assert_eq!(cpu.delay(), SimulationTime::ZERO);
137
138 cpu.update_time(EmulatedTime::UNIX_EPOCH + SimulationTime::from_secs(2));
140 assert_eq!(cpu.delay(), SimulationTime::ZERO);
141 }
142
143 #[test]
144 fn no_overflow() {
145 let mut cpu = Cpu::new(
147 1_000_000 * MHZ,
148 1_000_000 * MHZ,
149 Some(SimulationTime::NANOSECOND),
150 None,
151 );
152
153 cpu.add_delay(Duration::from_secs(3600));
155
156 assert_eq!(cpu.delay(), SimulationTime::from_secs(3600));
157 }
158
159 #[test]
160 fn faster_native() {
161 let mut cpu = Cpu::new(
162 1000 * MHZ,
163 1100 * MHZ,
164 Some(SimulationTime::NANOSECOND),
165 None,
166 );
167 assert_eq!(cpu.delay(), SimulationTime::ZERO);
168
169 cpu.add_delay(Duration::from_millis(1000));
171 assert_eq!(cpu.delay(), SimulationTime::from_millis(1100));
172 }
173
174 #[test]
175 fn faster_simulated() {
176 let mut cpu = Cpu::new(
177 1100 * MHZ,
178 1000 * MHZ,
179 Some(SimulationTime::NANOSECOND),
180 None,
181 );
182 assert_eq!(cpu.delay(), SimulationTime::ZERO);
183
184 cpu.add_delay(Duration::from_millis(1100));
186 assert_eq!(cpu.delay(), SimulationTime::from_millis(1000));
187 }
188
189 #[test]
190 fn thresholded() {
191 let threshold = SimulationTime::from_millis(100);
192 let mut cpu = Cpu::new(1000 * MHZ, 1000 * MHZ, Some(threshold), None);
193 assert_eq!(cpu.delay(), SimulationTime::ZERO);
194
195 cpu.add_delay(Duration::from_millis(1));
197
198 assert_eq!(cpu.delay(), SimulationTime::ZERO);
200
201 cpu.add_delay(Duration::from_millis(100));
203
204 assert_eq!(cpu.delay(), SimulationTime::from_millis(101));
206 }
207
208 #[test]
209 fn round_lt_half_precision() {
210 let precision = SimulationTime::from_millis(100);
211 let mut cpu = Cpu::new(
212 1000 * MHZ,
213 1000 * MHZ,
214 Some(SimulationTime::NANOSECOND),
215 Some(precision),
216 );
217 cpu.add_delay(Duration::from_millis(149));
218 assert_eq!(cpu.delay(), SimulationTime::from_millis(100));
219 }
220
221 #[test]
222 fn round_half_precision() {
223 let precision = SimulationTime::from_millis(100);
224 let mut cpu = Cpu::new(
225 1000 * MHZ,
226 1000 * MHZ,
227 Some(SimulationTime::NANOSECOND),
228 Some(precision),
229 );
230 cpu.add_delay(Duration::from_millis(150));
231 assert_eq!(cpu.delay(), SimulationTime::from_millis(200));
232 }
233
234 #[test]
235 fn round_gt_half_precision() {
236 let precision = SimulationTime::from_millis(100);
237 let mut cpu = Cpu::new(
238 1000 * MHZ,
239 1000 * MHZ,
240 Some(SimulationTime::NANOSECOND),
241 Some(precision),
242 );
243 cpu.add_delay(Duration::from_millis(151));
244 assert_eq!(cpu.delay(), SimulationTime::from_millis(200));
245 }
246}