use std::collections::BTreeSet;
use sv_lang::kinds::{ExpressionKind, StatementKind, SymbolKind, TimingControlKind};
use sv_lang::{Expression, KnownSystemName, Symbol};
use crate::symid::{SymbolId, SymbolUid};
#[derive(Clone, Copy)]
pub struct Watcher<'d> {
pub block: Symbol<'d>,
pub inst: Expression<'d>,
}
impl<'d> Watcher<'d> {
pub fn id(&self) -> SymbolId {
watcher_id(&self.block.uid(), &self.inst)
}
}
pub fn watcher_id(owner: &SymbolId, inst: &Expression<'_>) -> SymbolId {
let s = inst.source_range().start;
owner.derive(&format!("ep{}.{}", s.buffer, s.offset))
}
pub fn watched_call<'d>(e: &Expression<'d>) -> Option<Expression<'d>> {
if e.kind() != ExpressionKind::Call
|| e.system_subroutine()?.known_name_id() != KnownSystemName::Triggered
{
return None;
}
let op = *e.operands().first()?;
(op.kind() == ExpressionKind::AssertionInstance).then_some(op)
}
pub fn block_watchers<'d>(block: &Symbol<'d>) -> Vec<Watcher<'d>> {
let mut out = own_watchers(block);
let instance = crate::netlist::enclosing_instance_body(block).map(|b| b.uid());
let mut visited = BTreeSet::new();
let mut work = vec![*block];
while let Some(owner) = work.pop() {
let Some(body) = crate::process::cfg::proc_body(&owner) else {
continue;
};
let mut calls = Vec::new();
let mut stack = vec![body.as_sem_node()];
while let Some(n) = stack.pop() {
if let Some(e) = n.as_expression()
&& e.kind() == ExpressionKind::Call
&& e.is_system_call() != Some(true)
&& let Some(callee) = crate::netlist::itf::call_target(&e)
&& callee.kind() == SymbolKind::Subroutine
&& visited.insert(callee.uid())
&& crate::netlist::enclosing_instance_body(&callee).map(|b| b.uid()) == instance
{
calls.push(callee);
}
let mut kids = crate::process::cfg::live_children(&n);
kids.reverse();
stack.extend(kids);
}
for callee in calls {
out.extend(own_watchers(&callee));
work.push(callee);
}
}
out
}
fn own_watchers<'d>(block: &Symbol<'d>) -> Vec<Watcher<'d>> {
let Some(body) = crate::process::cfg::proc_body(block) else {
return Vec::new();
};
let mut out = Vec::new();
let mut seen = BTreeSet::new();
let mut push = |inst: Expression<'d>, out: &mut Vec<Watcher<'d>>| {
let s = inst.source_range().start;
if seen.insert((s.buffer, s.offset)) {
out.push(Watcher {
block: *block,
inst,
});
}
};
let mut stack = vec![body.as_sem_node()];
while let Some(n) = stack.pop() {
if let Some(s) = n.as_statement()
&& s.kind() == StatementKind::ConcurrentAssertion
{
for inst in clock_sequences(n) {
push(inst, &mut out);
}
continue;
}
if let Some(e) = n.as_expression()
&& let Some(inst) = watched_call(&e)
{
push(inst, &mut out);
continue;
}
if let Some(tc) = n.as_timing_control()
&& tc.kind() == TimingControlKind::SignalEvent
&& let Some(e) = tc.children().first().and_then(|c| c.as_expression())
&& e.kind() == ExpressionKind::AssertionInstance
{
push(e, &mut out);
continue;
}
let mut kids = crate::process::cfg::live_children(&n);
kids.reverse();
stack.extend(kids);
}
out
}
fn clock_sequences<'d>(root: sv_lang::SemNode<'d>) -> Vec<Expression<'d>> {
let mut out = Vec::new();
let mut stack = vec![root];
while let Some(n) = stack.pop() {
if let Some(tc) = n.as_timing_control()
&& tc.kind() == TimingControlKind::SignalEvent
&& let Some(e) = tc.children().first().and_then(|c| c.as_expression())
&& e.kind() == ExpressionKind::AssertionInstance
{
out.push(e);
continue;
}
let mut kids = crate::process::cfg::live_children(&n);
kids.reverse();
stack.extend(kids);
}
out
}
pub fn watchers<'d>(body: &Symbol<'d>) -> Vec<Watcher<'d>> {
let mut seen = BTreeSet::new();
crate::netlist::module_items(body)
.iter()
.filter(|item| item.kind() == SymbolKind::ProceduralBlock)
.flat_map(block_watchers)
.filter(|w| seen.insert(w.id()))
.collect()
}