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.scheduler.time = current_time;
234
235 let mut periodic_events_to_process: Vec<SimEvent<B>> = Vec::new();
239 for event in &events_to_process {
240 let key = PeriodicEventKey::from_event(event);
241 let mut remove_key = false;
242 if let Some(count) = self.periodic_events.get_mut(&key) {
243 periodic_events_to_process.push(SimEvent {
244 time: event.time,
245 event_ref: event.event_ref,
246 signal: event.signal,
247 next_val: event.next_val,
248 });
249 *count -= 1;
250 remove_key = *count == 0;
251 }
252 if remove_key {
253 self.periodic_events.remove(&key);
254 }
255 }
256 debug_assert!(
257 self.periodic_events
258 .keys()
259 .all(|event| event.time > current_time),
260 "periodic event sidecar fell behind the scheduler"
261 );
262
263 let num_events = executor.backend().num_events();
264 for event in &events_to_process {
265 executor.backend_mut().set(event.signal, event.next_val);
266 }
267
268 let mut triggered_domains = BitSet::with_capacity(num_events);
269 let mut discovered_in_this_step = BitSet::with_capacity(num_events);
270 let mut scheduled_trigger_ids = BitSet::with_capacity(num_events);
271 let mut has_scheduled_event_signal = false;
272 executor.backend_mut().clear_triggered_bits();
273
274 for event in &events_to_process {
275 if let Some(&id) = self.signal_to_id.get(&event.signal) {
276 has_scheduled_event_signal = true;
277 let was_nonzero = self.last_clock_values.contains(id);
278 let is_nonzero = event.next_val != 0;
279 let triggered = match self.domain_kinds[id] {
280 Some(DomainKind::ClockPosedge | DomainKind::ResetAsyncHigh) => {
281 !was_nonzero && is_nonzero
282 }
283 Some(DomainKind::ClockNegedge | DomainKind::ResetAsyncLow) => {
284 was_nonzero && !is_nonzero
285 }
286 _ => !was_nonzero && is_nonzero,
287 };
288 if triggered {
289 scheduled_trigger_ids.insert(id);
290 executor.backend_mut().mark_triggered_bit(id);
291 }
292 }
293 }
294
295 executor.eval_comb()?;
296 if has_scheduled_event_signal {
297 if scheduled_trigger_ids.is_empty() {
303 self.replace_triggers_with_stable_edges(executor.backend_mut());
304 } else {
305 executor.backend_mut().clear_triggered_bits();
306 for id in scheduled_trigger_ids.iter() {
307 executor.backend_mut().mark_triggered_bit(id);
308 }
309 }
310 }
311
312 let mut comb_already_done = false;
313 loop {
314 let mut any_new_outer_loop_trigger = false;
315 let mut newly_triggered = Vec::new();
316
317 loop {
318 let mut any_new_sequential_trigger = false;
319 let marked_bits = executor.backend().get_triggered_bits();
320 executor.backend_mut().clear_triggered_bits();
321
322 let mut can_use_eval_apply =
323 triggered_domains.is_empty() && marked_bits.count() == 1;
324 if can_use_eval_apply {
325 let single_id = marked_bits.iter().next().expect("one marked trigger");
326 let info = self.event_info[single_id];
327 can_use_eval_apply = !info.is_cascaded;
328 if can_use_eval_apply {
329 if let Some(event) = info.eval_ff_event {
330 discovered_in_this_step.insert(single_id);
331 triggered_domains.insert(info.canonical_id);
332 any_new_outer_loop_trigger = true;
333 executor.stage_external_event(event, current_time)?;
334 executor.eval_apply_ff_at(event)?;
335 executor.fire_external_event(event, current_time)?;
336 executor.eval_comb()?;
337 if has_scheduled_event_signal {
338 self.replace_triggers_with_stable_edges(executor.backend_mut());
339 }
340 comb_already_done = true;
341 break;
342 }
343 }
344 }
345
346 for id in marked_bits.iter() {
347 if discovered_in_this_step.contains(id) {
348 continue;
349 }
350 discovered_in_this_step.insert(id);
351
352 let info = self.event_info[id];
353 if triggered_domains.contains(info.canonical_id) {
354 continue;
355 }
356 triggered_domains.insert(info.canonical_id);
357 any_new_sequential_trigger = true;
358 newly_triggered.push(info.canonical_id);
359
360 if let Some(event) = info.eval_only_event {
361 executor.stage_external_event(
362 info.eval_ff_event.unwrap_or(event),
363 current_time,
364 )?;
365 executor.eval_only_ff_at(event)?;
366 } else if let Some(event) = info.eval_ff_event {
367 executor.stage_external_event(event, current_time)?;
368 executor.eval_apply_ff_at(event)?;
369 } else {
370 unreachable!(
371 "FF trigger discovered without a corresponding execution unit"
372 );
373 }
374 }
375
376 if !any_new_sequential_trigger {
377 break;
378 }
379 }
380
381 if newly_triggered.is_empty() && !any_new_outer_loop_trigger {
382 break;
383 }
384
385 for id in &newly_triggered {
386 if let Some(event) = self.event_info[*id].apply_event {
387 executor.apply_ff_at(event)?;
388 }
389 }
390 for id in &newly_triggered {
391 if let Some(event) = self.event_info[*id].eval_ff_event {
392 executor.fire_external_event(event, current_time)?;
393 }
394 }
395
396 if comb_already_done {
397 comb_already_done = false;
398 } else {
399 executor.eval_comb()?;
400 if has_scheduled_event_signal {
401 self.replace_triggers_with_stable_edges(executor.backend_mut());
402 }
403 }
404 }
405
406 for (signal, id, _) in &self.topo_signals {
407 if *id == usize::MAX {
408 continue;
409 }
410 let value: u8 = executor.backend().get_as(*signal);
411 if value != 0 {
412 self.last_clock_values.insert(*id);
413 } else {
414 self.last_clock_values.remove(*id);
415 }
416 }
417
418 for event in periodic_events_to_process {
419 let event_id = event.event_ref.id();
420 if let Some(Some(clock)) = self.scheduler.clocks.get(event_id) {
421 self.push_periodic_event(SimEvent {
422 time: current_time + clock.period / 2,
423 event_ref: event.event_ref,
424 signal: event.signal,
425 next_val: 1 - event.next_val,
426 });
427 }
428 }
429
430 executor.finish_timed_step(current_time);
431 Ok(Some(current_time))
432 }
433
434 pub fn time(&self) -> u64 {
435 self.scheduler.time
436 }
437
438 pub fn set_time(&mut self, time: u64) {
439 self.scheduler.time = time;
440 }
441
442 pub fn next_event_time(&self) -> Option<u64> {
443 self.scheduler.next_event_time()
444 }
445}