pub mod actions;
pub mod build;
pub mod disable;
pub mod emit;
pub mod flow;
pub mod gclk;
pub mod leaf;
pub mod named;
pub mod op;
pub mod proc;
pub mod ranges;
pub mod trees;
use sv_lang::kinds::{AssertionExprKind, ExpressionKind};
use sv_lang::{AssertionExpr, Expression, SemNode};
pub fn collect_boolean_leaves<'d>(expr: AssertionExpr<'d>, out: &mut Vec<Expression<'d>>) {
for child in assertion_expr_leaf_children(expr) {
if let Some(e) = child.as_expression() {
out.push(e);
} else if let Some(a) = child.as_assertion_expr() {
collect_boolean_leaves(a, out);
}
}
}
pub(crate) fn assertion_expr_leaf_children<'d>(expr: AssertionExpr<'d>) -> Vec<SemNode<'d>> {
let children = expr.children();
let children: Vec<SemNode<'d>> = if matches!(
expr.kind(),
AssertionExprKind::SequenceWithMatch | AssertionExprKind::FirstMatch
) {
children
.into_iter()
.filter(|c| c.as_assertion_expr().is_some())
.collect()
} else {
children
};
children
.into_iter()
.flat_map(expand_assertion_instance)
.collect()
}
fn expand_assertion_instance<'d>(node: SemNode<'d>) -> Vec<SemNode<'d>> {
match node.as_expression() {
Some(e) if e.kind() == ExpressionKind::AssertionInstance => e
.as_sem_node()
.children()
.into_iter()
.flat_map(expand_assertion_instance)
.collect(),
_ => vec![node],
}
}
#[cfg(test)]
mod tests {
use super::*;
use sv_lang::{AnalysisFlags, Compilation, Session, Symbol};
fn with_clocking_root<R>(
extra_decls: &str,
property_body: &str,
f: impl FnOnce(AssertionExpr<'_>, Symbol<'_>) -> R,
) -> R {
let src = format!(
"module m;\n\
{extra_decls}\n\
logic clk, a, b;\n\
int cnt;\n\
always_ff @(posedge clk) begin\n\
assert property (@(posedge clk) {property_body});\n\
end\n\
endmodule\n"
);
let session = Session::new();
let mut comp = Compilation::new(&session).unwrap();
comp.add_source(&src).unwrap();
let design = comp.compile().unwrap();
assert!(
!design.has_fatal_errors(),
"fixture failed to compile: {src}"
);
let body = design
.top_instances()
.next()
.unwrap()
.instance_body()
.unwrap();
let analysis = design.analyze(AnalysisFlags::NONE, 1).unwrap();
let proc = analysis.procedures(body).next().unwrap();
let assertion = analysis.assertions(proc.symbol()).next().unwrap();
let root = assertion.root().as_assertion_expr().unwrap();
assert_eq!(root.kind(), AssertionExprKind::Clocking);
f(root, body)
}
#[test]
fn collects_leaf_through_named_sequence_reference() {
with_clocking_root(
"function static int f(); return 1; endfunction\n\
sequence seq1; f() == 1; endsequence",
"seq1",
|clocking, _scope| {
let mut leaves = Vec::new();
collect_boolean_leaves(clocking, &mut leaves);
assert_eq!(
leaves.len(),
1,
"expected exactly one real Boolean leaf (f() == 1), got {leaves:?}"
);
assert_eq!(
leaves[0].kind(),
ExpressionKind::BinaryOp,
"the collected leaf must be the sequence's own `f() == 1` expression, not \
the AssertionInstanceExpression wrapper for `seq1` itself"
);
},
);
}
#[test]
fn excludes_inline_sequence_match_item_from_collected_leaves() {
with_clocking_root("", "(a ##1 b, cnt++)", |clocking, _scope| {
let mut leaves = Vec::new();
collect_boolean_leaves(clocking, &mut leaves);
assert_eq!(
leaves.len(),
2,
"expected exactly the two sub-sequence leaves (a, b), got {leaves:?}"
);
for leaf in &leaves {
assert_eq!(
leaf.kind(),
ExpressionKind::NamedValue,
"a sequence match item must never appear in the leaf set: {leaves:?}"
);
}
});
}
#[test]
fn excludes_first_match_match_item_from_collected_leaves() {
with_clocking_root("", "first_match(a ##1 b, cnt++)", |clocking, _scope| {
let mut leaves = Vec::new();
collect_boolean_leaves(clocking, &mut leaves);
assert_eq!(
leaves.len(),
2,
"expected exactly the two sub-sequence leaves (a, b), got {leaves:?}"
);
for leaf in &leaves {
assert_eq!(
leaf.kind(),
ExpressionKind::NamedValue,
"a first_match match item must never appear in the leaf set: {leaves:?}"
);
}
});
}
}