use std::collections::HashMap;
use sv_lang::kinds::{ExpressionKind, SymbolKind};
use sv_lang::{Expression, SemNode, Statement, Symbol, VariableLifetime};
use sv_sim_sim::symid::{SymbolId, SymbolUid};
use crate::error::LoweringError;
use crate::process_lower::SignalSlot;
use crate::types::TypeWidth;
#[derive(Clone, Copy)]
pub struct HostedAssertion<'d> {
pub stmt: Statement<'d>,
pub procedural: bool,
pub instance: Option<Symbol<'d>>,
}
impl<'d> HostedAssertion<'d> {
pub fn queue_key(&self, block: &Symbol<'_>) -> String {
queued_assertion_key(block, self.instance.as_ref(), &self.stmt)
}
}
impl std::fmt::Debug for HostedAssertion<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let s = self.stmt.source_range().start;
write!(f, "HostedAssertion({}.{})", s.buffer, s.offset)
}
}
impl PartialEq for HostedAssertion<'_> {
fn eq(&self, other: &Self) -> bool {
self.stmt.source_range().start == other.stmt.source_range().start
&& self.instance.map(|i| i.uid()) == other.instance.map(|i| i.uid())
}
}
impl Eq for HostedAssertion<'_> {}
pub fn assertion_key(block: &Symbol<'_>, stmt: &Statement<'_>) -> String {
let s = stmt.source_range().start;
format!("{}@{}.{}", block.uid().key_string(), s.buffer, s.offset)
}
pub fn queued_assertion_key(
block: &Symbol<'_>,
instance: Option<&Symbol<'_>>,
stmt: &Statement<'_>,
) -> String {
let base = assertion_key(block, stmt);
match instance {
Some(i) => format!("{base}#{}", i.uid().key_string()),
None => base,
}
}
#[derive(Clone)]
pub struct CaptureItem<'d> {
pub key: SymbolId,
pub expr: Expression<'d>,
pub width: u32,
pub signed: bool,
}
impl CaptureItem<'_> {
pub fn words(&self) -> usize {
if self.width > 64 {
2 * (self.width as usize).div_ceil(64)
} else {
1
}
}
}
pub fn capture_words(items: &[CaptureItem<'_>]) -> usize {
items.iter().map(CaptureItem::words).sum()
}
pub fn const_cast_key(e: &Expression<'_>) -> SymbolId {
let s = e.source_range().start;
SymbolId::synthetic_tagged(&format!("const_cast.{}.{}", s.buffer, s.offset))
}
pub fn call_arg_key(e: &Expression<'_>) -> SymbolId {
let r = e.source_range();
SymbolId::synthetic(&format!(
"call_arg.{}.{}.{}",
r.start.buffer, r.start.offset, r.end.offset
))
}
thread_local! {
static CALL_ARGS_ACTIVE: std::cell::Cell<bool> = const { std::cell::Cell::new(false) };
}
pub fn call_args_active() -> bool {
CALL_ARGS_ACTIVE.with(std::cell::Cell::get)
}
thread_local! {
static SPAWN_ARGS: std::cell::RefCell<std::collections::HashMap<SymbolId, (usize, u32, bool)>> =
std::cell::RefCell::new(std::collections::HashMap::new());
}
pub fn spawn_arg_input(key: &SymbolId) -> Option<(usize, u32, bool)> {
SPAWN_ARGS.with(|m| m.borrow().get(key).copied())
}
pub struct SpawnArgScope {
_active: CallArgScope,
}
impl SpawnArgScope {
pub fn enter(args: Vec<(SymbolId, (usize, u32, bool))>) -> SpawnArgScope {
SPAWN_ARGS.with(|m| *m.borrow_mut() = args.into_iter().collect());
SpawnArgScope {
_active: CallArgScope::enter(),
}
}
}
impl Drop for SpawnArgScope {
fn drop(&mut self) {
SPAWN_ARGS.with(|m| m.borrow_mut().clear());
}
}
pub struct CallArgScope(bool);
impl CallArgScope {
pub fn enter() -> CallArgScope {
CallArgScope(CALL_ARGS_ACTIVE.with(|c| c.replace(true)))
}
}
impl Drop for CallArgScope {
fn drop(&mut self) {
CALL_ARGS_ACTIVE.with(|c| c.set(self.0));
}
}
pub fn is_const_cast(e: &Expression<'_>) -> bool {
e.kind() == ExpressionKind::Conversion && e.conversion_is_const_cast() == Some(true)
}
pub fn capture_items<'d>(
signals: &HashMap<SymbolId, SignalSlot>,
stmt: Statement<'d>,
at: &Symbol<'d>,
) -> Result<Vec<CaptureItem<'d>>, LoweringError> {
let mut items: Vec<CaptureItem<'d>> = Vec::new();
let is_expect = stmt.assertion_kind() == Some(sv_lang::AssertionKind::Expect);
let mut roots: Vec<SemNode<'d>> = if is_expect {
Vec::new()
} else {
vec![stmt.as_sem_node()]
};
if let Some(root) = stmt
.as_sem_node()
.children()
.into_iter()
.find_map(|n| n.as_assertion_expr())
{
let mut leaves = Vec::new();
crate::sva::collect_boolean_leaves(root, &mut leaves);
if is_expect {
roots.push(root.as_sem_node());
}
roots.extend(leaves.into_iter().map(|l| l.as_sem_node()));
}
for root in roots {
let mut stack = vec![root];
while let Some(n) = stack.pop() {
if let Some(e) = n.as_expression() {
if is_const_cast(&e) {
let key = const_cast_key(&e);
if !items.iter().any(|i| i.key == key) {
items.push(item_of(key, e, at)?);
}
continue;
}
if matches!(
e.kind(),
ExpressionKind::NamedValue | ExpressionKind::HierarchicalValue
) && let Some(sym) = e.referenced_symbol()
&& ((sym.kind() == SymbolKind::Variable
&& sym.variable_lifetime() == VariableLifetime::Automatic)
|| sym.kind() == SymbolKind::FormalArgument
|| sym.kind() == SymbolKind::Iterator)
&& !signals.contains_key(&sym.uid())
{
let key = sym.uid();
if !items.iter().any(|i| i.key == key) {
items.push(item_of(key, e, at)?);
}
}
}
let mut kids = n.children();
kids.reverse();
stack.extend(kids);
}
}
items.sort_by(|a, b| a.key.cmp(&b.key));
Ok(items)
}
pub fn const_cast_reads<'d>(stmt: Statement<'d>) -> Vec<Symbol<'d>> {
let mut roots: Vec<SemNode<'d>> = vec![stmt.as_sem_node()];
if let Some(root) = stmt
.as_sem_node()
.children()
.into_iter()
.find_map(|n| n.as_assertion_expr())
{
let mut leaves = Vec::new();
crate::sva::collect_boolean_leaves(root, &mut leaves);
roots.extend(leaves.into_iter().map(|l| l.as_sem_node()));
}
let mut out: Vec<Symbol<'d>> = Vec::new();
for root in roots {
let mut stack = vec![(root, false)];
while let Some((n, inside)) = stack.pop() {
let mut inside = inside;
if let Some(e) = n.as_expression() {
if is_const_cast(&e) {
inside = true;
}
if inside
&& matches!(
e.kind(),
ExpressionKind::NamedValue | ExpressionKind::HierarchicalValue
)
&& let Some(sym) = e.referenced_symbol()
&& matches!(sym.kind(), SymbolKind::Variable | SymbolKind::Net)
&& !out.contains(&sym)
{
out.push(sym);
}
}
stack.extend(n.children().into_iter().map(|k| (k, inside)));
}
}
out
}
fn item_of<'d>(
key: SymbolId,
expr: Expression<'d>,
at: &Symbol<'d>,
) -> Result<CaptureItem<'d>, LoweringError> {
let ty = expr.expr_type().ok_or_else(|| {
LoweringError::unsupported_at("a captured value with no type in an assertion", at)
})?;
if !ty.is_integral() {
return Err(LoweringError::unsupported_at(
"a captured real, string or aggregate value in a procedural concurrent assertion is \
not executable yet",
at,
));
}
let width = ty.value_width();
if width == 0 {
return Err(LoweringError::unsupported_at(
"a captured value of no bits in a procedural concurrent assertion",
at,
));
}
Ok(CaptureItem {
key,
expr,
width,
signed: ty.is_signed(),
})
}
pub fn first_non_captured_read(items: &[CaptureItem<'_>], leaf: Expression<'_>) -> Option<String> {
let mut stack = vec![leaf.as_sem_node()];
while let Some(n) = stack.pop() {
if let Some(e) = n.as_expression() {
if is_const_cast(&e) || e.constant_value().is_some() {
continue;
}
if matches!(
e.kind(),
ExpressionKind::NamedValue | ExpressionKind::HierarchicalValue
) && let Some(sym) = e.referenced_symbol()
&& !items.iter().any(|it| it.key == sym.uid())
{
return Some(sym.name().to_string());
}
}
stack.extend(n.children());
}
None
}
pub fn items_used_by(items: &[CaptureItem<'_>], leaf: Expression<'_>) -> Vec<usize> {
let mut used = Vec::new();
if items.is_empty() {
return used;
}
let mut stack = vec![leaf.as_sem_node()];
while let Some(n) = stack.pop() {
if let Some(e) = n.as_expression() {
if is_const_cast(&e) {
let key = const_cast_key(&e);
if let Some(i) = items.iter().position(|it| it.key == key)
&& !used.contains(&i)
{
used.push(i);
}
continue;
}
if matches!(
e.kind(),
ExpressionKind::NamedValue | ExpressionKind::HierarchicalValue
) && let Some(sym) = e.referenced_symbol()
&& let key = sym.uid()
&& let Some(i) = items.iter().position(|it| it.key == key)
&& !used.contains(&i)
{
used.push(i);
}
}
stack.extend(sv_sim_sim::process::cfg::live_children(&n));
}
used
}