1mod builder;
2mod error;
3
4#[cfg(all(
5 feature = "host-runtime",
6 any(
7 target_arch = "x86_64",
8 feature = "arm64-codegen",
9 target_arch = "aarch64"
10 )
11))]
12pub use builder::NativeCompilation;
13#[cfg(feature = "host-runtime")]
14pub use builder::{DeadStorePolicy, SimulatorBuilder, SimulatorOptions, TierPromotion};
15pub use builder::{compile_frontend_to_sir, compile_to_sir};
16#[cfg(feature = "systemverilog")]
17pub use builder::{compile_mixed_to_sir, compile_sv_to_sir};
18pub use error::render_diagnostic;
19pub use error::{CodegenError, CompilationWarning, SimulatorError, SimulatorErrorKind};
20
21#[cfg(feature = "host-runtime")]
22mod host {
23 use super::*;
24 use std::sync::Arc;
25 use std::sync::atomic::{AtomicBool, AtomicU64, Ordering};
26
27 use crate::backend::RuntimeEventBuffer;
28 #[cfg(any(
29 target_arch = "x86_64",
30 feature = "arm64-codegen",
31 target_arch = "aarch64"
32 ))]
33 use crate::backend::native::{NativeBackend, SharedNativeCode};
34 use crate::{
35 IOContext, RuntimeErrorCode,
36 backend::{JitBackend, MemoryLayout, SharedJitCode, SimBackend},
37 ir::{
38 InitialMemoryData, InitialMemoryWriteRun, InstancePath, RuntimeEventKind,
39 RuntimeEventSite, RuntimeProgram, SignalRef, VariableInfo,
40 },
41 };
42 use celox_testbench::{DisplayFormatArg, format_display_arg};
43 use num_bigint::BigUint;
44
45 #[derive(Debug, Clone)]
47 pub struct InstanceHierarchy {
48 pub module_name: String,
49 pub signals: Vec<NamedSignal>,
50 pub children: Vec<(String, Vec<InstanceHierarchy>)>,
51 }
52
53 #[derive(Debug, Clone)]
55 pub struct NamedSignal {
56 pub name: String,
57 pub signal: SignalRef,
58 pub info: VariableInfo,
59 pub associated_clock: Option<String>,
61 }
62
63 #[derive(Debug, Clone)]
65 pub struct NamedEvent<B: SimBackend = crate::DefaultBackend> {
66 pub name: String,
67 pub id: usize,
68 pub event_ref: B::Event,
69 }
70
71 pub struct Simulator<B: SimBackend = crate::DefaultBackend> {
80 pub(crate) backend: B,
81 pub(crate) program: RuntimeProgram,
82 pub(crate) vcd_writer: Option<crate::VcdWriter>,
83 pub(crate) dirty: bool,
84 pub(crate) warnings: Vec<CompilationWarning>,
85 pub(crate) components: crate::component::ComponentRuntime,
86 pub(crate) component_simulation: Option<celox_runtime::SimulationState<B>>,
87 runtime_event_read_seq: Arc<AtomicU64>,
88 runtime_event_drain_active: Arc<AtomicBool>,
89 comb_observer_snapshots: Vec<Vec<(BigUint, BigUint)>>,
90 comb_observer_initial_eval: bool,
91 pub(crate) diagnostics: crate::RuntimeDiagnostics,
92 tick_timing_ticks: u64,
93 tick_timing_eval_apply_ns: u64,
94 tick_timing_eval_comb_ns: u64,
95 }
96
97 #[derive(Debug, Clone, PartialEq, Eq)]
98 pub enum RuntimeEvent {
99 Display { message: String },
100 Write { message: String },
101 AssertContinue { message: String },
102 AssertFatal { message: String },
103 Missed { count: u64 },
104 }
105
106 #[derive(Debug, Clone, Copy, Default)]
107 pub struct RuntimeFormatContext<'a> {
108 pub tb_time: Option<u64>,
109 pub scope: Option<&'a str>,
110 }
111
112 pub struct RuntimeEventDrain {
113 buffer: Arc<RuntimeEventBuffer>,
114 layout: MemoryLayout,
115 sites: Vec<RuntimeEventSite>,
116 read_seq: u64,
117 shared_read_seq: Arc<AtomicU64>,
118 active: Arc<AtomicBool>,
119 }
120
121 impl RuntimeEventDrain {
122 pub fn drain(&mut self) -> Vec<RuntimeEvent> {
123 self.drain_with_context(RuntimeFormatContext::default())
124 }
125
126 pub fn drain_with_context(&mut self, ctx: RuntimeFormatContext<'_>) -> Vec<RuntimeEvent> {
127 let events = drain_raw_runtime_events_from_buffer(
128 &self.buffer,
129 &self.layout,
130 &self.sites,
131 &mut self.read_seq,
132 );
133 self.shared_read_seq.store(self.read_seq, Ordering::Release);
134 events
135 .into_iter()
136 .filter_map(|raw| render_raw_runtime_event(raw, &self.sites, ctx))
137 .collect()
138 }
139 }
140
141 impl Drop for RuntimeEventDrain {
142 fn drop(&mut self) {
143 self.shared_read_seq.store(self.read_seq, Ordering::Release);
144 self.active.store(false, Ordering::Release);
145 }
146 }
147
148 impl<B: SimBackend> std::fmt::Debug for Simulator<B> {
149 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
150 f.debug_struct("Simulator").finish()
151 }
152 }
153
154 #[derive(Clone)]
155 struct RuntimeEventArgValue {
156 values: Vec<u64>,
157 masks: Vec<u64>,
158 width: usize,
159 signed: bool,
160 is_string: bool,
161 }
162
163 #[derive(Clone)]
164 enum RawRuntimeEvent {
165 Event {
166 site_id: usize,
167 args: Vec<RuntimeEventArgValue>,
168 },
169 Missed {
170 count: u64,
171 },
172 }
173
174 fn runtime_event_words_to_biguint(words: &[u64], width: usize) -> BigUint {
175 let mut value = BigUint::from(0u8);
176 for (idx, word) in words.iter().enumerate() {
177 value |= BigUint::from(*word) << (idx * 64);
178 }
179 if width > 0 {
180 value & ((BigUint::from(1u8) << width) - BigUint::from(1u8))
181 } else {
182 BigUint::from(0u8)
183 }
184 }
185
186 fn mask_width(value: BigUint, width: usize) -> BigUint {
187 if width == 0 {
188 BigUint::from(0u8)
189 } else {
190 value & ((BigUint::from(1u8) << width) - BigUint::from(1u8))
191 }
192 }
193
194 fn slice_biguint(value: &BigUint, lsb: usize, msb: usize) -> BigUint {
195 if msb < lsb {
196 return BigUint::from(0u8);
197 }
198 mask_width(value >> lsb, msb - lsb + 1)
199 }
200
201 fn write_bits_to_memory(mem: &mut [u8], dst_bit_offset: usize, bit_width: usize, src: &[u8]) {
202 write_bits_to_memory_from(mem, dst_bit_offset, bit_width, src, 0);
203 }
204
205 fn write_bits_to_memory_from(
206 mem: &mut [u8],
207 dst_bit_offset: usize,
208 bit_width: usize,
209 src: &[u8],
210 src_bit_offset: usize,
211 ) {
212 for bit in 0..bit_width {
213 let src_bit_index = src_bit_offset + bit;
214 let src_bit = (src[src_bit_index / 8] >> (src_bit_index % 8)) & 1;
215 let dst_idx = (dst_bit_offset + bit) / 8;
216 let dst_mask = 1u8 << ((dst_bit_offset + bit) % 8);
217 if src_bit == 0 {
218 mem[dst_idx] &= !dst_mask;
219 } else {
220 mem[dst_idx] |= dst_mask;
221 }
222 }
223 }
224
225 fn write_initial_run_to_plane(
226 mem: &mut [u8],
227 signal: SignalRef,
228 mask_plane: bool,
229 run: &InitialMemoryWriteRun,
230 src: &[u8],
231 ) {
232 let Some(array) = signal.array_layout else {
233 let plane_size = signal.width.div_ceil(8);
234 let plane_bit_offset =
235 (signal.offset + usize::from(mask_plane) * plane_size) * 8 + run.bit_offset;
236 write_bits_to_memory(mem, plane_bit_offset, run.bit_width, src);
237 return;
238 };
239
240 let plane_offset = signal.offset + usize::from(mask_plane) * array.plane_size;
241 let mut consumed = 0usize;
242 while consumed < run.bit_width {
243 let logical_offset = run.bit_offset + consumed;
244 let element = logical_offset / array.element_width;
245 let intra_element = logical_offset % array.element_width;
246 let part_width = (run.bit_width - consumed).min(array.element_width - intra_element);
247 let destination_bit_offset =
248 (plane_offset + element * array.element_stride) * 8 + intra_element;
249
250 if consumed.is_multiple_of(8)
251 && destination_bit_offset.is_multiple_of(8)
252 && part_width.is_multiple_of(8)
253 {
254 let src_byte = consumed / 8;
255 let dst_byte = destination_bit_offset / 8;
256 let byte_width = part_width / 8;
257 mem[dst_byte..dst_byte + byte_width]
258 .copy_from_slice(&src[src_byte..src_byte + byte_width]);
259 } else {
260 write_bits_to_memory_from(mem, destination_bit_offset, part_width, src, consumed);
261 }
262 consumed += part_width;
263 }
264 }
265
266 fn runtime_event_format_arg(arg: &RuntimeEventArgValue, spec: Option<char>) -> String {
267 let value = runtime_event_words_to_biguint(&arg.values, arg.width);
268 let mask = runtime_event_words_to_biguint(&arg.masks, arg.width);
269 format_display_arg(
270 &DisplayFormatArg {
271 value: &value,
272 mask: Some(&mask),
273 width: arg.width,
274 signed: arg.signed,
275 is_string: arg.is_string,
276 },
277 spec,
278 )
279 }
280
281 fn render_runtime_event_message(
282 site: &RuntimeEventSite,
283 args: &[RuntimeEventArgValue],
284 ctx: RuntimeFormatContext<'_>,
285 ) -> String {
286 let Some(template) = site.template.as_deref() else {
287 let default_spec = match site.kind {
288 RuntimeEventKind::Display | RuntimeEventKind::Write => 'd',
289 RuntimeEventKind::AssertContinue | RuntimeEventKind::AssertFatal => {
290 if args.is_empty() {
291 return "assertion failed".to_string();
292 }
293 'x'
294 }
295 };
296 return args
297 .iter()
298 .map(|arg| runtime_event_format_arg(arg, Some(default_spec)))
299 .collect::<Vec<_>>()
300 .join(" ");
301 };
302 let mut out = String::new();
303 let mut arg_idx = 0usize;
304 let mut chars = template.chars().peekable();
305 while let Some(ch) = chars.next() {
306 if ch != '%' {
307 out.push(ch);
308 continue;
309 }
310 if matches!(chars.peek(), Some('%')) {
311 chars.next();
312 out.push('%');
313 continue;
314 }
315 while matches!(chars.peek(), Some('0'..='9')) {
316 chars.next();
317 }
318 let spec = chars.next().unwrap_or('d');
319 match spec {
320 'x' | 'h' | 'X' | 'H' | 'b' | 'B' | 'o' | 'O' | 'c' | 'C' | 's' | 'S' => {
321 let Some(arg) = args.get(arg_idx) else {
322 arg_idx += 1;
323 continue;
324 };
325 out.push_str(&runtime_event_format_arg(arg, Some(spec)));
326 arg_idx += 1;
327 }
328 'd' | 'D' | 'i' | 'I' => {
329 let Some(arg) = args.get(arg_idx) else {
330 arg_idx += 1;
331 continue;
332 };
333 out.push_str(&runtime_event_format_arg(arg, Some(spec)));
334 arg_idx += 1;
335 }
336 't' | 'T' => out.push_str(&ctx.tb_time.unwrap_or(0).to_string()),
337 'm' | 'M' => {
338 out.push_str(ctx.scope.or(site.scope.as_deref()).unwrap_or("<hierarchy>"))
339 }
340 other => {
341 out.push('%');
342 out.push(other);
343 }
344 }
345 }
346 out
347 }
348
349 fn render_raw_runtime_event(
350 raw: RawRuntimeEvent,
351 sites: &[RuntimeEventSite],
352 ctx: RuntimeFormatContext<'_>,
353 ) -> Option<RuntimeEvent> {
354 match raw {
355 RawRuntimeEvent::Missed { count } => Some(RuntimeEvent::Missed { count }),
356 RawRuntimeEvent::Event { site_id, args } => {
357 let site = sites.get(site_id)?;
358 let message = render_runtime_event_message(site, &args, ctx);
359 Some(match site.kind {
360 RuntimeEventKind::Display => RuntimeEvent::Display { message },
361 RuntimeEventKind::Write => RuntimeEvent::Write { message },
362 RuntimeEventKind::AssertContinue => RuntimeEvent::AssertContinue { message },
363 RuntimeEventKind::AssertFatal => RuntimeEvent::AssertFatal { message },
364 })
365 }
366 }
367 }
368
369 fn collect_runtime_events(
370 layout: &MemoryLayout,
371 sites: &[RuntimeEventSite],
372 read_seq: &mut u64,
373 buffer_size: usize,
374 mut read_payload_u64: impl FnMut(usize) -> u64,
375 mut read_seq_u64: impl FnMut(usize) -> u64,
376 ) -> Vec<RawRuntimeEvent> {
377 use crate::backend::memory_layout::{
378 RUNTIME_EVENT_HEADER_SIZE, RUNTIME_EVENT_SLOT_ARG_COUNT_OFFSET,
379 RUNTIME_EVENT_SLOT_PAYLOAD_OFFSET, RUNTIME_EVENT_SLOT_SEQ_OFFSET,
380 RUNTIME_EVENT_SLOT_SITE_OFFSET, RUNTIME_EVENT_WRITING,
381 };
382
383 if RUNTIME_EVENT_HEADER_SIZE > buffer_size || layout.runtime_event_capacity == 0 {
384 return Vec::new();
385 }
386
387 let write_seq = read_seq_u64(0);
393 let mut events = Vec::new();
394 let capacity = layout.runtime_event_capacity as u64;
395 if *read_seq + capacity < write_seq {
396 let new_read = write_seq - capacity;
397 events.push(RawRuntimeEvent::Missed {
398 count: new_read - *read_seq,
399 });
400 *read_seq = new_read;
401 }
402
403 while *read_seq < write_seq {
404 let seq = *read_seq;
405 let slot = (seq as usize) & (layout.runtime_event_capacity - 1);
406 let slot_base = RUNTIME_EVENT_HEADER_SIZE + slot * layout.runtime_event_slot_size;
407 let published = read_seq_u64(slot_base + RUNTIME_EVENT_SLOT_SEQ_OFFSET);
408 if published == RUNTIME_EVENT_WRITING || published != seq {
409 break;
410 }
411
412 let site_id = read_payload_u64(slot_base + RUNTIME_EVENT_SLOT_SITE_OFFSET) as usize;
413 let site = sites.get(site_id);
414 let site_layout = layout.runtime_event_site_layouts.get(site_id);
415 let arg_count =
416 read_payload_u64(slot_base + RUNTIME_EVENT_SLOT_ARG_COUNT_OFFSET) as usize;
417 let arg_count = site
418 .map(|site| arg_count.min(site.arg_widths.len()))
419 .unwrap_or(0);
420 let mut args = Vec::with_capacity(arg_count);
421 if let Some(site_layout) = site_layout {
422 for idx in 0..arg_count {
423 let Some(arg_layout) = site_layout.args.get(idx) else {
424 break;
425 };
426 let mut values = Vec::with_capacity(arg_layout.word_count);
427 let mut masks = Vec::with_capacity(arg_layout.word_count);
428 for word_idx in 0..arg_layout.word_count {
429 values.push(read_payload_u64(
430 slot_base
431 + RUNTIME_EVENT_SLOT_PAYLOAD_OFFSET
432 + (arg_layout.value_word_offset + word_idx) * 8,
433 ));
434 masks.push(read_payload_u64(
435 slot_base
436 + RUNTIME_EVENT_SLOT_PAYLOAD_OFFSET
437 + (arg_layout.mask_word_offset + word_idx) * 8,
438 ));
439 }
440 args.push(RuntimeEventArgValue {
441 values,
442 masks,
443 width: site
444 .and_then(|site| site.arg_widths.get(idx).copied())
445 .unwrap_or(64),
446 signed: site
447 .and_then(|site| site.arg_signed.get(idx).copied())
448 .unwrap_or(false),
449 is_string: site
450 .and_then(|site| site.arg_is_string.get(idx).copied())
451 .unwrap_or(false),
452 });
453 }
454 }
455
456 let published_after = read_seq_u64(slot_base + RUNTIME_EVENT_SLOT_SEQ_OFFSET);
457 if published_after == RUNTIME_EVENT_WRITING || published_after != seq {
458 break;
459 }
460
461 if site.is_some() {
462 events.push(RawRuntimeEvent::Event { site_id, args });
463 }
464 *read_seq += 1;
465 }
466
467 events
468 }
469
470 fn drain_raw_runtime_events_from_buffer(
471 buffer: &RuntimeEventBuffer,
472 layout: &MemoryLayout,
473 sites: &[RuntimeEventSite],
474 read_seq: &mut u64,
475 ) -> Vec<RawRuntimeEvent> {
476 use std::sync::atomic::Ordering;
477
478 collect_runtime_events(
479 layout,
480 sites,
481 read_seq,
482 buffer.byte_size(),
483 |offset| buffer.read_u64(offset),
484 |offset| buffer.load_atomic_u64(offset, Ordering::Acquire),
485 )
486 }
487
488 #[cfg(any(
489 target_arch = "x86_64",
490 feature = "arm64-codegen",
491 target_arch = "aarch64"
492 ))]
493 pub(crate) fn runtime_event_write_seq_for_backend<B: SimBackend>(backend: &B) -> u64 {
494 if let Some(buffer) = backend.runtime_event_buffer() {
495 buffer.load_atomic_u64(0, std::sync::atomic::Ordering::Acquire)
496 } else {
497 let (pointer, size) = backend.runtime_event_buffer_as_ptr();
498 if size < std::mem::size_of::<u64>() {
499 return 0;
500 }
501 unsafe { std::ptr::read_volatile(pointer.cast::<u64>()) }
503 }
504 }
505
506 #[cfg(any(
507 target_arch = "x86_64",
508 feature = "arm64-codegen",
509 target_arch = "aarch64"
510 ))]
511 pub(crate) fn collect_runtime_events_for_backend<B: SimBackend>(
512 backend: &B,
513 sites: &[RuntimeEventSite],
514 read_seq: &mut u64,
515 context: RuntimeFormatContext<'_>,
516 ) -> Vec<RuntimeEvent> {
517 let layout = backend.layout();
518 let raw = if let Some(buffer) = backend.runtime_event_buffer() {
519 collect_runtime_events(
520 layout,
521 sites,
522 read_seq,
523 buffer.byte_size(),
524 |offset| buffer.read_u64(offset),
525 |offset| buffer.load_atomic_u64(offset, std::sync::atomic::Ordering::Acquire),
526 )
527 } else {
528 let (pointer, size) = backend.runtime_event_buffer_as_ptr();
529 let read_u64 = |offset: usize| -> u64 {
530 unsafe { std::ptr::read_volatile(pointer.add(offset).cast::<u64>()) }
532 };
533 collect_runtime_events(layout, sites, read_seq, size, read_u64, read_u64)
534 };
535 raw.into_iter()
536 .filter_map(|event| render_raw_runtime_event(event, sites, context))
537 .collect()
538 }
539
540 impl<B: SimBackend> Simulator<B> {
542 fn decorate_runtime_error(&self, err: RuntimeErrorCode) -> RuntimeErrorCode {
543 match err {
544 RuntimeErrorCode::DetectedTrueLoopCode(code) => {
545 let Some(info) = self.program.runtime_schema.runtime_errors.get(&code) else {
546 return RuntimeErrorCode::DetectedTrueLoop;
547 };
548 let signals = info
549 .signals
550 .iter()
551 .map(|addr| self.program.get_path(addr))
552 .collect::<Vec<_>>();
553 if info.message == "Detected True Loop" {
554 RuntimeErrorCode::DetectedTrueLoopAt { signals }
555 } else {
556 RuntimeErrorCode::Runtime {
557 message: info.message.clone(),
558 signals,
559 }
560 }
561 }
562 other => other,
563 }
564 }
565
566 pub fn with_backend_and_program(
567 backend: B,
568 program: RuntimeProgram,
569 warnings: Vec<CompilationWarning>,
570 ) -> Self {
571 let mut sim = Self {
572 backend,
573 program,
574 vcd_writer: None,
575 dirty: false,
576 warnings,
577 components: Default::default(),
578 component_simulation: None,
579 runtime_event_read_seq: Arc::new(AtomicU64::new(0)),
580 runtime_event_drain_active: Arc::new(AtomicBool::new(false)),
581 comb_observer_snapshots: Vec::new(),
582 comb_observer_initial_eval: true,
583 diagnostics: crate::RuntimeDiagnostics::default(),
584 tick_timing_ticks: 0,
585 tick_timing_eval_apply_ns: 0,
586 tick_timing_eval_comb_ns: 0,
587 };
588 sim.comb_observer_snapshots = sim.snapshot_all_comb_observers();
589 sim
590 }
591
592 fn record_tick_timing(&mut self, eval_apply_ns: u64, eval_comb_ns: u64) {
593 let Some(every) = self.diagnostics.tick_timing_every else {
594 return;
595 };
596 if every == 0 {
597 return;
598 }
599 self.tick_timing_ticks = self.tick_timing_ticks.saturating_add(1);
600 self.tick_timing_eval_apply_ns =
601 self.tick_timing_eval_apply_ns.saturating_add(eval_apply_ns);
602 self.tick_timing_eval_comb_ns =
603 self.tick_timing_eval_comb_ns.saturating_add(eval_comb_ns);
604 if self.tick_timing_ticks.is_multiple_of(every) {
605 tracing::debug!(
606 "[tick-timing] ticks={} eval_apply_ms={:.3} eval_comb_ms={:.3} avg_apply_us={:.3} avg_comb_us={:.3}",
607 self.tick_timing_ticks,
608 self.tick_timing_eval_apply_ns as f64 / 1_000_000.0,
609 self.tick_timing_eval_comb_ns as f64 / 1_000_000.0,
610 self.tick_timing_eval_apply_ns as f64 / self.tick_timing_ticks as f64 / 1_000.0,
611 self.tick_timing_eval_comb_ns as f64 / self.tick_timing_ticks as f64 / 1_000.0,
612 );
613 }
614 }
615
616 pub(crate) fn apply_initial_values(&mut self) {
617 let mut applied = false;
618 let initial_memory_values = self.program.design.take_initial_state();
619 for init in &initial_memory_values {
620 applied = true;
621 let signal = self.backend.resolve_signal(&init.address);
622 match &init.data {
623 InitialMemoryData::Packed {
624 value,
625 mask,
626 written_mask,
627 } => {
628 let width_mask = if signal.width == 0 {
629 BigUint::default()
630 } else {
631 (BigUint::from(1u8) << signal.width) - BigUint::from(1u8)
632 };
633 let preserve_mask = &width_mask ^ (written_mask & &width_mask);
634 let (current_value, current_mask) = self.backend.get_four_state(signal);
635 let value = (current_value & &preserve_mask) | (value & written_mask);
636 let mask = (current_mask & &preserve_mask) | (mask & written_mask);
637 if self.backend.layout().four_state && signal.is_4state {
638 self.backend.set_four_state(signal, value, mask);
639 } else {
640 let known_mask = &width_mask ^ (&mask & &width_mask);
641 self.backend.set_wide(signal, value & known_mask);
642 }
643 }
644 InitialMemoryData::Writes(runs) => {
645 self.apply_initial_memory_writes(signal, runs);
646 }
647 }
648 }
649 if applied {
650 self.dirty = true;
651 }
652 self.program
653 .design
654 .restore_initial_state(initial_memory_values);
655 }
656
657 fn apply_initial_memory_writes(
658 &mut self,
659 signal: SignalRef,
660 runs: &[InitialMemoryWriteRun],
661 ) {
662 let value_byte_size = signal.width.div_ceil(8);
663 let write_mask = self.backend.layout().four_state && signal.is_4state;
664 let (ptr, mem_len) = self.backend.memory_as_ptr();
666 let ptr = ptr.cast_mut();
667 let mem = unsafe { std::slice::from_raw_parts_mut(ptr, mem_len) };
668
669 for run in runs {
670 if run.bit_width == 0 {
671 continue;
672 }
673 if signal.array_layout.is_some() {
674 write_initial_run_to_plane(mem, signal, false, run, &run.value_bytes);
675 if write_mask {
676 write_initial_run_to_plane(mem, signal, true, run, &run.mask_bytes);
677 }
678 continue;
679 }
680 if run.bit_offset % 8 == 0 && run.bit_width % 8 == 0 {
681 let byte_offset = run.bit_offset / 8;
682 let byte_width = run.bit_width / 8;
683 let value_offset = signal.offset + byte_offset;
684 mem[value_offset..value_offset + byte_width]
685 .copy_from_slice(&run.value_bytes[..byte_width]);
686 if write_mask {
687 let mask_offset = signal.offset + value_byte_size + byte_offset;
688 mem[mask_offset..mask_offset + byte_width]
689 .copy_from_slice(&run.mask_bytes[..byte_width]);
690 }
691 continue;
692 }
693
694 write_bits_to_memory(
695 mem,
696 signal.offset * 8 + run.bit_offset,
697 run.bit_width,
698 &run.value_bytes,
699 );
700 if write_mask {
701 write_bits_to_memory(
702 mem,
703 (signal.offset + value_byte_size) * 8 + run.bit_offset,
704 run.bit_width,
705 &run.mask_bytes,
706 );
707 }
708 }
709 }
710
711 pub fn drain_runtime_events(&mut self) -> Vec<RuntimeEvent> {
712 self.drain_runtime_events_with_context(RuntimeFormatContext::default())
713 }
714
715 pub fn drain_runtime_events_with_context(
716 &mut self,
717 ctx: RuntimeFormatContext<'_>,
718 ) -> Vec<RuntimeEvent> {
719 assert!(
720 !self.runtime_event_drain_active.load(Ordering::Acquire),
721 "cannot use Simulator::drain_runtime_events while a RuntimeEventDrain is active",
722 );
723 if self.dirty {
724 self.eval_comb_checked().unwrap();
725 self.dirty = false;
726 }
727 self.collect_formatted_runtime_events(ctx)
728 }
729
730 pub(crate) fn drain_runtime_events_deferred_with_context(
731 &mut self,
732 ctx: RuntimeFormatContext<'_>,
733 ) -> Vec<RuntimeEvent> {
734 assert!(
735 !self.runtime_event_drain_active.load(Ordering::Acquire),
736 "cannot use Simulator::drain_runtime_events while a RuntimeEventDrain is active",
737 );
738 if !self.program.runtime_schema.comb_observers.is_empty() && self.dirty {
739 self.eval_comb_checked().unwrap();
740 self.dirty = false;
741 }
742 self.collect_formatted_runtime_events(ctx)
743 }
744
745 fn collect_formatted_runtime_events(
746 &mut self,
747 ctx: RuntimeFormatContext<'_>,
748 ) -> Vec<RuntimeEvent> {
749 if self.runtime_event_read_seq.load(Ordering::Acquire) == self.runtime_event_write_seq()
750 {
751 return Vec::new();
752 }
753 self.collect_backend_runtime_events()
754 .into_iter()
755 .filter_map(|raw| {
756 render_raw_runtime_event(
757 raw,
758 &self.program.runtime_schema.runtime_event_sites,
759 ctx,
760 )
761 })
762 .collect()
763 }
764
765 fn collect_backend_runtime_events(&mut self) -> Vec<RawRuntimeEvent> {
766 let layout = self.backend.layout();
767 let mut read_seq = self.runtime_event_read_seq.load(Ordering::Acquire);
768 if let Some(buffer) = self.backend.runtime_event_buffer() {
769 let events = collect_runtime_events(
770 layout,
771 &self.program.runtime_schema.runtime_event_sites,
772 &mut read_seq,
773 buffer.byte_size(),
774 |offset| buffer.read_u64(offset),
775 |offset| buffer.load_atomic_u64(offset, std::sync::atomic::Ordering::Acquire),
776 );
777 self.runtime_event_read_seq
778 .store(read_seq, Ordering::Release);
779 events
780 } else {
781 let (ptr, size) = self.backend.runtime_event_buffer_as_ptr();
782 let read_u64 = |offset: usize| -> u64 {
783 unsafe { std::ptr::read_volatile(ptr.add(offset) as *const u64) }
784 };
785 let events = collect_runtime_events(
786 layout,
787 &self.program.runtime_schema.runtime_event_sites,
788 &mut read_seq,
789 size,
790 read_u64,
791 read_u64,
792 );
793 self.runtime_event_read_seq
794 .store(read_seq, Ordering::Release);
795 events
796 }
797 }
798
799 fn runtime_event_write_seq(&self) -> u64 {
800 if let Some(buffer) = self.backend.runtime_event_buffer() {
801 buffer.load_atomic_u64(0, std::sync::atomic::Ordering::Acquire)
802 } else {
803 let (ptr, _size) = self.backend.runtime_event_buffer_as_ptr();
804 unsafe { std::ptr::read_volatile(ptr as *const u64) }
805 }
806 }
807
808 fn peek_backend_runtime_events_from(&self, read_seq: u64) -> Vec<RawRuntimeEvent> {
809 let mut read_seq = read_seq;
810 let layout = self.backend.layout();
811 if let Some(buffer) = self.backend.runtime_event_buffer() {
812 collect_runtime_events(
813 layout,
814 &self.program.runtime_schema.runtime_event_sites,
815 &mut read_seq,
816 buffer.byte_size(),
817 |offset| buffer.read_u64(offset),
818 |offset| buffer.load_atomic_u64(offset, std::sync::atomic::Ordering::Acquire),
819 )
820 } else {
821 let (ptr, size) = self.backend.runtime_event_buffer_as_ptr();
822 let read_u64 = |offset: usize| -> u64 {
823 unsafe { std::ptr::read_volatile(ptr.add(offset) as *const u64) }
824 };
825 collect_runtime_events(
826 layout,
827 &self.program.runtime_schema.runtime_event_sites,
828 &mut read_seq,
829 size,
830 read_u64,
831 read_u64,
832 )
833 }
834 }
835
836 pub fn runtime_event_drain(&mut self) -> Option<RuntimeEventDrain> {
837 let buffer = self.backend.runtime_event_buffer()?;
838 self.runtime_event_drain_active
839 .compare_exchange(false, true, Ordering::AcqRel, Ordering::Acquire)
840 .ok()?;
841 Some(RuntimeEventDrain {
842 buffer,
843 layout: self.backend.layout().clone(),
844 sites: self.program.runtime_schema.runtime_event_sites.clone(),
845 read_seq: self.runtime_event_read_seq.load(Ordering::Acquire),
846 shared_read_seq: Arc::clone(&self.runtime_event_read_seq),
847 active: Arc::clone(&self.runtime_event_drain_active),
848 })
849 }
850
851 pub fn program(&self) -> &RuntimeProgram {
853 &self.program
854 }
855
856 pub fn backend_ref(&self) -> &B {
858 &self.backend
859 }
860
861 pub fn warnings(&self) -> &[CompilationWarning] {
863 &self.warnings
864 }
865
866 pub fn dump(&mut self, timestamp: u64) {
868 if self.dirty {
869 self.eval_comb_checked().unwrap();
870 self.dirty = false;
871 }
872 let component_traces = self.components.trace_values();
873 if let Some(ref mut writer) = self.vcd_writer {
874 writer
875 .dump_backend(timestamp, &mut self.backend, &component_traces)
876 .unwrap();
877 }
878 }
879
880 pub fn flush_vcd(&mut self) -> std::io::Result<()> {
882 if let Some(writer) = self.vcd_writer.as_mut() {
883 writer.flush()?;
884 }
885 Ok(())
886 }
887
888 pub fn vcd_statistics(&self) -> Option<celox_runtime::VcdStatistics> {
889 self.vcd_writer.as_ref().map(crate::VcdWriter::statistics)
890 }
891
892 pub fn set<T: Copy>(&mut self, signal: SignalRef, val: T) {
894 self.backend.set(signal, val);
895 self.dirty = true;
896 self.settle_dirty_for_runtime_event_drain();
897 }
898
899 pub fn set_wide(&mut self, signal: SignalRef, val: BigUint) {
901 self.backend.set_wide(signal, val);
902 self.dirty = true;
903 self.settle_dirty_for_runtime_event_drain();
904 }
905
906 pub fn set_four_state(&mut self, signal: SignalRef, val: BigUint, mask: BigUint) {
908 self.backend.set_four_state(signal, val, mask);
909 self.dirty = true;
910 self.settle_dirty_for_runtime_event_drain();
911 }
912
913 pub fn modify<F>(&mut self, f: F) -> Result<(), RuntimeErrorCode>
915 where
916 F: FnOnce(&mut IOContext<B>),
917 {
918 let mut ctx = IOContext {
919 backend: &mut self.backend,
920 };
921 f(&mut ctx);
922 self.dirty = true;
923 if self.runtime_event_drain_active.load(Ordering::Acquire) {
924 self.eval_comb_checked()?;
925 self.dirty = false;
926 }
927 Ok(())
928 }
929
930 fn settle_dirty_for_runtime_event_drain(&mut self) {
931 if self.runtime_event_drain_active.load(Ordering::Acquire) {
932 self.eval_comb_checked().unwrap();
933 self.dirty = false;
934 }
935 }
936
937 pub(crate) fn eval_comb_checked(&mut self) -> Result<(), RuntimeErrorCode> {
938 if self.program.runtime_schema.runtime_event_sites.is_empty() {
939 return self
940 .backend
941 .eval_comb()
942 .map_err(|e| self.decorate_runtime_error(e));
943 }
944 if self.program.runtime_schema.comb_observers.is_empty() {
945 let runtime_event_start_seq = self.runtime_event_write_seq();
946 let eval_result = self
947 .backend
948 .eval_comb()
949 .map_err(|e| self.decorate_runtime_error(e));
950 let runtime_events = self.peek_backend_runtime_events_from(runtime_event_start_seq);
951 if let Some(err) = self.fatal_comb_capture_error(&runtime_events) {
952 return Err(err);
953 }
954 return eval_result;
955 }
956
957 let before = self.snapshot_all_comb_observers();
958 let active_before: Vec<bool> = before
959 .iter()
960 .zip(&self.comb_observer_snapshots)
961 .map(|(now, prev)| now != prev)
962 .collect();
963 let mut active_sites =
964 vec![false; self.program.runtime_schema.runtime_event_sites.len()];
965 for (observer, is_active) in self
966 .program
967 .runtime_schema
968 .comb_observers
969 .iter()
970 .zip(active_before.iter().copied())
971 {
972 if is_active || self.comb_observer_initial_eval {
973 let group = observer.activation_group;
974 for group_observer in &self.program.runtime_schema.comb_observers {
975 if group_observer.activation_group == group {
976 active_sites[group_observer.site_id as usize] = true;
977 }
978 }
979 }
980 }
981 self.backend.set_comb_capture_event_enabled(&active_sites);
982 let runtime_event_start_seq = self.runtime_event_write_seq();
983 let eval_result = self
984 .backend
985 .eval_comb()
986 .map_err(|e| self.decorate_runtime_error(e));
987 let after = self.snapshot_all_comb_observers();
988 let runtime_events = self.peek_backend_runtime_events_from(runtime_event_start_seq);
989 let fatal_error = self.fatal_comb_capture_error(&runtime_events);
990 self.backend.set_comb_capture_event_enabled(&vec![
991 false;
992 self.program
993 .runtime_schema
994 .runtime_event_sites
995 .len()
996 ]);
997 self.comb_observer_snapshots = after;
998 self.comb_observer_initial_eval = false;
999 if let Some(err) = fatal_error {
1000 return Err(err);
1001 }
1002 eval_result
1003 }
1004
1005 fn snapshot_all_comb_observers(&self) -> Vec<Vec<(BigUint, BigUint)>> {
1006 self.program
1007 .runtime_schema
1008 .comb_observers
1009 .iter()
1010 .map(|observer| {
1011 observer
1012 .sensitivity
1013 .iter()
1014 .map(|atom| {
1015 let signal = self.backend.resolve_signal(&atom.id);
1016 let (value, mask) = if signal.is_4state {
1017 self.backend.get_four_state(signal)
1018 } else {
1019 (self.backend.get(signal), BigUint::default())
1020 };
1021 (
1022 slice_biguint(&value, atom.access.lsb, atom.access.msb),
1023 slice_biguint(&mask, atom.access.lsb, atom.access.msb),
1024 )
1025 })
1026 .collect()
1027 })
1028 .collect()
1029 }
1030
1031 fn fatal_comb_capture_error(&self, events: &[RawRuntimeEvent]) -> Option<RuntimeErrorCode> {
1032 events.iter().find_map(|raw| {
1033 let RawRuntimeEvent::Event { site_id, args } = raw else {
1034 return None;
1035 };
1036 let site = self
1037 .program
1038 .runtime_schema
1039 .runtime_event_sites
1040 .get(*site_id)?;
1041 if !matches!(site.kind, RuntimeEventKind::AssertFatal) {
1042 return None;
1043 }
1044 Some(RuntimeErrorCode::Runtime {
1045 message: render_runtime_event_message(
1046 site,
1047 args,
1048 RuntimeFormatContext::default(),
1049 ),
1050 signals: Vec::new(),
1051 })
1052 })
1053 }
1054
1055 pub(crate) fn eval_apply_ff_at_checked(
1056 &mut self,
1057 event: B::Event,
1058 ) -> Result<(), RuntimeErrorCode> {
1059 self.backend
1060 .eval_apply_ff_at(event)
1061 .map_err(|e| self.decorate_runtime_error(e))
1062 }
1063
1064 pub(crate) fn eval_comb_apply_ff_at_checked(
1065 &mut self,
1066 event: B::Event,
1067 ) -> Result<(), RuntimeErrorCode> {
1068 self.backend
1069 .eval_comb_apply_ff_at(event)
1070 .map_err(|e| self.decorate_runtime_error(e))
1071 }
1072
1073 pub(crate) fn eval_only_ff_at_checked(
1074 &mut self,
1075 event: B::Event,
1076 ) -> Result<(), RuntimeErrorCode> {
1077 self.backend
1078 .eval_only_ff_at(event)
1079 .map_err(|e| self.decorate_runtime_error(e))
1080 }
1081
1082 pub(crate) fn apply_ff_at_checked(
1083 &mut self,
1084 event: B::Event,
1085 ) -> Result<(), RuntimeErrorCode> {
1086 self.backend
1087 .apply_ff_at(event)
1088 .map_err(|e| self.decorate_runtime_error(e))
1089 }
1090
1091 pub fn tick(&mut self, event: B::Event) -> Result<(), RuntimeErrorCode> {
1093 let timing_enabled = self.diagnostics.tick_timing_every.is_some();
1094 let mut eval_comb_ns = 0u64;
1095 let mut eval_apply_ns = 0u64;
1096 if self.dirty {
1097 if timing_enabled {
1098 let start = crate::timing::now();
1099 self.eval_comb_checked()?;
1100 eval_comb_ns = eval_comb_ns.saturating_add(start.elapsed().as_nanos() as u64);
1101 let start = crate::timing::now();
1102 self.eval_apply_ff_at_checked(event)?;
1103 eval_apply_ns = eval_apply_ns.saturating_add(start.elapsed().as_nanos() as u64);
1104 } else {
1105 self.eval_comb_checked()?;
1106 self.eval_apply_ff_at_checked(event)?;
1107 }
1108 self.dirty = false;
1109 } else {
1110 if timing_enabled {
1111 let start = crate::timing::now();
1112 self.eval_apply_ff_at_checked(event)?;
1113 eval_apply_ns = eval_apply_ns.saturating_add(start.elapsed().as_nanos() as u64);
1114 } else {
1115 self.eval_apply_ff_at_checked(event)?;
1116 }
1117 }
1118 if timing_enabled {
1119 let start = crate::timing::now();
1120 self.eval_comb_checked()?;
1121 eval_comb_ns = eval_comb_ns.saturating_add(start.elapsed().as_nanos() as u64);
1122 self.record_tick_timing(eval_apply_ns, eval_comb_ns);
1123 } else {
1124 self.eval_comb_checked()?;
1125 }
1126 self.dirty = false;
1127 Ok(())
1128 }
1129
1130 pub(crate) fn tick_deferred_comb(
1134 &mut self,
1135 event: B::Event,
1136 ) -> Result<(), RuntimeErrorCode> {
1137 if !self.program.runtime_schema.comb_observers.is_empty() {
1138 return self.tick(event);
1139 }
1140 if self.dirty {
1141 self.eval_comb_apply_ff_at_checked(event)?;
1142 } else {
1143 self.eval_apply_ff_at_checked(event)?;
1144 }
1145 self.dirty = true;
1146 Ok(())
1147 }
1148
1149 pub(crate) fn tick_deferred_comb_many(
1153 &mut self,
1154 event: B::Event,
1155 count: u64,
1156 ) -> (u64, Result<(), RuntimeErrorCode>) {
1157 if count == 0 {
1158 return (0, Ok(()));
1159 }
1160 if !self.program.runtime_schema.comb_observers.is_empty() || !self.dirty {
1161 return (1, self.tick_deferred_comb(event));
1162 }
1163 let (completed, result) = self.backend.eval_comb_apply_ff_many_at(event, count);
1164 self.dirty = true;
1165 (
1166 completed,
1167 result.map_err(|error| self.decorate_runtime_error(error)),
1168 )
1169 }
1170
1171 pub fn signal(&self, path: &str) -> SignalRef {
1174 let addr = self.program.get_addr(&[], &[path]).unwrap();
1175 self.backend.resolve_signal(&addr)
1176 }
1177
1178 pub fn event(&self, port: &str) -> B::Event {
1180 let addr = self.program.get_addr(&[], &[port]).unwrap();
1181 self.backend.resolve_event(&addr)
1182 }
1183
1184 pub fn try_signal(&self, path: &str) -> Result<SignalRef, crate::ir::AddrLookupError> {
1186 let addr = self.program.get_addr(&[], &[path])?;
1187 Ok(self.backend.resolve_signal(&addr))
1188 }
1189
1190 pub fn try_event(&self, port: &str) -> Result<B::Event, crate::ir::AddrLookupError> {
1192 let addr = self.program.get_addr(&[], &[port])?;
1193 Ok(self.backend.resolve_event(&addr))
1194 }
1195
1196 pub fn get_as<T: Default + Copy>(&mut self, signal: SignalRef) -> T {
1199 if self.dirty {
1200 self.eval_comb_checked().unwrap();
1201 self.dirty = false;
1202 }
1203 self.backend.get_as(signal)
1204 }
1205
1206 pub fn get(&mut self, signal: SignalRef) -> BigUint {
1209 if self.dirty {
1210 self.eval_comb_checked().unwrap();
1211 self.dirty = false;
1212 }
1213 self.backend.get(signal)
1214 }
1215
1216 pub fn get_four_state(&mut self, signal: SignalRef) -> (BigUint, BigUint) {
1219 if self.dirty {
1220 self.eval_comb_checked().unwrap();
1221 self.dirty = false;
1222 }
1223 self.backend.get_four_state(signal)
1224 }
1225
1226 pub fn eval_comb(&mut self) -> Result<(), RuntimeErrorCode> {
1228 self.eval_comb_checked()?;
1229 self.dirty = false;
1230 Ok(())
1231 }
1232
1233 pub fn memory_as_ptr(&self) -> (*const u8, usize) {
1235 self.backend.memory_as_ptr()
1236 }
1237
1238 pub fn memory_as_mut_ptr(&mut self) -> (*mut u8, usize) {
1240 self.backend.memory_as_mut_ptr()
1241 }
1242
1243 pub fn memory_owner(&self) -> Option<Arc<dyn std::any::Any + Send + Sync>> {
1245 self.backend.memory_owner()
1246 }
1247
1248 pub fn stable_region_size(&self) -> usize {
1250 self.backend.stable_region_size()
1251 }
1252
1253 pub fn layout(&self) -> &MemoryLayout {
1255 self.backend.layout()
1256 }
1257
1258 pub fn take_vcd_writer(&mut self) -> Option<crate::VcdWriter> {
1264 self.vcd_writer.take()
1265 }
1266
1267 pub fn build_vcd_descs(&self, four_state_mode: bool) -> Vec<crate::VcdSignalDesc> {
1273 let mut descs = Vec::new();
1274 let mut sorted_instances = self.program.design.instances().collect::<Vec<_>>();
1275 sorted_instances.sort_by_key(|instance| instance.id);
1276
1277 for instance in sorted_instances {
1278 let scope = format!("{}", instance.id);
1279
1280 let mut sorted_vars: Vec<_> = instance
1281 .state_addresses()
1282 .iter()
1283 .filter_map(|address| self.program.design.variable(address))
1284 .filter(|variable| instance.resolves_path_to(&variable.path, variable.address))
1285 .collect();
1286 sorted_vars.sort_by(|a, b| a.path.cmp(&b.path));
1287
1288 for variable in sorted_vars {
1289 let name = variable.path.join(".");
1290 let signal = self.backend.resolve_signal(&variable.address);
1291
1292 descs.push(crate::VcdSignalDesc {
1293 scope: scope.clone(),
1294 name,
1295 offset: signal.offset,
1296 width: signal.width,
1297 is_4state: four_state_mode && signal.is_4state,
1298 });
1299 }
1300 }
1301 descs
1302 }
1303
1304 pub fn named_signals(&self) -> Vec<NamedSignal> {
1306 let top_instance = self
1307 .program
1308 .design
1309 .root_instance()
1310 .expect("top-level instance not found");
1311 self.build_signals_for_instance(top_instance.id)
1312 }
1313
1314 pub fn instance_signals(&self, instance_path: &[(&str, usize)]) -> Vec<NamedSignal> {
1319 let path: Vec<_> = instance_path
1320 .iter()
1321 .map(|(name, idx)| ((*name).to_string(), *idx))
1322 .collect();
1323 match self.program.design.instance_at_path(&InstancePath(path)) {
1324 Some(instance) => self.build_signals_for_instance(instance.id),
1325 None => Vec::new(),
1326 }
1327 }
1328
1329 fn build_signals_for_instance(
1335 &self,
1336 instance_id: crate::ir::InstanceId,
1337 ) -> Vec<NamedSignal> {
1338 let instance = self.program.design.instance(instance_id).unwrap();
1339
1340 let mut result = Vec::new();
1341 for address in instance.state_addresses() {
1342 let variable = self.program.design.variable(address).unwrap();
1343 if !instance.resolves_path_to(&variable.path, *address) {
1345 continue;
1346 }
1347 let name = variable.path.join(".");
1348 let signal = self.backend.resolve_signal(address);
1349
1350 let associated_clock = self
1352 .program
1353 .design
1354 .events
1355 .reset_clocks
1356 .get(address)
1357 .map(|clock_addr| self.program.get_path(clock_addr));
1358
1359 result.push(NamedSignal {
1360 name,
1361 signal,
1362 info: self.program.design.variable_info(address).unwrap(),
1363 associated_clock,
1364 });
1365 }
1366 result
1367 }
1368
1369 pub fn named_events(&self) -> Vec<NamedEvent<B>> {
1371 let mut result = Vec::new();
1372 for (id, addr) in self.backend.id_to_addr_slice().iter().enumerate() {
1373 let name = self.program.get_path(addr);
1374 if let Some(ev) = self.backend.resolve_event_opt(addr) {
1375 result.push(NamedEvent {
1376 name,
1377 id,
1378 event_ref: ev,
1379 });
1380 }
1381 }
1382 result
1383 }
1384
1385 pub fn tick_by_id(&mut self, event_id: usize) -> Result<(), RuntimeErrorCode> {
1392 let event = self
1393 .backend
1394 .id_to_event_slice()
1395 .get(event_id)
1396 .copied()
1397 .ok_or_else(|| RuntimeErrorCode::NotAnEvent(format!("event_id={event_id}")))?;
1398 self.tick(event)
1399 }
1400
1401 pub fn tick_by_id_n(
1410 &mut self,
1411 event_id: usize,
1412 count: u32,
1413 ) -> Result<(), RuntimeErrorCode> {
1414 let event = self
1415 .backend
1416 .id_to_event_slice()
1417 .get(event_id)
1418 .copied()
1419 .ok_or_else(|| RuntimeErrorCode::NotAnEvent(format!("event_id={event_id}")))?;
1420 for _ in 0..count {
1421 self.tick(event)?;
1422 }
1423 Ok(())
1424 }
1425
1426 pub fn child_signal(&self, instance_path: &[(&str, usize)], var: &str) -> SignalRef {
1428 let addr = self.program.get_addr(instance_path, &[var]).unwrap();
1429 self.backend.resolve_signal(&addr)
1430 }
1431
1432 pub fn try_child_signal(
1434 &self,
1435 instance_path: &[(&str, usize)],
1436 var: &str,
1437 ) -> Result<SignalRef, crate::ir::AddrLookupError> {
1438 let addr = self.program.get_addr(instance_path, &[var])?;
1439 Ok(self.backend.resolve_signal(&addr))
1440 }
1441
1442 pub fn named_hierarchy(&self) -> InstanceHierarchy {
1444 self.build_hierarchy(&[])
1445 }
1446
1447 fn build_hierarchy(&self, current_path: &[(String, usize)]) -> InstanceHierarchy {
1448 let instance = self
1449 .program
1450 .design
1451 .instance_at_path(&InstancePath(current_path.to_vec()))
1452 .expect("instance not found");
1453 let module_name = instance.module_name.clone();
1454
1455 let signals = self.build_signals_for_instance(instance.id);
1456
1457 let current_len = current_path.len();
1459 let mut children_map: crate::HashMap<String, Vec<(usize, InstanceHierarchy)>> =
1460 crate::HashMap::default();
1461
1462 for child in self.program.design.instances() {
1463 if child.path.0.len() == current_len + 1 && child.path.0.starts_with(current_path) {
1464 let (child_name, child_index) = &child.path.0[current_len];
1465 let child_hierarchy = self.build_hierarchy(&child.path.0);
1466 children_map
1467 .entry(child_name.clone())
1468 .or_default()
1469 .push((*child_index, child_hierarchy));
1470 }
1471 }
1472
1473 let mut children: Vec<(String, Vec<InstanceHierarchy>)> = children_map
1475 .into_iter()
1476 .map(|(name, mut instances)| {
1477 instances.sort_by_key(|(idx, _)| *idx);
1478 let sorted = instances.into_iter().map(|(_, h)| h).collect();
1479 (name, sorted)
1480 })
1481 .collect();
1482 children.sort_by(|(a, _), (b, _)| a.cmp(b));
1483
1484 InstanceHierarchy {
1485 module_name,
1486 signals,
1487 children,
1488 }
1489 }
1490 }
1491
1492 impl Simulator {
1494 pub fn builder<'a>(code: &'a str, top: &'a str) -> SimulatorBuilder<'a, Simulator> {
1495 SimulatorBuilder::<Simulator>::new(code, top)
1496 }
1497
1498 pub fn from_sources<'a>(
1499 sources: Vec<(&'a str, &'a std::path::Path)>,
1500 top: &'a str,
1501 ) -> SimulatorBuilder<'a, Simulator> {
1502 SimulatorBuilder::<Simulator>::from_sources(sources, top)
1503 }
1504
1505 pub fn from_frontend(
1511 artifact: celox_frontend_sdk::FrontendArtifact,
1512 ) -> SimulatorBuilder<'static, Simulator> {
1513 SimulatorBuilder::<Simulator>::from_frontend(artifact)
1514 }
1515
1516 pub fn from_frontend_with_testbench<'a>(
1518 artifact: celox_frontend_sdk::FrontendArtifact,
1519 sources: Vec<(&'a str, &'a std::path::Path)>,
1520 top: &'a str,
1521 ) -> SimulatorBuilder<'a, Simulator> {
1522 SimulatorBuilder::<Simulator>::from_frontend_with_testbench(artifact, sources, top)
1523 }
1524
1525 #[cfg(feature = "systemverilog")]
1527 pub fn from_sv_sources<'a>(
1528 sources: Vec<(&'a str, &'a std::path::Path)>,
1529 top: &'a str,
1530 ) -> SimulatorBuilder<'a, Simulator> {
1531 SimulatorBuilder::<Simulator>::from_sv_sources(sources, top)
1532 }
1533
1534 #[cfg(feature = "systemverilog")]
1536 pub fn from_mixed_sources<'a>(
1537 sources: Vec<(&'a str, &'a std::path::Path)>,
1538 sv_sources: Vec<(&'a str, &'a std::path::Path)>,
1539 top: &'a str,
1540 ) -> SimulatorBuilder<'a, Simulator> {
1541 SimulatorBuilder::<Simulator>::from_mixed_sources(sources, sv_sources, top)
1542 }
1543 }
1544
1545 impl Simulator<JitBackend> {
1546 pub fn shared_code(&self) -> Arc<SharedJitCode> {
1549 self.backend.shared_code()
1550 }
1551
1552 pub fn into_backend(self) -> JitBackend {
1554 self.backend
1555 }
1556 }
1557
1558 impl Simulator<crate::backend::wasm_runtime::WasmBackend> {
1559 pub fn into_backend(self) -> crate::backend::wasm_runtime::WasmBackend {
1561 self.backend
1562 }
1563 }
1564
1565 impl Simulator<crate::backend::TieredBackend> {
1566 pub fn is_compiled(&self) -> bool {
1571 self.backend.is_compiled()
1572 }
1573
1574 pub fn tiered_execution_stats(&self) -> crate::TieredExecutionStats {
1579 self.backend.execution_stats()
1580 }
1581
1582 pub fn start_tiered_execution_timing(&mut self) {
1585 self.backend.start_execution_timing();
1586 }
1587
1588 pub fn finish_tiered_execution_timing(&mut self) -> Option<crate::TieredExecutionTiming> {
1590 self.backend.finish_execution_timing()
1591 }
1592
1593 pub fn promotion_error(&self) -> Option<&SimulatorError> {
1595 self.backend.promotion_error()
1596 }
1597
1598 pub fn cancel_background_compilation(&mut self) -> bool {
1604 self.backend.cancel_background_compilation()
1605 }
1606
1607 pub fn into_backend(self) -> crate::backend::TieredBackend {
1609 self.backend
1610 }
1611 }
1612
1613 #[cfg(any(
1614 target_arch = "x86_64",
1615 feature = "arm64-codegen",
1616 target_arch = "aarch64"
1617 ))]
1618 impl Simulator<NativeBackend> {
1619 pub fn shared_code(&self) -> Arc<SharedNativeCode> {
1622 self.backend.shared_code()
1623 }
1624
1625 pub fn start_native_execution_timing(&mut self) {
1631 self.backend.start_execution_timing();
1632 }
1633
1634 pub fn finish_native_execution_timing(
1636 &mut self,
1637 ) -> Option<crate::native_backend::NativeExecutionTiming> {
1638 self.backend.finish_execution_timing()
1639 }
1640
1641 pub fn from_shared(
1643 shared: Arc<SharedNativeCode>,
1644 program: crate::ir::OptimizedSir,
1645 ) -> Self {
1646 let backend = NativeBackend::from_shared(shared);
1647 let mut sim = Self::with_backend_and_program(backend, program.into_runtime(), vec![]);
1648 sim.apply_initial_values();
1649 sim
1650 }
1651
1652 pub fn into_backend(self) -> NativeBackend {
1654 self.backend
1655 }
1656 }
1657}
1658
1659#[cfg(feature = "host-runtime")]
1660pub use host::*;