1use 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#[derive(Clone, Copy)]
24pub struct Watcher<'d> {
25 pub block: Symbol<'d>,
27 pub inst: Expression<'d>,
29}
30
31impl<'d> Watcher<'d> {
32 pub fn id(&self) -> SymbolId {
34 watcher_id(&self.block.uid(), &self.inst)
35 }
36}
37
38pub 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
44pub 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
56pub 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
95fn 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 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
147fn 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
168pub 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}