use sv_lang::kinds::AssertionExprKind;
use sv_lang::{AssertionExpr, Expression, SemNode, Symbol};
use sv_sim_sim::sva::sampled::is_future_call;
use super::{collect_boolean_leaves, expand_assertion_instance};
use crate::error::LoweringError;
const MAX_INSTANCE_DEPTH: usize = 256;
fn future_calls<'d>(e: Expression<'d>) -> (bool, bool) {
let mut any = false;
let mut nested = false;
let mut stack = vec![(e.as_sem_node(), false)];
while let Some((n, inside)) = stack.pop() {
let mut inside_here = inside;
if let Some(x) = n.as_expression()
&& is_future_call(x)
{
any = true;
nested |= inside;
inside_here = true;
}
for c in n.children() {
stack.push((c, inside_here));
}
}
(any, nested)
}
#[derive(Default)]
struct Scan {
match_items: bool,
future_in_item: bool,
too_deep: bool,
}
fn scan<'d>(node: SemNode<'d>, depth: usize, out: &mut Scan) {
if depth > MAX_INSTANCE_DEPTH {
out.too_deep = true;
return;
}
let Some(a) = node.as_assertion_expr() else {
return;
};
let children = a.children();
if matches!(
a.kind(),
AssertionExprKind::SequenceWithMatch | AssertionExprKind::FirstMatch
) {
for c in &children {
if let Some(item) = c.as_expression()
&& c.as_assertion_expr().is_none()
{
out.match_items = true;
out.future_in_item |= future_calls(item).0;
}
}
}
for c in children {
for n in expand_assertion_instance(c) {
scan(n, depth + 1, out);
}
}
}
pub(crate) fn check<'d>(
root: AssertionExpr<'d>,
scope: &Symbol<'d>,
) -> Result<bool, LoweringError> {
let mut leaves = Vec::new();
collect_boolean_leaves(root, &mut leaves);
let mut any = false;
for leaf in leaves {
let (has, nested) = future_calls(leaf);
if nested {
return Err(LoweringError::unsupported_at(
"global clocking future sampled value functions shall not be nested (LRM 16.9.4)",
scope,
));
}
any |= has;
}
let mut found = Scan::default();
scan(root.as_sem_node(), 0, &mut found);
if found.future_in_item {
any = true;
}
if !any {
return Ok(false);
}
if found.too_deep {
return Err(LoweringError::unsupported_at(
"an assertion with global clocking future sampled value functions nests named \
instances too deeply to check that it has no match items (LRM 16.9.4)",
scope,
));
}
if found.match_items {
return Err(LoweringError::unsupported_at(
"a global clocking future sampled value function shall not be used in an assertion \
containing sequence match items (LRM 16.9.4, 16.10, 16.11)",
scope,
));
}
Ok(true)
}