Skip to main content

vj_sim/netlist/
items.rs

1//! A module body's items as one flat list.
2//!
3//! A generate block (`if`/`case`/`for` generate, LRM 1800-2023 §27) is a
4//! nested scope, but everything declared in an instantiated one -- signals,
5//! processes, continuous assignments, subroutines, instances -- belongs to
6//! the enclosing module instance and runs with it. Every pass that
7//! enumerates a module's contents goes through [`module_items`], so they
8//! all agree on which items exist and in what order (member indices are
9//! used as keys, e.g. `SymbolKey`).
10
11use crate::symid::SymbolUid;
12use sv_lang::{DefinitionKind, Expression, Symbol};
13use sv_lang_kinds::SymbolKind;
14
15/// `body`'s members, each instantiated generate block (and each
16/// instantiated iteration of a generate loop) replaced by its own items,
17/// recursively, in declaration order. Uninstantiated branches are dropped.
18pub fn module_items<'d>(body: &Symbol<'d>) -> Vec<Symbol<'d>> {
19    let mut out = Vec::new();
20    push_items(body, &mut out);
21    out
22}
23
24/// The static checker instances of `body` (LRM 17.3), nested ones included,
25/// outermost first, in declaration order: the instances [`module_items`]
26/// flattens away.
27pub fn checker_instances<'d>(body: &Symbol<'d>) -> Vec<Symbol<'d>> {
28    fn walk<'d>(scope: &Symbol<'d>, out: &mut Vec<Symbol<'d>>) {
29        for m in scope.members() {
30            if let Some(blocks) = generate_children(&m) {
31                for b in blocks {
32                    walk(&b, out);
33                }
34            } else if m.kind() == SymbolKind::CheckerInstance {
35                out.push(m);
36                walk(&m, out);
37            } else if let Some(elements) = checker_array_elements(&m) {
38                for e in elements {
39                    out.push(e);
40                    walk(&e, out);
41                }
42            } else if m.kind() == SymbolKind::StatementBlock {
43                // A procedural instance (17.3) is declared in the scope of the
44                // statement that instantiates it.
45                walk(&m, out);
46            }
47        }
48    }
49    let mut out = Vec::new();
50    walk(body, &mut out);
51    out
52}
53
54/// One checker instantiation statement (LRM 17.3: a checker instantiated in an
55/// `always` or `initial` procedure).
56#[derive(Clone, Copy)]
57pub struct ProceduralChecker<'d> {
58    /// The procedure the statement is in.
59    pub block: Symbol<'d>,
60    /// The statement.
61    pub stmt: sv_lang::Statement<'d>,
62    /// The instance it instantiates.
63    pub instance: Symbol<'d>,
64}
65
66/// The procedural checker instantiations of `body`'s procedures (generate
67/// blocks and static checker instances' included), in source order. A
68/// procedural instance is a member of its scope like a static one, so only the
69/// statements tell the two apart; designs with no checker instance at all skip
70/// the walk.
71pub fn procedural_checkers<'d>(body: &Symbol<'d>) -> Vec<ProceduralChecker<'d>> {
72    if checker_instances(body).is_empty() {
73        return Vec::new();
74    }
75    let mut out = Vec::new();
76    for block in module_items(body) {
77        if block.kind() != SymbolKind::ProceduralBlock {
78            continue;
79        }
80        let Some(root) = crate::process::cfg::proc_body(&block) else {
81            continue;
82        };
83        let mut stack = vec![root.as_sem_node()];
84        while let Some(n) = stack.pop() {
85            if let Some(stmt) = n.as_statement() {
86                for instance in stmt.procedural_checker_instances() {
87                    out.push(ProceduralChecker {
88                        block,
89                        stmt,
90                        instance,
91                    });
92                }
93            }
94            let mut kids = n.children();
95            kids.reverse();
96            stack.extend(kids);
97        }
98    }
99    out
100}
101
102/// The `CheckerInstanceBody` of checker instance `inst`: the scope slang's
103/// analysis keys its results by. The body is not a member of anything, so it
104/// is reached through a member's parent; `None` for an instance with no
105/// members (nothing to analyze).
106pub fn checker_instance_body<'d>(inst: &Symbol<'d>) -> Option<Symbol<'d>> {
107    inst.members().next()?.parent()
108}
109
110/// One output formal argument of a checker instance and its connection
111/// (LRM 17.3: "as if there were continuous assignments, executed in the
112/// Reactive region, of the checker's output formal arguments to their
113/// corresponding actual arguments").
114#[derive(Clone, Copy)]
115pub struct CheckerOutput<'d> {
116    /// The checker's output formal: a variable of the checker.
117    pub formal: Symbol<'d>,
118    /// slang's `actual = <formal value>` assignment (its right-hand side is
119    /// an empty argument standing for the formal), or `None` for an output
120    /// left unconnected.
121    pub assign: Option<sv_lang::Expression<'d>>,
122    /// The formal's initial value (`output int ctr = 0`), evaluated in the
123    /// instantiation's context. `None` when it declares none.
124    pub initial: Option<sv_lang::Expression<'d>>,
125}
126
127/// The output formals of checker instance `inst`, in port order. Input
128/// formals (`AssertionPort`s) and outputs (`FormalArgument`s) are listed by
129/// the instance's members in the same order as its connections.
130pub fn checker_outputs<'d>(inst: &Symbol<'d>) -> Vec<CheckerOutput<'d>> {
131    let formals = inst.members().filter(|m| {
132        matches!(
133            m.kind(),
134            SymbolKind::AssertionPort | SymbolKind::FormalArgument
135        )
136    });
137    let mut out = Vec::new();
138    for (formal, conn) in formals.zip(inst.checker_instance_connections()) {
139        if formal.kind() == SymbolKind::FormalArgument {
140            out.push(CheckerOutput {
141                formal,
142                assign: conn.actual().and_then(|a| a.as_expression()),
143                initial: conn.output_initial_expr(),
144            });
145        }
146    }
147    out
148}
149
150/// The output formal of a checker instance that `formal` is, with its
151/// instance's connection data: `None` for any other symbol.
152pub fn checker_output_of<'d>(formal: &Symbol<'d>) -> Option<CheckerOutput<'d>> {
153    if formal.kind() != SymbolKind::FormalArgument {
154        return None;
155    }
156    let inst = formal.parent()?.checker_instance_body_parent_instance()?;
157    checker_outputs(&inst)
158        .into_iter()
159        .find(|o| o.formal.uid() == formal.uid())
160}
161
162/// Whether `sym` is declared in a checker (LRM 17): the first instance or
163/// checker instance body up its parent chain is a checker instance's. A
164/// checker's procedures and continuous assignments are scheduled in the
165/// Reactive region (LRM 17.7.3).
166pub fn is_in_checker(sym: &Symbol<'_>) -> bool {
167    let mut cur = Some(*sym);
168    while let Some(s) = cur {
169        let kind = s.kind();
170        if kind == SymbolKind::CheckerInstanceBody {
171            return true;
172        }
173        if kind == SymbolKind::InstanceBody {
174            return false;
175        }
176        cur = s.parent();
177    }
178    false
179}
180
181/// Whether `sym` is declared in a `program` (LRM 24.3): the instance body it
182/// belongs to ([`enclosing_instance_body`]) was instantiated from a program
183/// definition. A program's `initial` blocks and the code they run are
184/// scheduled in the Reactive region (LRM 4.4.2.6, 24.3.1), after the design
185/// has settled for the time step, like a checker's ([`is_in_checker`]).
186pub fn is_in_program(sym: &Symbol<'_>) -> bool {
187    enclosing_instance_body(sym)
188        .and_then(|b| b.instance_body_definition())
189        .is_some_and(|d| d.definition_kind() == Some(DefinitionKind::Program))
190}
191
192/// The instance body whose items `sym` is one of: the first `InstanceBody`
193/// up its parent chain, except that a checker instance's body (LRM 17.3)
194/// hands over to the instance body that instantiates the checker -- its
195/// items run there ([`module_items`]), although its `parent()` is the scope
196/// the checker was declared in. `None` for a symbol of no instance (a
197/// package's).
198pub fn enclosing_instance_body<'d>(sym: &Symbol<'d>) -> Option<Symbol<'d>> {
199    let mut cur = Some(*sym);
200    while let Some(s) = cur {
201        let kind = s.kind();
202        if kind == SymbolKind::InstanceBody {
203            return Some(s);
204        }
205        cur = if kind == SymbolKind::CheckerInstanceBody {
206            s.checker_instance_body_parent_instance()?.parent()
207        } else {
208            s.parent()
209        };
210    }
211    None
212}
213
214/// The package (or compilation unit) whose items `sym` is one of, `None` for
215/// a symbol of an instance.
216pub fn enclosing_package<'d>(sym: &Symbol<'d>) -> Option<Symbol<'d>> {
217    if enclosing_instance_body(sym).is_some() {
218        return None;
219    }
220    let mut cur = sym.parent();
221    while let Some(p) = cur {
222        if matches!(p.kind(), SymbolKind::Package | SymbolKind::CompilationUnit) {
223            return Some(p);
224        }
225        cur = p.parent();
226    }
227    None
228}
229
230/// The instantiated generate blocks nested anywhere in `body` (the scopes
231/// [`module_items`] flattens), outermost first.
232pub fn generate_scopes<'d>(body: &Symbol<'d>) -> Vec<Symbol<'d>> {
233    let mut out = Vec::new();
234    let mut stack = vec![*body];
235    while let Some(scope) = stack.pop() {
236        for m in scope.members() {
237            if let Some(blocks) = generate_children(&m) {
238                for b in blocks {
239                    out.push(b);
240                    stack.push(b);
241                }
242            } else if m.kind() == SymbolKind::CheckerInstance
243                || m.kind() == SymbolKind::StatementBlock
244            {
245                // Its generate blocks flatten into the enclosing instance
246                // like its other items ([`push_items`]); so do those of a
247                // procedural checker declared in a statement block.
248                stack.push(m);
249            } else if let Some(elements) = checker_array_elements(&m) {
250                stack.extend(elements);
251            }
252        }
253    }
254    out
255}
256
257/// The checker instances an array of checker instances `m` (LRM 17.3,
258/// `name_of_instance` with unpacked dimensions) stands for, every dimension
259/// flattened, in element order; `None` for any other symbol.
260pub fn checker_array_elements<'d>(m: &Symbol<'d>) -> Option<Vec<Symbol<'d>>> {
261    fn flatten<'d>(a: &Symbol<'d>, out: &mut Vec<Symbol<'d>>) -> bool {
262        for e in a.instance_array_elements() {
263            if e.kind() == SymbolKind::CheckerInstance {
264                out.push(e);
265            } else if e.kind() != SymbolKind::InstanceArray || !flatten(&e, out) {
266                // A module or primitive array: not a checker array.
267                return false;
268            }
269        }
270        true
271    }
272    if m.kind() != SymbolKind::InstanceArray {
273        return None;
274    }
275    let mut out = Vec::new();
276    (flatten(m, &mut out) && !out.is_empty()).then_some(out)
277}
278
279/// The instantiated blocks `m` stands for, if it is a generate construct.
280fn generate_children<'d>(m: &Symbol<'d>) -> Option<Vec<Symbol<'d>>> {
281    let kind = m.kind();
282    if kind == SymbolKind::GenerateBlock {
283        return Some(if m.generate_block_is_uninstantiated() {
284            Vec::new()
285        } else {
286            vec![*m]
287        });
288    }
289    if kind == SymbolKind::GenerateBlockArray {
290        return Some(
291            m.members()
292                .filter(|e| {
293                    e.kind() == SymbolKind::GenerateBlock && !e.generate_block_is_uninstantiated()
294                })
295                .collect(),
296        );
297    }
298    None
299}
300
301fn push_items<'d>(scope: &Symbol<'d>, out: &mut Vec<Symbol<'d>>) {
302    for m in scope.members() {
303        match generate_children(&m) {
304            Some(blocks) => {
305                for b in blocks {
306                    push_items(&b, out);
307                }
308            }
309            // A checker instance (LRM 17.3) is a pseudo-instance: its
310            // body's items belong to the enclosing instance and run with
311            // it, as a generate block's do. Its input formals are
312            // `AssertionPort` clones whose actuals slang already
313            // substituted into every body expression, so they are not
314            // items.
315            None if m.kind() == SymbolKind::CheckerInstance => push_items(&m, out),
316            None if checker_array_elements(&m).is_some() => {
317                for e in checker_array_elements(&m).unwrap_or_default() {
318                    push_items(&e, out);
319                }
320            }
321            None if m.kind() == SymbolKind::AssertionPort => {}
322            None if m.kind() == SymbolKind::StatementBlock => {
323                out.push(m);
324                push_block_checkers(&m, out);
325            }
326            None => out.push(m),
327        }
328    }
329}
330
331/// The items of the procedural checker instances declared in the statement
332/// block `block` (LRM 17.3), nested blocks' included. A procedural instance's
333/// own variables, functions and procedures exist whether or not the
334/// instantiating procedure ever reaches the statement ("each of the three
335/// instantiations has its own version of sum").
336fn push_block_checkers<'d>(block: &Symbol<'d>, out: &mut Vec<Symbol<'d>>) {
337    for m in block.members() {
338        if m.kind() == SymbolKind::CheckerInstance {
339            push_items(&m, out);
340        } else if m.kind() == SymbolKind::StatementBlock {
341            push_block_checkers(&m, out);
342        }
343    }
344}
345
346/// What a free checker variable (a `rand` checker variable, LRM 17.7) is to
347/// assume-based randomization (17.7.2).
348#[derive(Clone, Copy, Debug, PartialEq, Eq)]
349pub enum FreeVarKind {
350    /// `rand const`: a random value fixed at initialization.
351    Const,
352    /// Not assigned by the checker: the simulator chooses its values.
353    Active,
354    /// The left-hand side of a checker variable assignment (17.7.1): it only
355    /// takes a random initial value.
356    Inactive,
357}
358
359/// The kind of `var` as a free checker variable, `None` for any other symbol.
360pub fn free_var_kind(var: &Symbol<'_>) -> Option<FreeVarKind> {
361    if var.kind() != SymbolKind::Variable
362        || !var
363            .variable_flags()
364            .contains(sv_lang::VariableFlags::CHECKER_FREE_VARIABLE)
365    {
366        return None;
367    }
368    if var.variable_flags().contains(sv_lang::VariableFlags::CONST) {
369        return Some(FreeVarKind::Const);
370    }
371    let assigned = var
372        .parent()
373        .and_then(|b| b.checker_instance_body_parent_instance())
374        .is_some_and(|inst| checker_assigned(&inst).contains(&var.uid()));
375    Some(if assigned {
376        FreeVarKind::Inactive
377    } else {
378        FreeVarKind::Active
379    })
380}
381
382/// The variables the procedures and continuous assignments of checker instance
383/// `inst` assign (17.7.1): whatever an assignment's left-hand side names.
384fn checker_assigned(inst: &Symbol<'_>) -> std::collections::HashSet<crate::symid::SymbolId> {
385    let mut out = std::collections::HashSet::new();
386    let mut stack = Vec::new();
387    for m in module_items(inst) {
388        if m.kind() == SymbolKind::ProceduralBlock {
389            stack.extend(crate::process::cfg::proc_body(&m).map(|b| b.as_sem_node()));
390        } else if m.kind() == SymbolKind::ContinuousAssign {
391            stack.extend(m.continuous_assign_assignment().map(|e| e.as_sem_node()));
392        }
393    }
394    while let Some(n) = stack.pop() {
395        if let Some(e) = n.as_expression()
396            && e.kind() == sv_lang::kinds::ExpressionKind::Assignment
397            && let Some(lhs) = e.left()
398        {
399            let mut lv = vec![lhs.as_sem_node()];
400            while let Some(x) = lv.pop() {
401                if let Some(xe) = x.as_expression()
402                    && crate::symid::is_value_name(xe.kind())
403                    && let Some(s) = xe.referenced_symbol()
404                {
405                    out.insert(s.uid());
406                }
407                lv.extend(x.children());
408            }
409        }
410        stack.extend(n.children());
411    }
412    out
413}
414
415/// The generator state a free checker variable's random values start from:
416/// its hierarchical path, hashed (17.7.2; stable from run to run, distinct per
417/// variable and instance).
418pub fn free_var_seed(var: &Symbol<'_>) -> u64 {
419    let mut h: u64 = 0xcbf2_9ce4_8422_2325;
420    for b in var.hierarchical_path().bytes() {
421        h = (h ^ u64::from(b)).wrapping_mul(0x0000_0100_0000_01b3);
422    }
423    h
424}
425
426/// Per checker instance of `body` that declares active free variables
427/// ([`FreeVarKind::Active`]): the instance and those variables.
428pub fn free_var_groups<'d>(body: &Symbol<'d>) -> Vec<(Symbol<'d>, Vec<Symbol<'d>>)> {
429    checker_instances(body)
430        .into_iter()
431        .filter_map(|inst| {
432            let vars: Vec<Symbol<'d>> = inst
433                .members()
434                .filter(|m| free_var_kind(m) == Some(FreeVarKind::Active))
435                .collect();
436            (!vars.is_empty()).then_some((inst, vars))
437        })
438        .collect()
439}
440
441/// [`module_items`], then [`static_procedural_vars`]: everything in `body`
442/// that has storage of the instance's own (a STATE slot), in the order
443/// slots are numbered.
444pub fn state_items<'d>(body: &Symbol<'d>) -> Vec<Symbol<'d>> {
445    let mut out = module_items(body);
446    out.extend(static_procedural_vars(body));
447    out
448}
449
450/// The static variables `body`'s procedural code declares: those of its
451/// tasks and functions, their formal arguments included (not a function's
452/// result variable), and those of the
453/// blocks in its processes and subroutines. LRM 1800-2023 §6.21: each has
454/// one copy per instance, shared by every call and every process -- STATE
455/// storage, like a module variable, rather than a process's frame.
456pub fn static_procedural_vars<'d>(body: &Symbol<'d>) -> Vec<Symbol<'d>> {
457    let is_static = |v: &Symbol<'_>| {
458        matches!(v.kind(), SymbolKind::Variable | SymbolKind::FormalArgument)
459            && v.variable_lifetime() == sv_lang::VariableLifetime::Static
460            && !is_array_formal(v)
461    };
462    let mut out = Vec::new();
463    // The module-side definitions of interface `extern` methods are
464    // subroutines of the body that are not among its members.
465    let mut items = module_items(body);
466    items.extend(super::itf::extern_impls_in(body));
467    for m in items {
468        let kind = m.kind();
469        if kind == SymbolKind::Subroutine {
470            let result = m.subroutine_return_val_var().map(|r| r.uid());
471            for v in m.members() {
472                if is_static(&v) && Some(v.uid()) != result {
473                    out.push(v);
474                }
475            }
476        }
477        if kind != SymbolKind::Subroutine && kind != SymbolKind::ProceduralBlock {
478            continue;
479        }
480        let Some(code) = crate::process::cfg::proc_body(&m) else {
481            continue;
482        };
483        let mut stack = vec![code.as_sem_node()];
484        while let Some(n) = stack.pop() {
485            if let Some(s) = n.as_statement() {
486                for block in crate::process::cfg::block_scopes(&s) {
487                    out.extend(block.members().filter(|v| is_static(v)));
488                }
489            }
490            let mut kids = crate::process::cfg::live_children(&n);
491            kids.reverse();
492            stack.extend(kids);
493        }
494    }
495    out
496}
497
498/// Whether `var` is a formal argument of fixed unpacked-array type: passed
499/// as the caller's temporary copy (LRM 13.5.1), never instance storage, even
500/// in a static subroutine (LRM 13.3). A queue / dynamic array / associative
501/// formal is a 64-bit handle and stays static storage.
502fn is_array_formal(var: &Symbol<'_>) -> bool {
503    var.kind() == SymbolKind::FormalArgument
504        && var
505            .value_type()
506            .is_some_and(|t| t.is_unpacked_array() && !crate::state::storage::is_collection(&t))
507}
508
509/// Whether `var` is one of [`static_procedural_vars`] of an instance or of a
510/// package.
511pub fn is_static_procedural_var(var: &Symbol<'_>) -> bool {
512    if !matches!(
513        var.kind(),
514        SymbolKind::Variable | SymbolKind::FormalArgument
515    ) || var.variable_lifetime() != sv_lang::VariableLifetime::Static
516        || is_array_formal(var)
517    {
518        return false;
519    }
520    let mut procedural = false;
521    let mut in_class = false;
522    let mut in_task = false;
523    let mut cur = var.parent();
524    while let Some(p) = cur {
525        let kind = p.kind();
526        if kind == SymbolKind::Subroutine {
527            if p.subroutine_return_val_var().map(|r| r.uid()) == Some(var.uid()) {
528                return false;
529            }
530            procedural = true;
531            in_task |= p.subroutine_kind() == Some(sv_lang::SubroutineKind::Task);
532        } else if kind == SymbolKind::StatementBlock || kind == SymbolKind::ProceduralBlock {
533            procedural = true;
534        } else if kind == SymbolKind::InstanceBody || kind == SymbolKind::CheckerInstanceBody {
535            // A class method's static local lives with its class (LRM 8.9,
536            // 6.21), whether the class is in a module or the compilation unit.
537            return procedural && !in_class;
538        } else if kind == SymbolKind::ClassType {
539            in_class = true;
540        } else if kind == SymbolKind::Package || kind == SymbolKind::CompilationUnit {
541            // A package TASK's static local is the package's own state (LRM
542            // 6.21: one copy across calls; an inlined task body would
543            // re-initialize a frame local per call). Package functions keep
544            // their own storage, formals stay call-frame locals, and a class
545            // method's statics live with its class.
546            return procedural && !in_class && in_task && var.kind() == SymbolKind::Variable;
547        }
548        cur = p.parent();
549    }
550    false
551}
552
553/// A variable's initializer, including the one an ANSI port declaration
554/// carries (LRM 1800-2023 6.8, 23.2.2.4): `module m(output reg [31:0] z = 5)`
555/// puts the `= 5` on the `Port` symbol (`port_initializer`), not on the
556/// internal `Variable`, so `Symbol::initializer` alone misses it.
557pub fn variable_initializer<'d>(var: &Symbol<'d>) -> Option<Expression<'d>> {
558    if let Some(init) = var.initializer() {
559        return Some(init);
560    }
561    if var.kind() != SymbolKind::Variable {
562        return None;
563    }
564    let body = var
565        .parent()
566        .filter(|p| p.kind() == SymbolKind::InstanceBody)?;
567    let uid = var.uid();
568    body.instance_body_ports()
569        .find(|p| p.port_internal_symbol().is_some_and(|i| i.uid() == uid))
570        .and_then(|p| p.port_initializer())
571}