Skip to main content

vj_sim/sva/
endpoint.rs

1//! Sequence end points read by procedural code (LRM 1800-2023 16.9.11, 9.4.2.4):
2//! `wait (s.triggered)`, `if (s.triggered)`, `@(s)`.
3//!
4//! The assertion engine decides an instance's end points in the Observed region.
5//! For a procedural reader each distinct instance a procedural block reads is
6//! *watched*: a hidden cell of the instance body holds, like a named `event`
7//! (`crate::state::EVENT_CELL_WIDTH`), a match count in its low 64 bits and
8//! `tick + 1` of the latest end point in its high 64 bits. A cover-sequence
9//! engine over the instance (`vj-codegen`'s watcher job) stamps it when the
10//! instance reaches an end point; `s.triggered` is then "the stamp is this time
11//! step's", and `@(s)` an any-change wait on the cell. Nothing clears it.
12//!
13//! [`watchers`] is the enumeration both slot builders append, in its order.
14
15use std::collections::BTreeSet;
16
17use sv_lang::kinds::{ExpressionKind, StatementKind, SymbolKind, TimingControlKind};
18use sv_lang::{Expression, KnownSystemName, Symbol};
19
20use crate::symid::{SymbolId, SymbolUid};
21
22/// One watched sequence instance of a procedural block.
23#[derive(Clone, Copy)]
24pub struct Watcher<'d> {
25    /// The procedural block that reads it.
26    pub block: Symbol<'d>,
27    /// The `AssertionInstance` expression.
28    pub inst: Expression<'d>,
29}
30
31impl<'d> Watcher<'d> {
32    /// The hidden cell's id ([`watcher_id`]).
33    pub fn id(&self) -> SymbolId {
34        watcher_id(&self.block.uid(), &self.inst)
35    }
36}
37
38/// The id of the hidden cell of instance `inst`, read by the block `owner`.
39pub fn watcher_id(owner: &SymbolId, inst: &Expression<'_>) -> SymbolId {
40    let s = inst.source_range().start;
41    owner.derive(&format!("ep{}.{}", s.buffer, s.offset))
42}
43
44/// The sequence instance `e` reads the end point of, when `e` is
45/// `inst.triggered` on an `AssertionInstance`.
46pub fn watched_call<'d>(e: &Expression<'d>) -> Option<Expression<'d>> {
47    if e.kind() != ExpressionKind::Call
48        || e.system_subroutine()?.known_name_id() != KnownSystemName::Triggered
49    {
50        return None;
51    }
52    let op = *e.operands().first()?;
53    (op.kind() == ExpressionKind::AssertionInstance).then_some(op)
54}
55
56/// Every watched instance of procedural block `block` -- its own, then those
57/// of the user subroutines of the same instance it calls (transitively), which
58/// read the cell through the instance handle -- in a deterministic order, one
59/// per source position and owner. A subroutine's watchers are owned by it, so
60/// several callers share them.
61pub fn block_watchers<'d>(block: &Symbol<'d>) -> Vec<Watcher<'d>> {
62    let mut out = own_watchers(block);
63    let instance = crate::netlist::enclosing_instance_body(block).map(|b| b.uid());
64    let mut visited = BTreeSet::new();
65    let mut work = vec![*block];
66    while let Some(owner) = work.pop() {
67        let Some(body) = crate::process::cfg::proc_body(&owner) else {
68            continue;
69        };
70        let mut calls = Vec::new();
71        let mut stack = vec![body.as_sem_node()];
72        while let Some(n) = stack.pop() {
73            if let Some(e) = n.as_expression()
74                && e.kind() == ExpressionKind::Call
75                && e.is_system_call() != Some(true)
76                && let Some(callee) = crate::netlist::itf::call_target(&e)
77                && callee.kind() == SymbolKind::Subroutine
78                && visited.insert(callee.uid())
79                && crate::netlist::enclosing_instance_body(&callee).map(|b| b.uid()) == instance
80            {
81                calls.push(callee);
82            }
83            let mut kids = crate::process::cfg::live_children(&n);
84            kids.reverse();
85            stack.extend(kids);
86        }
87        for callee in calls {
88            out.extend(own_watchers(&callee));
89            work.push(callee);
90        }
91    }
92    out
93}
94
95/// Every watched instance read by the code of `block` itself, in a
96/// deterministic walk order, one per source position. An assertion
97/// statement's own end-point methods are the assertion engine's (not watched).
98fn own_watchers<'d>(block: &Symbol<'d>) -> Vec<Watcher<'d>> {
99    let Some(body) = crate::process::cfg::proc_body(block) else {
100        return Vec::new();
101    };
102    let mut out = Vec::new();
103    let mut seen = BTreeSet::new();
104    let mut push = |inst: Expression<'d>, out: &mut Vec<Watcher<'d>>| {
105        let s = inst.source_range().start;
106        if seen.insert((s.buffer, s.offset)) {
107            out.push(Watcher {
108                block: *block,
109                inst,
110            });
111        }
112    };
113    let mut stack = vec![body.as_sem_node()];
114    while let Some(n) = stack.pop() {
115        if let Some(s) = n.as_statement()
116            && s.kind() == StatementKind::ConcurrentAssertion
117        {
118            // The assertion's own end-point methods are the engine's, but a
119            // sequence used as its clocking event (`@seq`, 16.5.2) ticks it at
120            // the sequence's end points: that sequence is watched.
121            for inst in clock_sequences(n) {
122                push(inst, &mut out);
123            }
124            continue;
125        }
126        if let Some(e) = n.as_expression()
127            && let Some(inst) = watched_call(&e)
128        {
129            push(inst, &mut out);
130            continue;
131        }
132        if let Some(tc) = n.as_timing_control()
133            && tc.kind() == TimingControlKind::SignalEvent
134            && let Some(e) = tc.children().first().and_then(|c| c.as_expression())
135            && e.kind() == ExpressionKind::AssertionInstance
136        {
137            push(e, &mut out);
138            continue;
139        }
140        let mut kids = crate::process::cfg::live_children(&n);
141        kids.reverse();
142        stack.extend(kids);
143    }
144    out
145}
146
147/// The sequence instances used as clocking events (`@seq`, LRM 16.5.2) anywhere
148/// in the concurrent assertion statement `root`, in a deterministic walk order.
149fn clock_sequences<'d>(root: sv_lang::SemNode<'d>) -> Vec<Expression<'d>> {
150    let mut out = Vec::new();
151    let mut stack = vec![root];
152    while let Some(n) = stack.pop() {
153        if let Some(tc) = n.as_timing_control()
154            && tc.kind() == TimingControlKind::SignalEvent
155            && let Some(e) = tc.children().first().and_then(|c| c.as_expression())
156            && e.kind() == ExpressionKind::AssertionInstance
157        {
158            out.push(e);
159            continue;
160        }
161        let mut kids = crate::process::cfg::live_children(&n);
162        kids.reverse();
163        stack.extend(kids);
164    }
165    out
166}
167
168/// Every watched instance of the procedural blocks of instance body `body` (the
169/// enumeration the state layout and the codegen signal map both append).
170pub fn watchers<'d>(body: &Symbol<'d>) -> Vec<Watcher<'d>> {
171    let mut seen = BTreeSet::new();
172    crate::netlist::module_items(body)
173        .iter()
174        .filter(|item| item.kind() == SymbolKind::ProceduralBlock)
175        .flat_map(block_watchers)
176        .filter(|w| seen.insert(w.id()))
177        .collect()
178}