1use bit_set::BitSet;
2use celox_design::DomainKind;
3use fxhash::FxHashMap;
4
5use crate::{
6 AbsoluteAddr, SignalRef, SimulatorErrorCode,
7 backend::{EventHandle, SimBackend},
8 scheduler::{ClockDef, Scheduler, SimEvent},
9};
10
11pub trait SimulationExecutor {
16 type Backend: SimBackend;
17
18 fn backend(&self) -> &Self::Backend;
19 fn backend_mut(&mut self) -> &mut Self::Backend;
20 fn eval_comb(&mut self) -> Result<(), SimulatorErrorCode>;
21 fn eval_apply_ff_at(
22 &mut self,
23 event: <Self::Backend as SimBackend>::Event,
24 ) -> Result<(), SimulatorErrorCode>;
25 fn eval_only_ff_at(
26 &mut self,
27 event: <Self::Backend as SimBackend>::Event,
28 ) -> Result<(), SimulatorErrorCode>;
29 fn apply_ff_at(
30 &mut self,
31 event: <Self::Backend as SimBackend>::Event,
32 ) -> Result<(), SimulatorErrorCode>;
33
34 fn stage_external_event(
37 &mut self,
38 _event: <Self::Backend as SimBackend>::Event,
39 _timestamp: u64,
40 ) -> Result<(), SimulatorErrorCode> {
41 Ok(())
42 }
43
44 fn fire_external_event(
47 &mut self,
48 _event: <Self::Backend as SimBackend>::Event,
49 _timestamp: u64,
50 ) -> Result<(), SimulatorErrorCode> {
51 Ok(())
52 }
53
54 fn finish_timed_step(&mut self, _timestamp: u64) {}
56}
57
58pub struct EventInfo<B: SimBackend> {
60 pub canonical_id: usize,
61 pub is_cascaded: bool,
62 pub eval_ff_event: Option<B::Event>,
63 pub eval_only_event: Option<B::Event>,
64 pub apply_event: Option<B::Event>,
65}
66
67impl<B: SimBackend> Clone for EventInfo<B> {
68 fn clone(&self) -> Self {
69 *self
70 }
71}
72
73impl<B: SimBackend> Copy for EventInfo<B> {}
74
75impl<B: SimBackend> std::fmt::Debug for EventInfo<B> {
76 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
77 f.debug_struct("EventInfo")
78 .field("canonical_id", &self.canonical_id)
79 .field("is_cascaded", &self.is_cascaded)
80 .field("eval_ff_event", &self.eval_ff_event)
81 .field("eval_only_event", &self.eval_only_event)
82 .field("apply_event", &self.apply_event)
83 .finish()
84 }
85}
86
87#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
88struct PeriodicEventKey {
89 time: u64,
90 event_id: usize,
91 event_addr: AbsoluteAddr,
92 signal: SignalRef,
93 next_val: u8,
94}
95
96impl PeriodicEventKey {
97 fn from_event<B: SimBackend>(event: &SimEvent<B>) -> Self {
98 Self {
99 time: event.time,
100 event_id: event.event_ref.id(),
101 event_addr: event.event_ref.addr(),
102 signal: event.signal,
103 next_val: event.next_val,
104 }
105 }
106}
107
108pub struct SimulationState<B: SimBackend> {
110 scheduler: Scheduler<B>,
111 periodic_events: FxHashMap<PeriodicEventKey, usize>,
112 last_clock_values: BitSet,
113 topo_signals: Vec<(SignalRef, usize, usize)>,
114 domain_kinds: Vec<Option<DomainKind>>,
115 event_info: Vec<EventInfo<B>>,
116 signal_to_id: FxHashMap<SignalRef, usize>,
117}
118
119impl<B: SimBackend> SimulationState<B> {
120 pub fn synchronize_event_values(&mut self, backend: &B) {
122 self.last_clock_values.make_empty();
123 for (signal, id, _) in &self.topo_signals {
124 if *id == usize::MAX {
125 continue;
126 }
127 let value: u8 = backend.get_as(*signal);
128 if value != 0 {
129 self.last_clock_values.insert(*id);
130 }
131 }
132 }
133
134 fn replace_triggers_with_stable_edges(&self, backend: &mut B) {
135 backend.clear_triggered_bits();
136 for (signal, id, _) in &self.topo_signals {
137 if *id == usize::MAX {
138 continue;
139 }
140 let was_nonzero = self.last_clock_values.contains(*id);
141 let value: u8 = backend.get_as(*signal);
142 let is_nonzero = value != 0;
143 let triggered = match self.domain_kinds[*id] {
144 Some(DomainKind::ClockPosedge | DomainKind::ResetAsyncHigh) => {
145 !was_nonzero && is_nonzero
146 }
147 Some(DomainKind::ClockNegedge | DomainKind::ResetAsyncLow) => {
148 was_nonzero && !is_nonzero
149 }
150 _ => was_nonzero != is_nonzero,
151 };
152 if triggered {
153 backend.mark_triggered_bit(*id);
154 }
155 }
156 }
157
158 pub fn new(
159 backend: &B,
160 topo_signals: Vec<(SignalRef, usize, usize)>,
161 domain_kinds: Vec<Option<DomainKind>>,
162 event_info: Vec<EventInfo<B>>,
163 ) -> Self {
164 let mut last_clock_values = BitSet::with_capacity(backend.num_events());
165 let mut signal_to_id = FxHashMap::default();
166 for (signal, id, _) in topo_signals.iter().copied() {
167 if id == usize::MAX {
168 continue;
169 }
170 signal_to_id.insert(signal, id);
171 let value: u8 = backend.get_as(signal);
172 if value != 0 {
173 last_clock_values.insert(id);
174 }
175 }
176
177 Self {
178 scheduler: Scheduler::new(),
179 periodic_events: FxHashMap::default(),
180 last_clock_values,
181 topo_signals,
182 domain_kinds,
183 event_info,
184 signal_to_id,
185 }
186 }
187
188 pub fn add_clock(
189 &mut self,
190 event: B::Event,
191 signal: SignalRef,
192 period: u64,
193 initial_delay: u64,
194 ) {
195 let event_id = event.id();
196 if event_id >= self.scheduler.clocks.len() {
197 self.scheduler.clocks.resize(event_id + 1, None);
198 }
199 self.scheduler.clocks[event_id] = Some(ClockDef { period });
200 self.push_periodic_event(SimEvent {
201 time: initial_delay,
202 event_ref: event,
203 signal,
204 next_val: 1,
205 });
206 }
207
208 pub fn schedule(&mut self, event: B::Event, signal: SignalRef, time: u64, value: u8) {
209 self.scheduler.push(SimEvent {
210 time,
211 event_ref: event,
212 signal,
213 next_val: value,
214 });
215 }
216
217 fn push_periodic_event(&mut self, event: SimEvent<B>) {
218 *self
219 .periodic_events
220 .entry(PeriodicEventKey::from_event(&event))
221 .or_default() += 1;
222 self.scheduler.push(event);
223 }
224
225 pub fn step<E>(&mut self, executor: &mut E) -> Result<Option<u64>, SimulatorErrorCode>
226 where
227 E: SimulationExecutor<Backend = B>,
228 {
229 let (current_time, events_to_process) = match self.scheduler.pop_all_at_next_time() {
230 Some(events) => events,
231 None => return Ok(None),
232 };
233 self.step_events(executor, current_time, events_to_process)
234 }
235
236 pub fn settle_at<E>(
239 &mut self,
240 executor: &mut E,
241 time: u64,
242 ) -> Result<Option<u64>, SimulatorErrorCode>
243 where
244 E: SimulationExecutor<Backend = B>,
245 {
246 self.step_events(executor, time, Vec::new())
247 }
248
249 fn step_events<E>(
250 &mut self,
251 executor: &mut E,
252 current_time: u64,
253 events_to_process: Vec<SimEvent<B>>,
254 ) -> Result<Option<u64>, SimulatorErrorCode>
255 where
256 E: SimulationExecutor<Backend = B>,
257 {
258 self.scheduler.time = current_time;
259
260 let mut periodic_events_to_process: Vec<SimEvent<B>> = Vec::new();
264 for event in &events_to_process {
265 let key = PeriodicEventKey::from_event(event);
266 let mut remove_key = false;
267 if let Some(count) = self.periodic_events.get_mut(&key) {
268 periodic_events_to_process.push(SimEvent {
269 time: event.time,
270 event_ref: event.event_ref,
271 signal: event.signal,
272 next_val: event.next_val,
273 });
274 *count -= 1;
275 remove_key = *count == 0;
276 }
277 if remove_key {
278 self.periodic_events.remove(&key);
279 }
280 }
281 debug_assert!(
282 self.periodic_events
283 .keys()
284 .all(|event| event.time > current_time),
285 "periodic event sidecar fell behind the scheduler"
286 );
287
288 let num_events = executor.backend().num_events();
289 for event in &events_to_process {
290 executor.backend_mut().set(event.signal, event.next_val);
291 }
292
293 let mut triggered_domains = BitSet::with_capacity(num_events);
294 let mut discovered_in_this_step = BitSet::with_capacity(num_events);
295 let mut scheduled_trigger_ids = BitSet::with_capacity(num_events);
296 let mut track_stable_edges = events_to_process.is_empty();
300 executor.backend_mut().clear_triggered_bits();
301
302 for event in &events_to_process {
303 if let Some(&id) = self.signal_to_id.get(&event.signal) {
304 track_stable_edges = true;
305 let was_nonzero = self.last_clock_values.contains(id);
306 let is_nonzero = event.next_val != 0;
307 let triggered = match self.domain_kinds[id] {
308 Some(DomainKind::ClockPosedge | DomainKind::ResetAsyncHigh) => {
309 !was_nonzero && is_nonzero
310 }
311 Some(DomainKind::ClockNegedge | DomainKind::ResetAsyncLow) => {
312 was_nonzero && !is_nonzero
313 }
314 _ => !was_nonzero && is_nonzero,
315 };
316 if triggered {
317 scheduled_trigger_ids.insert(id);
318 executor.backend_mut().mark_triggered_bit(id);
319 }
320 }
321 }
322
323 executor.eval_comb()?;
324 if track_stable_edges {
325 if scheduled_trigger_ids.is_empty() {
331 self.replace_triggers_with_stable_edges(executor.backend_mut());
332 } else {
333 executor.backend_mut().clear_triggered_bits();
334 for id in scheduled_trigger_ids.iter() {
335 executor.backend_mut().mark_triggered_bit(id);
336 }
337 }
338 }
339
340 let mut comb_already_done = false;
341 loop {
342 let mut any_new_outer_loop_trigger = false;
343 let mut newly_triggered = Vec::new();
344
345 loop {
346 let mut any_new_sequential_trigger = false;
347 let marked_bits = executor.backend().get_triggered_bits();
348 executor.backend_mut().clear_triggered_bits();
349
350 let mut can_use_eval_apply =
351 triggered_domains.is_empty() && marked_bits.count() == 1;
352 if can_use_eval_apply {
353 let single_id = marked_bits.iter().next().expect("one marked trigger");
354 let info = self.event_info[single_id];
355 can_use_eval_apply = !info.is_cascaded;
356 if can_use_eval_apply {
357 if let Some(event) = info.eval_ff_event {
358 discovered_in_this_step.insert(single_id);
359 triggered_domains.insert(info.canonical_id);
360 any_new_outer_loop_trigger = true;
361 executor.stage_external_event(event, current_time)?;
362 executor.eval_apply_ff_at(event)?;
363 executor.fire_external_event(event, current_time)?;
364 executor.eval_comb()?;
365 if track_stable_edges {
366 self.replace_triggers_with_stable_edges(executor.backend_mut());
367 }
368 comb_already_done = true;
369 break;
370 }
371 }
372 }
373
374 for id in marked_bits.iter() {
375 if discovered_in_this_step.contains(id) {
376 continue;
377 }
378 discovered_in_this_step.insert(id);
379
380 let info = self.event_info[id];
381 if triggered_domains.contains(info.canonical_id) {
382 continue;
383 }
384 triggered_domains.insert(info.canonical_id);
385 any_new_sequential_trigger = true;
386 newly_triggered.push(info.canonical_id);
387
388 if let Some(event) = info.eval_only_event {
389 executor.stage_external_event(
390 info.eval_ff_event.unwrap_or(event),
391 current_time,
392 )?;
393 executor.eval_only_ff_at(event)?;
394 } else if let Some(event) = info.eval_ff_event {
395 executor.stage_external_event(event, current_time)?;
396 executor.eval_apply_ff_at(event)?;
397 } else {
398 unreachable!(
399 "FF trigger discovered without a corresponding execution unit"
400 );
401 }
402 }
403
404 if !any_new_sequential_trigger {
405 break;
406 }
407 }
408
409 if newly_triggered.is_empty() && !any_new_outer_loop_trigger {
410 break;
411 }
412
413 for id in &newly_triggered {
414 if let Some(event) = self.event_info[*id].apply_event {
415 executor.apply_ff_at(event)?;
416 }
417 }
418 for id in &newly_triggered {
419 if let Some(event) = self.event_info[*id].eval_ff_event {
420 executor.fire_external_event(event, current_time)?;
421 }
422 }
423
424 if comb_already_done {
425 comb_already_done = false;
426 } else {
427 executor.eval_comb()?;
428 if track_stable_edges {
429 self.replace_triggers_with_stable_edges(executor.backend_mut());
430 }
431 }
432 }
433
434 for (signal, id, _) in &self.topo_signals {
435 if *id == usize::MAX {
436 continue;
437 }
438 let value: u8 = executor.backend().get_as(*signal);
439 if value != 0 {
440 self.last_clock_values.insert(*id);
441 } else {
442 self.last_clock_values.remove(*id);
443 }
444 }
445
446 for event in periodic_events_to_process {
447 let event_id = event.event_ref.id();
448 if let Some(Some(clock)) = self.scheduler.clocks.get(event_id) {
449 self.push_periodic_event(SimEvent {
450 time: current_time + clock.period / 2,
451 event_ref: event.event_ref,
452 signal: event.signal,
453 next_val: 1 - event.next_val,
454 });
455 }
456 }
457
458 executor.finish_timed_step(current_time);
459 Ok(Some(current_time))
460 }
461
462 pub fn time(&self) -> u64 {
463 self.scheduler.time
464 }
465
466 pub fn set_time(&mut self, time: u64) {
467 self.scheduler.time = time;
468 }
469
470 pub fn next_event_time(&self) -> Option<u64> {
471 self.scheduler.next_event_time()
472 }
473}