Skip to main content

vj_sim/image/
mod.rs

1//! The design image: everything a slang-free runner needs, as plain data.
2//!
3//! `vj build-aot` plans a design with slang, lowers it to machine code, and
4//! embeds one [`DesignImage`] in the produced `.so`; the runner
5//! (`vj-exec`) decodes it and assembles the same `StateEngine` + scheduler
6//! `vj run` assembles, without ever touching slang. `vj run` itself
7//! builds the same value from its own locals and instantiates it through the
8//! same code, so there is exactly one boot sequence (a second one is how "card
9//! vs sim" drift happens).
10//!
11//! The image holds no pointer and no hash-map iteration order: it is a
12//! `Vec`-ordered value with a hand-written, versioned, little-endian codec
13//! ([`codec`]), so equal designs encode to identical bytes.
14
15pub mod codec;
16pub mod covdecl;
17pub mod verif;
18
19use crate::collections::FrozenData;
20use crate::netlist::{InstanceId, TemplateKey};
21use crate::objects::TemplateSnapshot;
22use crate::sched::{ProcessKind, SpawnSpec, WaitTableEntry};
23use crate::state::{DriveStrengthPair, EdgeKind, FourStateValue, NetKind, StateLayout};
24
25/// First eight bytes of an encoded image.
26pub const IMAGE_MAGIC: [u8; 8] = *b"VJ-IMAGE";
27
28/// Bumped on any change to the encoded layout.
29pub const IMAGE_VERSION: u32 = 61;
30
31/// Which kind of state instance an [`InstanceImage`] is.
32#[derive(Clone, Copy, Debug, PartialEq, Eq)]
33pub enum InstanceKind {
34    /// An elaborated module instance (`registration_order`).
35    Elaborated,
36    /// A package's variables (a pseudo-instance with its own template).
37    Package,
38    /// The synthetic driver-contribution bank.
39    Bank,
40}
41
42/// One unique `(module, parameters)` template and its state layout.
43#[derive(Clone, Debug, PartialEq)]
44pub struct TemplateImage {
45    pub key: TemplateKey,
46    pub layout: StateLayout,
47}
48
49/// One state instance, in allocation order (the order decides each instance's
50/// arena range and `SimHandle`, so it is part of the design).
51#[derive(Clone, Debug, PartialEq)]
52pub struct InstanceImage {
53    pub id: InstanceId,
54    pub template: TemplateKey,
55    pub kind: InstanceKind,
56    /// Hierarchical path (`top.u0`), empty for package and bank instances.
57    pub path: String,
58}
59
60/// An instance's foreign slots: slots `own..own + targets.len()` alias the
61/// listed `(owner instance, owner slot)` cells.
62#[derive(Clone, Debug, PartialEq, Eq)]
63pub struct ForeignImage {
64    pub instance: InstanceId,
65    /// The instance's own slot count (what the first foreign slot follows).
66    pub own_slots: u32,
67    /// `Some`: the target names a window of the owner's slot (a `ref` port
68    /// bound to a select, LRM 23.3.3.2) -- its own shape (width, class) is
69    /// already the window's, laid out by `process_lower::SignalSlot`.
70    pub targets: Vec<(InstanceId, u32, Option<crate::state::AliasWindow>)>,
71}
72
73/// A slot whose initial value is overwritten after the reset blit (a net starts
74/// at Z, but the bits a driver drives start at X).
75#[derive(Clone, Debug, PartialEq)]
76pub struct XWindow {
77    pub instance: InstanceId,
78    pub slot: u32,
79    pub value: FourStateValue,
80}
81
82/// A slot's storage class, as a wire or a resolver sees it.
83#[derive(Clone, Copy, Debug, PartialEq, Eq)]
84pub enum PlaceClass {
85    /// At most 64 bits: the accessor ABI's `(val, unk)` scalar.
86    Inline,
87    /// More than 64 bits: `(aval, bval)` word arrays.
88    Wide,
89    /// An unpacked array: `rows` rows of one element each.
90    Memory { rows: u32 },
91}
92
93/// One signal of one instance (the bank included), as a native process
94/// addresses it.
95#[derive(Clone, Copy, Debug, PartialEq, Eq)]
96pub struct PlaceImage {
97    pub instance: InstanceId,
98    /// The slot in the instance's own numbering (a foreign slot included).
99    pub slot: u32,
100    pub width: u32,
101    pub class: PlaceClass,
102}
103
104/// One instance port connection, realized as LRM 23.3.3's synthetic
105/// continuous assignment: a native process copying `src` to `dst`, with static
106/// sensitivity on `src`'s cell. The list order is the registration order (after
107/// the continuous assignments).
108#[derive(Clone, Copy, Debug, PartialEq, Eq)]
109pub struct WireImage {
110    pub src: PlaceImage,
111    /// `Some((lo, width))`: only that sub-range of `src` is copied.
112    pub src_range: Option<(u32, u32)>,
113    pub dst: PlaceImage,
114    /// `Some((lo, width))`: the value lands in only that sub-range of `dst`.
115    pub dst_range: Option<(u32, u32)>,
116    /// A bit-select/part-select connection (a read-modify-write of the
117    /// destination), as opposed to a whole-signal or whole-memory copy.
118    pub ranged: bool,
119}
120
121/// One driver of a resolved net: its contribution cell in the bank, its
122/// strength, and its strength-ambiguity mask cell (the bits that are `L`/`H`).
123#[derive(Clone, Copy, Debug, PartialEq, Eq)]
124pub struct DriverImage {
125    pub slot: u32,
126    pub strength: DriveStrengthPair,
127    pub ambiguous: Option<u32>,
128    /// A unidirectional MOS gate's driver passes its data input's strength
129    /// (LRM 28.4, Table 28-8), read at resolve time.
130    pub src: Option<StrengthSrc>,
131}
132
133/// Where a MOS gate's driver takes its strength from: the data input net
134/// (`%v` registry `key`, bit `bit`), lowered a level when `resistive`. The
135/// driver drives bit `out_bit` of its net. `tick` is the input group's
136/// strength-change cell, which wakes the output's resolver on a change in
137/// strength only.
138#[derive(Clone, Copy, Debug, PartialEq, Eq)]
139pub struct StrengthSrc {
140    pub key: u64,
141    pub bit: u32,
142    pub out_bit: u32,
143    pub resistive: bool,
144    pub tick: u32,
145}
146
147/// A group of nets resolved together (`nets`: one, or several joined by
148/// `inout` ports).
149#[derive(Clone, Debug, PartialEq, Eq)]
150pub struct GroupImage {
151    pub kind: NetKind,
152    pub width: u32,
153    /// `(instance, slot)` of each net.
154    pub nets: Vec<(InstanceId, u32)>,
155    pub drivers: Vec<DriverImage>,
156    /// Part of a switch network, which resolves it.
157    pub switched: bool,
158    /// A `trireg` group's charge decay time in scheduler ticks.
159    pub decay: Option<u64>,
160    /// A `trireg` group's charge strength as a `%v` code (LRM 28.16.1): 5
161    /// small, 4 medium (the default), 6 large.
162    pub charge: u8,
163    /// `%v` registry keys (`vj_runtime::fmt::strength_key`) of the group's
164    /// nets, same order: the resolver records the group's per-bit strengths
165    /// under each. Empty unless the design prints `%v`.
166    pub strength_keys: Vec<u64>,
167    /// A bank cell the resolver bumps whenever the group's strengths change
168    /// (a MOS gate downstream reads them: [`StrengthSrc::tick`]).
169    pub strength_tick: Option<u32>,
170    /// The `$dumpports` port views of the group (LRM 21.7.4.3.2): empty
171    /// unless the design calls `$dumpports`.
172    pub sides: Vec<SideSpec>,
173}
174
175/// One dumped port's split of a group's drivers, for the resolver to resolve
176/// separately (LRM 21.7.4.3.2: the test fixture's drivers and the DUT's own).
177#[derive(Clone, Debug, PartialEq, Eq)]
178pub struct SideSpec {
179    /// The registry key the per-side result is recorded under
180    /// ([`DumpSignalImage::side_key`]).
181    pub key: u64,
182    /// Parallel to the group's `drivers`: the driver belongs to the dumped
183    /// instance (the DUT side), not to the fixture around it.
184    pub inside: Vec<bool>,
185}
186
187/// A bidirectional switch's control terminal.
188#[derive(Clone, Copy, Debug, PartialEq, Eq)]
189pub enum ControlImage {
190    /// Bit `bit` of a `width`-bit signal.
191    Bit {
192        instance: InstanceId,
193        slot: u32,
194        width: u32,
195        bit: u32,
196    },
197    /// 0, 1, or 2 for unknown.
198    Const(u8),
199}
200
201/// When a bidirectional switch conducts.
202#[derive(Clone, Copy, Debug, PartialEq, Eq)]
203pub enum ConductsImage {
204    Always,
205    /// `tranif1` (`true`) / `tranif0` (`false`): when the control is 1 / 0.
206    When(ControlImage, bool),
207}
208
209/// One bit-to-bit bidirectional switch: `(group, bit)` ends, where `group`
210/// indexes [`NetworkImage::groups`].
211#[derive(Clone, Copy, Debug, PartialEq, Eq)]
212pub struct SwitchImage {
213    pub a: (u32, u32),
214    pub b: (u32, u32),
215    pub conducts: ConductsImage,
216    pub resistive: bool,
217}
218
219/// Groups connected by bidirectional switches, resolved together.
220#[derive(Clone, Debug, PartialEq, Eq)]
221pub struct NetworkImage {
222    /// Indices into [`DesignImage::groups`].
223    pub groups: Vec<u32>,
224    pub switches: Vec<SwitchImage>,
225}
226
227/// What a kernel is in the registration order.
228#[derive(Clone, Copy, Debug, PartialEq, Eq)]
229pub enum KernelRole {
230    /// A procedural block (`initial`/`always*`/`final`).
231    Process,
232    /// A module-level continuous assignment: an `AlwaysComb` process with zero
233    /// frame words.
234    ContAssign,
235    /// A variable's non-constant initializer: a continuous assignment that
236    /// seeds from its instance's initialization RNG.
237    VarInit,
238}
239
240/// One compiled kernel the scheduler registers as a process.
241#[derive(Clone, Debug, PartialEq)]
242pub struct KernelImage {
243    /// Index into [`DesignImage::symbols`]: the kernel function's name.
244    pub symbol: u32,
245    pub frame_words: u32,
246    pub kind: ProcessKind,
247    pub instance: InstanceId,
248    pub role: KernelRole,
249    /// `always @*`-style head: registered first, so it is already waiting when
250    /// the time-zero updates change its inputs.
251    pub level_head: bool,
252    /// Runs in the Reactive region (a checker's procedures).
253    pub reactive: bool,
254    /// A program block's `initial` thread (LRM 24.3): the simulation ends
255    /// implicitly once every such thread and its descendants have ended.
256    pub program_initial: bool,
257    /// `false` for a kernel the scheduler never registers (it only hosts an
258    /// assertion's action blocks).
259    pub registered: bool,
260    /// `AlwaysComb`/`AlwaysLatch`: the template slots the whole procedure
261    /// reads, each an "any change" edge.
262    pub read_set: Vec<u32>,
263    /// `AlwaysComb`/`AlwaysLatch`: `(slot, lsb, width)` windows narrowing a
264    /// `read_set` slot the body reads only through constant selects (LRM
265    /// 9.2.2.2.1); a slot without a window is sensitive to its whole value.
266    pub read_windows: Vec<(u32, u32, u32)>,
267    /// `AlwaysFF` with an asynchronous reset: its template slot and edge.
268    pub reset: Option<(u32, EdgeKind)>,
269    /// `ProcessCfg::disable_scopes` as `(resume pc, label, exit pc)`: what a
270    /// `disable` of another process resumes this one at (LRM 9.6.2).
271    pub disable_scopes: Vec<(u32, String, u32)>,
272}
273
274/// A fork branch's kernel (`SpawnSpec::kernel` indexes these).
275#[derive(Clone, Debug, PartialEq, Eq)]
276pub struct ForkBranchImage {
277    pub symbol: u32,
278    pub frame_words: u32,
279    /// `(parent frame word, child frame word)` of each shared variable.
280    pub captures: Vec<(u32, u32)>,
281    /// The branch's `ProcessCfg::disable_scopes` as `(resume pc, label, exit
282    /// pc)`, like [`KernelImage::disable_scopes`].
283    pub disable_scopes: Vec<(u32, String, u32)>,
284}
285
286/// A VCD scope type (LRM 1800-2023 21.7.2.1): what kind of scope a dumped
287/// signal sits in.
288#[derive(Clone, Copy, Debug, PartialEq, Eq)]
289pub enum ScopeKind {
290    /// An elaborated module instance.
291    Module,
292    /// A generate block or a named sequential block.
293    Begin,
294    /// A named fork.
295    Fork,
296    Task,
297    Function,
298}
299
300impl ScopeKind {
301    /// The keyword a VCD `$scope` line uses.
302    pub fn as_str(self) -> &'static str {
303        match self {
304            ScopeKind::Module => "module",
305            ScopeKind::Begin => "begin",
306            ScopeKind::Fork => "fork",
307            ScopeKind::Task => "task",
308            ScopeKind::Function => "function",
309        }
310    }
311}
312
313/// A VCD `$var` type (LRM 1800-2023 21.7.2.1 `var_type`; `parameter` is
314/// not a dumped signal). A `uwire` is `wire` (21.7.2.3); `logic`/`bit`/`byte`
315/// and the other 2/4-state vectors are `reg`; `Event` has no value, only the
316/// marker of a trigger (21.7.2.2).
317#[derive(Clone, Copy, Debug, PartialEq, Eq)]
318pub enum VcdVarType {
319    Reg,
320    Wire,
321    Integer,
322    Time,
323    Real,
324    RealTime,
325    Supply0,
326    Supply1,
327    Tri,
328    TriAnd,
329    TriOr,
330    TriReg,
331    Tri0,
332    Tri1,
333    WAnd,
334    WOr,
335    Event,
336}
337
338impl VcdVarType {
339    pub const ALL: [VcdVarType; 17] = [
340        VcdVarType::Reg,
341        VcdVarType::Wire,
342        VcdVarType::Integer,
343        VcdVarType::Time,
344        VcdVarType::Real,
345        VcdVarType::RealTime,
346        VcdVarType::Supply0,
347        VcdVarType::Supply1,
348        VcdVarType::Tri,
349        VcdVarType::TriAnd,
350        VcdVarType::TriOr,
351        VcdVarType::TriReg,
352        VcdVarType::Tri0,
353        VcdVarType::Tri1,
354        VcdVarType::WAnd,
355        VcdVarType::WOr,
356        VcdVarType::Event,
357    ];
358
359    /// The keyword a VCD `$var` line uses.
360    pub fn as_str(self) -> &'static str {
361        match self {
362            VcdVarType::Reg => "reg",
363            VcdVarType::Wire => "wire",
364            VcdVarType::Integer => "integer",
365            VcdVarType::Time => "time",
366            VcdVarType::Real => "real",
367            VcdVarType::RealTime => "realtime",
368            VcdVarType::Supply0 => "supply0",
369            VcdVarType::Supply1 => "supply1",
370            VcdVarType::Tri => "tri",
371            VcdVarType::TriAnd => "triand",
372            VcdVarType::TriOr => "trior",
373            VcdVarType::TriReg => "trireg",
374            VcdVarType::Tri0 => "tri0",
375            VcdVarType::Tri1 => "tri1",
376            VcdVarType::WAnd => "wand",
377            VcdVarType::WOr => "wor",
378            VcdVarType::Event => "event",
379        }
380    }
381}
382
383/// One statement-coverage site (LRM 40.2.1 a): the module definition it is
384/// declared in and the source identity of the statement (the VPI side maps a
385/// statement object to its site by it, 40.5.2).
386#[derive(Clone, Debug, PartialEq, Eq)]
387pub struct StmtSiteImage {
388    pub def: String,
389    pub buffer: u32,
390    pub start: u64,
391    pub end: u64,
392    /// The slang `StatementKind` as its raw `u16`.
393    pub kind: u16,
394    pub nest: u32,
395}
396
397/// An FSM named by the 40.4 pragmas, in one instance.
398#[derive(Clone, Debug, PartialEq, Eq)]
399pub struct FsmImage {
400    /// The instance whose module carries the pragmas.
401    pub instance: InstanceId,
402    /// The FSM's name (40.4.2).
403    pub name: String,
404    /// The state vector's pieces, most significant first.
405    pub parts: Vec<FsmPartImage>,
406    /// The legal states (40.4.6): the parameter's name and its value.
407    pub states: Vec<(String, u64)>,
408}
409
410/// One piece of an FSM state vector: bits `lsb..=msb` of a signal.
411#[derive(Clone, Debug, PartialEq, Eq)]
412pub struct FsmPartImage {
413    pub slot: u32,
414    pub msb: u32,
415    pub lsb: u32,
416}
417
418/// One signal `$dumpvars` can select (LRM 21.7): a variable or net of an
419/// instance, with the scope path the waveform shows it under. Memories
420/// and collections are not dumpable and are not listed.
421#[derive(Clone, Debug, PartialEq, Eq)]
422pub struct DumpSignalImage {
423    pub instance: InstanceId,
424    /// The name of the module definition `instance` instantiates (what
425    /// `$coverage_*(SV_COV_MODULE, "def name")` selects by, LRM 40.3.2.1).
426    pub def_name: String,
427    /// Scope path, outermost first (`["top", "u1", "blk"]`).
428    pub scope: Vec<String>,
429    /// Each scope's VCD type, parallel to `scope`.
430    pub scope_kinds: Vec<ScopeKind>,
431    pub name: String,
432    pub width: u32,
433    /// The VCD `$var` type (LRM 21.7.2.1 `var_type`).
434    pub var_type: VcdVarType,
435    /// The slot in the instance's own numbering.
436    pub slot: u32,
437    pub wide: bool,
438    /// A `real`/`shortreal`/`realtime`: the cell holds the IEEE bits.
439    pub real: bool,
440    /// A class-typed variable: an object-heap handle, never traced.
441    pub class_handle: bool,
442    /// The direction of the port this signal is the internal symbol of, for
443    /// `$dumpports` (LRM 21.7.3): `None` for a signal that is no port.
444    pub port: Option<PortDir>,
445    /// For a port, the `%v` registry key (`vj_runtime::fmt::strength_key`)
446    /// of the net whose recorded strengths and driver counts describe the
447    /// port's fixture-side value: its own net, or for an input port connected
448    /// to a whole parent net of the same width that net (the child's input
449    /// net carries only a strong copy). `None`: no record (a variable, or a
450    /// design without `$dumpports`), so the port is driven at strong by one
451    /// driver.
452    pub strength_key: Option<u64>,
453    /// For a port, the key [`SideSpec::key`] records the fixture/DUT split
454    /// of its net's drivers under; `None` when the net has no resolver (or
455    /// the design has no `$dumpports`).
456    pub side_key: Option<u64>,
457}
458
459/// A port's direction as extended VCD records it (LRM 21.7.4.3.1): an input
460/// is dumped as the test fixture's value (`D`/`U`/`N`/`Z`), an output as the
461/// device's (`L`/`H`/`X`/`T`), an inout/ref port with the unknown-direction
462/// characters (`0`/`1`/`?`/`F`).
463#[derive(Clone, Copy, Debug, PartialEq, Eq)]
464pub enum PortDir {
465    Input,
466    Output,
467    Inout,
468}
469
470/// What a design needs of DPI-C (LRM 1800-2023 35): the C functions its
471/// imports call, and the SystemVerilog subroutines it exports to C.
472#[derive(Clone, Debug, Default, PartialEq, Eq)]
473pub struct DpiImage {
474    /// The C identifier of every imported function or task, sorted. A runner
475    /// checks each resolves before the design starts.
476    pub imports: Vec<String>,
477    /// Each exported C identifier and the [`DesignImage::symbols`] index of the
478    /// function the C side reaches it through, sorted by identifier.
479    pub exports: Vec<(String, u32)>,
480    /// `(C identifier, instance scope, variant)`: which variant of an export
481    /// each instance that exports it runs.
482    pub variants: Vec<(String, String, u32)>,
483    /// The design has a timed export task (LRM 35.7): every process kernel
484    /// runs on a fiber, so that an import task's C code may block in it
485    /// (`CompiledKernelProcess::with_task_fibers`).
486    pub task_fibers: bool,
487}
488
489/// The run-wide timing options a design was built under (`vj`'s `TimingOptions`), in plain
490/// data. They shape what the build computed (the SDF annotations it replays, the corner it
491/// resolved, the negative-timing planning), so the runner may not change them: it refuses a
492/// conflicting flag by name instead of running a different design from the one that was built.
493#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
494pub struct TimingOptionsImage {
495    /// `--timing-check-per-bit` (LRM 31.8).
496    pub per_bit_vectors: bool,
497    /// `--enable-negative-timing-checks` (LRM 31.9.4).
498    pub negative_timing_checks: bool,
499    /// `--timing-reject-limit-pct`, as the IEEE bits of the percentage.
500    pub reject_limit_pct: Option<u64>,
501    /// `--timing-error-limit-pct`, as the IEEE bits of the percentage.
502    pub error_limit_pct: Option<u64>,
503    /// `--pulse-style`: 0 on-event, 1 on-detect.
504    pub pulse_style: Option<u8>,
505    /// `--showcancelled` / `--noshowcancelled`.
506    pub showcancelled: Option<bool>,
507    /// `--sdf-mtm` (the corner `TOOL_CONTROL` selects): 0 minimum, 1 typical, 2 maximum.
508    pub sdf_mtm: Option<u8>,
509}
510
511/// The design's timing section: the options it was built under and the timing plan (gate and
512/// UDP primitives, delay lines, specify paths, timing checks, SDF annotation deltas) as an
513/// opaque, deterministic byte string. Its typed codec lives in `vj-exec`, which owns the
514/// code that runs it; this crate only carries and checksums it. Empty for a design with no
515/// timing constructs.
516#[derive(Clone, Debug, Default, PartialEq, Eq)]
517pub struct TimingImage {
518    pub options: TimingOptionsImage,
519    pub plan: Vec<u8>,
520}
521
522/// Everything a runner needs to assemble and run one design.
523#[derive(Clone, Debug, PartialEq)]
524pub struct DesignImage {
525    /// `vj_abi::VJ_ABI_VERSION` the kernels were compiled against.
526    pub abi_version: u32,
527    /// The scheduler tick exponent (`1e<tick_exp>` seconds), which `%t`
528    /// defaults to.
529    pub tick_exp: i32,
530    /// Build default for `--x-initial-zero`.
531    pub x_initial_zero: bool,
532    pub templates: Vec<TemplateImage>,
533    pub instances: Vec<InstanceImage>,
534    pub foreign: Vec<ForeignImage>,
535    pub x_windows: Vec<XWindow>,
536    /// Port connection wires, in registration order.
537    pub wires: Vec<WireImage>,
538    /// Resolved net groups (resolvers register in group order, switch
539    /// networks first).
540    pub groups: Vec<GroupImage>,
541    pub networks: Vec<NetworkImage>,
542    /// Every function name the image refers to by index.
543    pub symbols: Vec<String>,
544    pub kernels: Vec<KernelImage>,
545    pub fork_table: Vec<SpawnSpec>,
546    pub fork_branches: Vec<ForkBranchImage>,
547    pub wait_table: Vec<WaitTableEntry>,
548    /// Every interned string but `""`, in id order (`crate::strings::snapshot`).
549    /// A string's id is baked into machine code and into cells' initial
550    /// values, so the runner re-interns these first, in this order.
551    pub strings: Vec<Vec<u8>>,
552    /// Every constant collection (`crate::collections::frozen_snapshot`), the
553    /// same way: entry `i` is handle `(1 << 32) | (i + 1)`.
554    pub collections: Vec<FrozenData>,
555    /// Every class template's runtime metadata (`crate::objects::template_snapshot`),
556    /// by ascending template id. Registered with the object heap before the
557    /// design runs, so `release`, `copy`, `$cast` and the cycle collector see
558    /// the same classes they saw in the process that built the image.
559    pub classes: Vec<TemplateSnapshot>,
560    /// Every dumpable signal, in the order `$dumpvars` numbers them.
561    pub dump_signals: Vec<DumpSignalImage>,
562    /// `Some(defining module name of each statement site)` when the code was
563    /// generated with statement-coverage probes (`build-aot --coverage`, LRM
564    /// 40.2.1 a); `None` otherwise.
565    pub stmt_cov: Option<Vec<StmtSiteImage>>,
566    /// The FSMs the 40.4 pragmas name, one per instance (LRM 40.2.1 c).
567    pub fsms: Vec<FsmImage>,
568    /// The design's DPI-C imports and exports.
569    pub dpi: DpiImage,
570    /// Assertions, checker free variables and sampled-value guards.
571    pub verif: verif::VerifImage,
572    /// Timing options and the timing plan (primitives, specify, timing checks, SDF).
573    pub timing: TimingImage,
574    /// The constraint solver's programs and `randomize()` call-site specs
575    /// (`crate::constraints::registry::snapshot`), opaque here; empty for a
576    /// design that never randomizes.
577    pub randomize: Vec<u8>,
578    /// The resolvers no [`GroupImage`] describes (user-defined nettypes,
579    /// unpacked-array nets), opaque here: its typed codec lives in
580    /// `vj-exec::usernets`, which owns the code that runs it. Empty for a
581    /// design with none.
582    pub native_nets: Vec<u8>,
583    /// The VPI object model (`vj` `vpi::model::VpiModel::encode`: objects, handle
584    /// relations, properties, expression nodes, name indexes), opaque here; empty for an
585    /// image built without VPI. Present so a design runs the same user VPI libraries under
586    /// the AOT runner as under the JIT, with no slang at run time.
587    pub vpi: Vec<u8>,
588}
589
590impl DesignImage {
591    /// An image with no content (for tests and as a builder's start).
592    pub fn empty(abi_version: u32) -> Self {
593        DesignImage {
594            abi_version,
595            tick_exp: 0,
596            x_initial_zero: false,
597            templates: Vec::new(),
598            instances: Vec::new(),
599            foreign: Vec::new(),
600            x_windows: Vec::new(),
601            wires: Vec::new(),
602            groups: Vec::new(),
603            networks: Vec::new(),
604            symbols: Vec::new(),
605            kernels: Vec::new(),
606            fork_table: Vec::new(),
607            fork_branches: Vec::new(),
608            wait_table: Vec::new(),
609            strings: Vec::new(),
610            collections: Vec::new(),
611            classes: Vec::new(),
612            dump_signals: Vec::new(),
613            stmt_cov: None,
614            fsms: Vec::new(),
615            dpi: DpiImage::default(),
616            verif: verif::VerifImage::default(),
617            timing: TimingImage::default(),
618            randomize: Vec::new(),
619            native_nets: Vec::new(),
620            vpi: Vec::new(),
621        }
622    }
623
624    /// Interns `name`, returning its index in [`Self::symbols`].
625    pub fn intern(&mut self, name: &str) -> u32 {
626        if let Some(i) = self.symbols.iter().position(|s| s == name) {
627            return i as u32;
628        }
629        self.symbols.push(name.to_owned());
630        (self.symbols.len() - 1) as u32
631    }
632
633    /// The name behind a symbol index.
634    pub fn symbol(&self, index: u32) -> Option<&str> {
635        self.symbols.get(index as usize).map(String::as_str)
636    }
637
638    /// The synthetic driver-contribution bank, if the design has one.
639    pub fn bank(&self) -> Option<InstanceId> {
640        self.instances
641            .iter()
642            .find(|i| i.kind == InstanceKind::Bank)
643            .map(|i| i.id)
644    }
645
646    /// The layout of `key`'s template.
647    pub fn layout_of(&self, key: &TemplateKey) -> Option<&StateLayout> {
648        self.templates
649            .iter()
650            .find(|t| t.key == *key)
651            .map(|t| &t.layout)
652    }
653}