1use std::{cmp::Reverse, collections::BinaryHeap};
2
3use bit_set::BitSet;
4use celox_design::{DomainKind, ProcessStatus};
5use fxhash::FxHashMap;
6
7use crate::{
8 AbsoluteAddr, SignalRef, SimulatorErrorCode,
9 backend::{EventHandle, SimBackend},
10 scheduler::{ClockDef, Scheduler, SimEvent},
11};
12
13pub trait SimulationExecutor {
18 type Backend: SimBackend;
19
20 fn backend(&self) -> &Self::Backend;
21 fn backend_mut(&mut self) -> &mut Self::Backend;
22 fn eval_comb(&mut self) -> Result<(), SimulatorErrorCode>;
23 fn eval_apply_ff_at(
24 &mut self,
25 event: <Self::Backend as SimBackend>::Event,
26 ) -> Result<(), SimulatorErrorCode>;
27 fn eval_only_ff_at(
28 &mut self,
29 event: <Self::Backend as SimBackend>::Event,
30 ) -> Result<(), SimulatorErrorCode>;
31 fn apply_ff_at(
32 &mut self,
33 event: <Self::Backend as SimBackend>::Event,
34 ) -> Result<(), SimulatorErrorCode>;
35 fn run_process(&mut self, index: usize) -> Result<(), SimulatorErrorCode> {
36 self.backend_mut().run_process(index)
37 }
38
39 fn stage_external_event(
42 &mut self,
43 _event: <Self::Backend as SimBackend>::Event,
44 _timestamp: u64,
45 ) -> Result<(), SimulatorErrorCode> {
46 Ok(())
47 }
48
49 fn fire_external_event(
52 &mut self,
53 _event: <Self::Backend as SimBackend>::Event,
54 _timestamp: u64,
55 ) -> Result<(), SimulatorErrorCode> {
56 Ok(())
57 }
58
59 fn finish_timed_step(&mut self, _timestamp: u64) {}
61}
62
63#[derive(Clone, Copy, Debug, PartialEq, Eq)]
65pub struct ProcessRefs {
66 pub status: SignalRef,
67 pub delay: SignalRef,
68}
69
70type ProcessWakeup = Reverse<(u64, usize)>;
74
75pub struct EventInfo<B: SimBackend> {
77 pub canonical_id: usize,
78 pub is_cascaded: bool,
79 pub eval_ff_event: Option<B::Event>,
80 pub eval_only_event: Option<B::Event>,
81 pub apply_event: Option<B::Event>,
82}
83
84impl<B: SimBackend> Clone for EventInfo<B> {
85 fn clone(&self) -> Self {
86 *self
87 }
88}
89
90impl<B: SimBackend> Copy for EventInfo<B> {}
91
92impl<B: SimBackend> std::fmt::Debug for EventInfo<B> {
93 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
94 f.debug_struct("EventInfo")
95 .field("canonical_id", &self.canonical_id)
96 .field("is_cascaded", &self.is_cascaded)
97 .field("eval_ff_event", &self.eval_ff_event)
98 .field("eval_only_event", &self.eval_only_event)
99 .field("apply_event", &self.apply_event)
100 .finish()
101 }
102}
103
104#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
105struct PeriodicEventKey {
106 time: u64,
107 event_id: usize,
108 event_addr: AbsoluteAddr,
109 signal: SignalRef,
110 next_val: u8,
111}
112
113impl PeriodicEventKey {
114 fn from_event<B: SimBackend>(event: &SimEvent<B>) -> Self {
115 Self {
116 time: event.time,
117 event_id: event.event_ref.id(),
118 event_addr: event.event_ref.addr(),
119 signal: event.signal,
120 next_val: event.next_val,
121 }
122 }
123}
124
125pub struct SimulationState<B: SimBackend> {
127 scheduler: Scheduler<B>,
128 periodic_events: FxHashMap<PeriodicEventKey, usize>,
129 last_clock_values: BitSet,
130 topo_signals: Vec<(SignalRef, usize, usize)>,
131 domain_kinds: Vec<Option<DomainKind>>,
132 event_info: Vec<EventInfo<B>>,
133 signal_to_id: FxHashMap<SignalRef, usize>,
134 signal_index_by_id: Vec<usize>,
135 processes: Vec<ProcessRefs>,
136 process_wakeups: BinaryHeap<ProcessWakeup>,
137 finished: bool,
139}
140
141impl<B: SimBackend> SimulationState<B> {
142 pub fn synchronize_event_values(&mut self, backend: &B) {
144 self.last_clock_values.make_empty();
145 for (signal, id, _) in &self.topo_signals {
146 if *id == usize::MAX {
147 continue;
148 }
149 let value: u8 = backend.get_as(*signal);
150 if value != 0 {
151 self.last_clock_values.insert(*id);
152 }
153 }
154 }
155
156 fn stable_edge_triggered(&self, backend: &B, id: usize) -> bool {
157 let Some((signal, _, _)) = self.topo_signals.get(self.signal_index_by_id[id]) else {
158 return true;
159 };
160 let was_nonzero = self.last_clock_values.contains(id);
161 let is_nonzero = backend.get_as::<u8>(*signal) != 0;
162 match self.domain_kinds[id] {
163 Some(DomainKind::ClockPosedge | DomainKind::ResetAsyncHigh) => {
164 !was_nonzero && is_nonzero
165 }
166 Some(DomainKind::ClockNegedge | DomainKind::ResetAsyncLow) => {
167 was_nonzero && !is_nonzero
168 }
169 _ => was_nonzero != is_nonzero,
170 }
171 }
172
173 fn replace_triggers_with_stable_edges(&self, backend: &mut B) {
174 backend.clear_triggered_bits();
175 for (signal, id, _) in &self.topo_signals {
176 if *id == usize::MAX {
177 continue;
178 }
179 let was_nonzero = self.last_clock_values.contains(*id);
180 let value: u8 = backend.get_as(*signal);
181 let is_nonzero = value != 0;
182 let triggered = match self.domain_kinds[*id] {
183 Some(DomainKind::ClockPosedge | DomainKind::ResetAsyncHigh) => {
184 !was_nonzero && is_nonzero
185 }
186 Some(DomainKind::ClockNegedge | DomainKind::ResetAsyncLow) => {
187 was_nonzero && !is_nonzero
188 }
189 _ => was_nonzero != is_nonzero,
190 };
191 if triggered {
192 backend.mark_triggered_bit(*id);
193 }
194 }
195 }
196
197 pub fn new(
199 backend: &B,
200 topo_signals: Vec<(SignalRef, usize, usize)>,
201 domain_kinds: Vec<Option<DomainKind>>,
202 event_info: Vec<EventInfo<B>>,
203 processes: Vec<ProcessRefs>,
204 ) -> Self {
205 let mut last_clock_values = BitSet::with_capacity(backend.num_events());
206 let mut signal_to_id = FxHashMap::default();
207 let mut signal_index_by_id = vec![usize::MAX; backend.num_events()];
208 for (index, (signal, id, _)) in topo_signals.iter().copied().enumerate() {
209 if id == usize::MAX {
210 continue;
211 }
212 signal_to_id.insert(signal, id);
213 signal_index_by_id[id] = index;
214 let value: u8 = backend.get_as(signal);
215 if value != 0 {
216 last_clock_values.insert(id);
217 }
218 }
219
220 Self {
221 scheduler: Scheduler::new(),
222 periodic_events: FxHashMap::default(),
223 last_clock_values,
224 topo_signals,
225 domain_kinds,
226 event_info,
227 signal_to_id,
228 signal_index_by_id,
229 process_wakeups: (0..processes.len())
230 .map(|process| Reverse((0, process)))
231 .collect(),
232 processes,
233 finished: false,
234 }
235 }
236
237 pub fn add_clock(
238 &mut self,
239 event: B::Event,
240 signal: SignalRef,
241 period: u64,
242 initial_delay: u64,
243 ) {
244 let event_id = event.id();
245 if event_id >= self.scheduler.clocks.len() {
246 self.scheduler.clocks.resize(event_id + 1, None);
247 }
248 self.scheduler.clocks[event_id] = Some(ClockDef {
249 period: period.max(2),
250 });
251 self.push_periodic_event(SimEvent {
252 time: initial_delay,
253 event_ref: event,
254 signal,
255 next_val: 1,
256 });
257 }
258
259 pub fn schedule(&mut self, event: B::Event, signal: SignalRef, time: u64, value: u8) {
260 self.scheduler.push(SimEvent {
261 time,
262 event_ref: event,
263 signal,
264 next_val: value,
265 });
266 }
267
268 fn push_periodic_event(&mut self, event: SimEvent<B>) {
269 *self
270 .periodic_events
271 .entry(PeriodicEventKey::from_event(&event))
272 .or_default() += 1;
273 self.scheduler.push(event);
274 }
275
276 pub fn step<E>(&mut self, executor: &mut E) -> Result<Option<u64>, SimulatorErrorCode>
277 where
278 E: SimulationExecutor<Backend = B>,
279 {
280 let Some(current_time) = self.next_event_time() else {
281 return Ok(None);
282 };
283 let events_to_process = if self.scheduler.next_event_time() == Some(current_time) {
284 self.scheduler
285 .pop_all_at_next_time()
286 .map(|(_, events)| events)
287 .unwrap_or_default()
288 } else {
289 Vec::new()
290 };
291 let ready = self.take_ready_processes(current_time);
292 self.step_round(executor, current_time, events_to_process, ready)?;
293 while !self.finished {
296 let ready = self.take_ready_processes(current_time);
297 if ready.is_empty() {
298 break;
299 }
300 self.step_round(executor, current_time, Vec::new(), ready)?;
301 }
302 executor.finish_timed_step(current_time);
303 Ok(Some(current_time))
304 }
305
306 fn take_ready_processes(&mut self, time: u64) -> Vec<usize> {
308 let mut ready = Vec::new();
309 while let Some(&Reverse((wakeup, process))) = self.process_wakeups.peek() {
310 if wakeup != time {
311 break;
312 }
313 self.process_wakeups.pop();
314 ready.push(process);
315 }
316 ready
317 }
318
319 fn run_processes<E>(
322 &mut self,
323 executor: &mut E,
324 current_time: u64,
325 ready: Vec<usize>,
326 ) -> Result<(), SimulatorErrorCode>
327 where
328 E: SimulationExecutor<Backend = B>,
329 {
330 for process in ready {
331 executor.run_process(process)?;
332 let refs = self.processes[process];
333 let status: u8 = executor.backend().get_as(refs.status);
334 match ProcessStatus::from_code(status) {
335 Some(ProcessStatus::Delay) => {
336 let delay: u64 = executor.backend().get_as(refs.delay);
337 let time = current_time.checked_add(delay).ok_or_else(|| {
338 SimulatorErrorCode::Runtime {
339 message: format!("process {process} delay overflows simulation time"),
340 signals: Vec::new(),
341 }
342 })?;
343 self.process_wakeups.push(Reverse((time, process)));
344 }
345 Some(ProcessStatus::Done) => {}
346 Some(ProcessStatus::Finish) => {
347 self.finished = true;
348 break;
349 }
350 None => return Err(SimulatorErrorCode::InternalError),
351 }
352 }
353 Ok(())
354 }
355
356 pub fn is_finished(&self) -> bool {
358 self.finished
359 }
360
361 pub fn settle_at<E>(
364 &mut self,
365 executor: &mut E,
366 time: u64,
367 ) -> Result<Option<u64>, SimulatorErrorCode>
368 where
369 E: SimulationExecutor<Backend = B>,
370 {
371 self.step_round(executor, time, Vec::new(), Vec::new())?;
372 executor.finish_timed_step(time);
373 Ok(Some(time))
374 }
375
376 fn step_round<E>(
379 &mut self,
380 executor: &mut E,
381 current_time: u64,
382 events_to_process: Vec<SimEvent<B>>,
383 ready_processes: Vec<usize>,
384 ) -> Result<(), SimulatorErrorCode>
385 where
386 E: SimulationExecutor<Backend = B>,
387 {
388 self.scheduler.time = current_time;
389
390 let mut periodic_events_to_process: Vec<SimEvent<B>> = Vec::new();
394 for event in &events_to_process {
395 let key = PeriodicEventKey::from_event(event);
396 let mut remove_key = false;
397 if let Some(count) = self.periodic_events.get_mut(&key) {
398 periodic_events_to_process.push(SimEvent {
399 time: event.time,
400 event_ref: event.event_ref,
401 signal: event.signal,
402 next_val: event.next_val,
403 });
404 *count -= 1;
405 remove_key = *count == 0;
406 }
407 if remove_key {
408 self.periodic_events.remove(&key);
409 }
410 }
411 debug_assert!(
412 self.periodic_events
413 .keys()
414 .all(|event| event.time > current_time),
415 "periodic event sidecar fell behind the scheduler"
416 );
417
418 let num_events = executor.backend().num_events();
419 for event in &events_to_process {
420 executor.backend_mut().set(event.signal, event.next_val);
421 }
422
423 let mut process_driven = Vec::new();
427 if !ready_processes.is_empty() {
428 let before: Vec<u8> = self
429 .topo_signals
430 .iter()
431 .map(|(signal, _, _)| executor.backend().get_as(*signal))
432 .collect();
433 self.run_processes(executor, current_time, ready_processes)?;
434 for ((signal, id, _), before) in self.topo_signals.iter().zip(before) {
435 if *id == usize::MAX {
436 continue;
437 }
438 let value: u8 = executor.backend().get_as(*signal);
439 if value != before {
440 process_driven.push((*id, value != 0));
441 }
442 }
443 }
444
445 let mut triggered_domains = BitSet::with_capacity(num_events);
446 let mut discovered_in_this_step = BitSet::with_capacity(num_events);
447 let mut scheduled_trigger_ids = BitSet::with_capacity(num_events);
448 let mut track_stable_edges = events_to_process.is_empty();
452 executor.backend_mut().clear_triggered_bits();
453
454 let scheduled = events_to_process
455 .iter()
456 .filter_map(|event| {
457 self.signal_to_id
458 .get(&event.signal)
459 .map(|&id| (id, event.next_val != 0))
460 })
461 .chain(process_driven);
462 for (id, is_nonzero) in scheduled {
463 track_stable_edges = true;
464 let was_nonzero = self.last_clock_values.contains(id);
465 let triggered = match self.domain_kinds[id] {
466 Some(DomainKind::ClockPosedge | DomainKind::ResetAsyncHigh) => {
467 !was_nonzero && is_nonzero
468 }
469 Some(DomainKind::ClockNegedge | DomainKind::ResetAsyncLow) => {
470 was_nonzero && !is_nonzero
471 }
472 _ => !was_nonzero && is_nonzero,
473 };
474 if triggered {
475 scheduled_trigger_ids.insert(id);
476 executor.backend_mut().mark_triggered_bit(id);
477 }
478 }
479
480 executor.eval_comb()?;
481 if track_stable_edges {
482 if scheduled_trigger_ids.is_empty() {
488 self.replace_triggers_with_stable_edges(executor.backend_mut());
489 } else {
490 executor.backend_mut().clear_triggered_bits();
491 for id in scheduled_trigger_ids.iter() {
492 executor.backend_mut().mark_triggered_bit(id);
493 }
494 }
495 }
496
497 let mut comb_already_done = false;
498 loop {
499 let mut any_new_outer_loop_trigger = false;
500 let mut newly_triggered = Vec::new();
501
502 loop {
503 let mut any_new_sequential_trigger = false;
504 let marked_bits = executor.backend().get_triggered_bits();
505 executor.backend_mut().clear_triggered_bits();
506
507 let mut can_use_eval_apply =
508 triggered_domains.is_empty() && marked_bits.count() == 1;
509 if can_use_eval_apply {
510 let single_id = marked_bits.iter().next().expect("one marked trigger");
511 let info = self.event_info[single_id];
512 can_use_eval_apply = !info.is_cascaded;
513 if can_use_eval_apply {
514 if let Some(event) = info.eval_ff_event {
515 discovered_in_this_step.insert(single_id);
516 triggered_domains.insert(info.canonical_id);
517 any_new_outer_loop_trigger = true;
518 executor.stage_external_event(event, current_time)?;
519 executor.eval_apply_ff_at(event)?;
520 executor.fire_external_event(event, current_time)?;
521 executor.eval_comb()?;
522 if track_stable_edges {
523 self.replace_triggers_with_stable_edges(executor.backend_mut());
524 }
525 comb_already_done = true;
526 break;
527 }
528 }
529 }
530
531 for id in marked_bits.iter() {
532 if discovered_in_this_step.contains(id) {
533 continue;
534 }
535 let info = self.event_info[id];
536 if triggered_domains.contains(info.canonical_id) {
537 continue;
538 }
539 if info.is_cascaded && !self.stable_edge_triggered(executor.backend(), id) {
543 continue;
544 }
545 discovered_in_this_step.insert(id);
546 triggered_domains.insert(info.canonical_id);
547 any_new_sequential_trigger = true;
548 newly_triggered.push(info.canonical_id);
549
550 if let Some(event) = info.eval_only_event {
551 executor.stage_external_event(
552 info.eval_ff_event.unwrap_or(event),
553 current_time,
554 )?;
555 executor.eval_only_ff_at(event)?;
556 } else if let Some(event) = info.eval_ff_event {
557 executor.stage_external_event(event, current_time)?;
558 executor.eval_apply_ff_at(event)?;
559 } else {
560 unreachable!(
561 "FF trigger discovered without a corresponding execution unit"
562 );
563 }
564 }
565
566 if !any_new_sequential_trigger {
567 break;
568 }
569 }
570
571 if newly_triggered.is_empty() && !any_new_outer_loop_trigger {
572 break;
573 }
574
575 for id in &newly_triggered {
576 if let Some(event) = self.event_info[*id].apply_event {
577 executor.apply_ff_at(event)?;
578 }
579 }
580 for id in &newly_triggered {
581 if let Some(event) = self.event_info[*id].eval_ff_event {
582 executor.fire_external_event(event, current_time)?;
583 }
584 }
585
586 if comb_already_done {
587 comb_already_done = false;
588 } else {
589 executor.eval_comb()?;
590 if track_stable_edges {
591 self.replace_triggers_with_stable_edges(executor.backend_mut());
592 }
593 }
594 }
595
596 for (signal, id, _) in &self.topo_signals {
597 if *id == usize::MAX {
598 continue;
599 }
600 let value: u8 = executor.backend().get_as(*signal);
601 if value != 0 {
602 self.last_clock_values.insert(*id);
603 } else {
604 self.last_clock_values.remove(*id);
605 }
606 }
607
608 for event in periodic_events_to_process {
609 let event_id = event.event_ref.id();
610 if let Some(Some(clock)) = self.scheduler.clocks.get(event_id) {
611 let period = clock.period.max(2);
615 let half = (period / 2) + ((period & 1) & u64::from(event.next_val != 0));
616 let time =
617 current_time
618 .checked_add(half)
619 .ok_or_else(|| SimulatorErrorCode::Runtime {
620 message: format!("event {event_id} overflows simulation time"),
621 signals: Vec::new(),
622 })?;
623 self.push_periodic_event(SimEvent {
624 time,
625 event_ref: event.event_ref,
626 signal: event.signal,
627 next_val: 1 - event.next_val,
628 });
629 }
630 }
631
632 Ok(())
633 }
634
635 pub fn time(&self) -> u64 {
636 self.scheduler.time
637 }
638
639 pub fn set_time(&mut self, time: u64) {
640 self.scheduler.time = time;
641 }
642
643 pub fn next_event_time(&self) -> Option<u64> {
646 if self.finished {
647 return None;
648 }
649 let process = self.process_wakeups.peek().map(|&Reverse((time, _))| time);
650 match (self.scheduler.next_event_time(), process) {
651 (Some(event), Some(process)) => Some(event.min(process)),
652 (event, process) => event.or(process),
653 }
654 }
655
656 pub fn clock_periods(&self) -> Vec<(usize, u64)> {
658 self.scheduler
659 .clocks
660 .iter()
661 .enumerate()
662 .filter_map(|(id, clock)| Some((id, clock.as_ref()?.period.max(2))))
663 .collect()
664 }
665
666 pub fn snapshot(&self) -> SimulationSnapshot<B> {
669 SimulationSnapshot {
670 time: self.scheduler.time,
671 clocks: self.scheduler.clocks.clone(),
672 event_queue: self.scheduler.event_queue.clone(),
673 periodic_events: self.periodic_events.clone(),
674 last_clock_values: self.last_clock_values.clone(),
675 process_wakeups: self.process_wakeups.clone(),
676 finished: self.finished,
677 }
678 }
679
680 pub fn restore(
688 &mut self,
689 snapshot: &SimulationSnapshot<B>,
690 mut remap: impl FnMut(B::Event) -> Option<B::Event>,
691 ) -> Result<(), B::Event> {
692 let event_queue = snapshot
693 .event_queue
694 .iter()
695 .map(|event| {
696 Ok(SimEvent {
697 event_ref: remap(event.event_ref).ok_or(event.event_ref)?,
698 ..event.clone()
699 })
700 })
701 .collect::<Result<_, B::Event>>()?;
702 self.scheduler.time = snapshot.time;
703 self.scheduler.clocks.clone_from(&snapshot.clocks);
704 self.scheduler.event_queue = event_queue;
705 self.periodic_events.clone_from(&snapshot.periodic_events);
706 self.last_clock_values
707 .clone_from(&snapshot.last_clock_values);
708 self.process_wakeups.clone_from(&snapshot.process_wakeups);
709 self.finished = snapshot.finished;
710 Ok(())
711 }
712}
713
714pub struct ScheduleParts<B: SimBackend> {
717 pub time: u64,
718 pub clocks: Vec<(B::Event, u64)>,
720 pub events: Vec<SimEvent<B>>,
722 pub periodic: Vec<(SimEvent<B>, u64)>,
725 pub high_events: Vec<B::Event>,
727 pub process_wakeups: Vec<(usize, u64)>,
729 pub finished: bool,
731}
732
733impl<B: SimBackend> SimulationState<B> {
734 pub fn export_schedule(&self, backend: &B) -> ScheduleParts<B> {
736 let events = backend.id_to_event_slice();
737 ScheduleParts {
738 time: self.scheduler.time,
739 clocks: self
740 .scheduler
741 .clocks
742 .iter()
743 .enumerate()
744 .filter_map(|(id, clock)| Some((events[id], clock.as_ref()?.period)))
745 .collect(),
746 events: self.scheduler.event_queue.iter().cloned().collect(),
747 periodic: self
748 .periodic_events
749 .iter()
750 .map(|(key, &count)| {
751 (
752 SimEvent {
753 time: key.time,
754 event_ref: events[key.event_id],
755 signal: key.signal,
756 next_val: key.next_val,
757 },
758 count as u64,
759 )
760 })
761 .collect(),
762 high_events: self.last_clock_values.iter().map(|id| events[id]).collect(),
763 process_wakeups: {
764 let mut wakeups = self
765 .process_wakeups
766 .iter()
767 .map(|&Reverse((time, process))| (process, time))
768 .collect::<Vec<_>>();
769 wakeups.sort_unstable();
770 wakeups
771 },
772 finished: self.finished,
773 }
774 }
775
776 pub fn import_schedule(&mut self, parts: ScheduleParts<B>) {
779 self.scheduler.time = parts.time;
780 self.scheduler.clocks.clear();
781 for (event, period) in parts.clocks {
782 let id = event.id();
783 if id >= self.scheduler.clocks.len() {
784 self.scheduler.clocks.resize(id + 1, None);
785 }
786 self.scheduler.clocks[id] = Some(ClockDef { period });
787 }
788 self.scheduler.event_queue = parts.events.into_iter().collect();
789 self.periodic_events = parts
790 .periodic
791 .into_iter()
792 .map(|(event, count)| (PeriodicEventKey::from_event(&event), count as usize))
793 .collect();
794 self.last_clock_values.make_empty();
795 for event in parts.high_events {
796 self.last_clock_values.insert(event.id());
797 }
798 self.process_wakeups = parts
799 .process_wakeups
800 .into_iter()
801 .map(|(process, time)| Reverse((time, process)))
802 .collect();
803 self.finished = parts.finished;
804 }
805}
806
807pub struct SimulationSnapshot<B: SimBackend> {
809 time: u64,
810 clocks: Vec<Option<ClockDef>>,
811 event_queue: std::collections::BinaryHeap<SimEvent<B>>,
812 periodic_events: FxHashMap<PeriodicEventKey, usize>,
813 last_clock_values: BitSet,
814 process_wakeups: BinaryHeap<ProcessWakeup>,
815 finished: bool,
816}
817
818impl<B: SimBackend> Clone for SimulationSnapshot<B> {
819 fn clone(&self) -> Self {
820 Self {
821 time: self.time,
822 clocks: self.clocks.clone(),
823 event_queue: self.event_queue.clone(),
824 periodic_events: self.periodic_events.clone(),
825 last_clock_values: self.last_clock_values.clone(),
826 process_wakeups: self.process_wakeups.clone(),
827 finished: self.finished,
828 }
829 }
830}
831
832impl<B: SimBackend> SimulationSnapshot<B> {
833 pub fn time(&self) -> u64 {
835 self.time
836 }
837}