use std::cell::RefCell;
use std::collections::HashSet;
use std::rc::Rc;
use sv_lang::kinds::{ExpressionKind, SymbolKind};
use sv_lang::{Expression, KnownSystemName, Symbol};
use super::event_pool::{event_count, needs_pool};
#[derive(Default)]
struct EventPlan {
forced: HashSet<String>,
group: bool,
}
thread_local! {
static PLAN: RefCell<Rc<EventPlan>> = RefCell::new(Rc::default());
}
pub struct EventPlanScope(Rc<EventPlan>);
impl EventPlanScope {
pub fn enter(design: &sv_lang::Design) -> Self {
let plan = Rc::new(compute_plan(design));
EventPlanScope(PLAN.with(|p| std::mem::replace(&mut *p.borrow_mut(), plan)))
}
}
impl Drop for EventPlanScope {
fn drop(&mut self) {
let outer = std::mem::take(&mut self.0);
PLAN.with(|p| *p.borrow_mut() = outer);
}
}
fn definition_of(body: &Symbol<'_>) -> Option<String> {
if body.kind() != SymbolKind::InstanceBody {
return None;
}
body.instance_body_parent_instance()?
.instance_definition()
.map(|d| d.name().to_string())
}
pub fn group_mode() -> bool {
PLAN.with(|p| p.borrow().group)
}
pub(super) fn plan_forces_pool(body: &Symbol<'_>) -> bool {
PLAN.with(|p| {
let p = p.borrow();
!p.forced.is_empty() && definition_of(body).is_some_and(|d| p.forced.contains(&d))
})
}
struct EventRef<'d> {
var: Symbol<'d>,
handle: bool,
}
fn hierarchical_event_operand<'d>(e: &Expression<'d>) -> Option<Symbol<'d>> {
let mut e = *e;
loop {
if e.kind() == ExpressionKind::Conversion
&& let Some(inner) = e.conversion_operand()
{
e = inner;
} else if e.kind() == ExpressionKind::ElementSelect
&& let Some(base) = e.select_value()
{
e = base;
} else {
break;
}
}
if e.kind() != ExpressionKind::HierarchicalValue {
return None;
}
let sym = e.referenced_symbol()?;
sym.value_type().as_ref().and_then(event_count)?;
Some(sym)
}
fn note<'d>(e: Option<Expression<'d>>, handle: bool, out: &mut Vec<EventRef<'d>>) {
if let Some(var) = e.as_ref().and_then(hierarchical_event_operand) {
out.push(EventRef { var, handle });
}
}
fn hierarchical_event_refs<'d>(body: &Symbol<'d>) -> Vec<EventRef<'d>> {
let mut out = Vec::new();
for m in crate::netlist::module_items(body) {
let kind = m.kind();
if kind == SymbolKind::Variable && m.value_type().as_ref().and_then(event_count).is_some() {
note(m.initializer(), true, &mut out);
}
if kind != SymbolKind::Subroutine && kind != SymbolKind::ProceduralBlock {
continue;
}
let Some(code) = crate::process::cfg::proc_body(&m) else {
continue;
};
let mut stack = vec![code.as_sem_node()];
while let Some(n) = stack.pop() {
if let Some(s) = n.as_statement() {
note(s.condition(), true, &mut out);
for block in crate::process::cfg::block_scopes(&s) {
for v in block.members() {
if v.value_type().as_ref().and_then(event_count).is_some() {
note(v.initializer(), true, &mut out);
}
}
}
}
let kids = crate::process::cfg::live_children(&n);
if let Some(e) = n.as_expression() {
note(Some(e), false, &mut out);
let is_triggered = e.kind() == ExpressionKind::Call
&& e.system_subroutine()
.is_some_and(|s| s.known_name_id() == KnownSystemName::Triggered);
if !is_triggered
&& matches!(
e.kind(),
ExpressionKind::Assignment
| ExpressionKind::BinaryOp
| ExpressionKind::UnaryOp
| ExpressionKind::Conversion
| ExpressionKind::Call
)
{
for k in &kids {
note(k.as_expression(), true, &mut out);
}
}
}
stack.extend(kids);
}
}
out
}
fn collect_bodies<'d>(inst: Symbol<'d>, out: &mut Vec<Symbol<'d>>) {
if inst.kind() == SymbolKind::InstanceArray {
for e in inst.instance_array_elements() {
collect_bodies(e, out);
}
return;
}
if inst.kind() != SymbolKind::Instance {
return;
}
if let Some(body) = inst.instance_body() {
out.push(body);
for m in crate::netlist::module_items(&body) {
collect_bodies(m, out);
}
}
}
fn compute_plan(design: &sv_lang::Design) -> EventPlan {
let mut bodies = Vec::new();
for top in design.top_instances() {
collect_bodies(top, &mut bodies);
}
let mut refs: Vec<(String, String, bool)> = Vec::new();
let mut pooled: HashSet<String> = HashSet::new();
for body in &bodies {
let Some(def) = definition_of(body) else {
continue;
};
if needs_pool(body) {
pooled.insert(def.clone());
}
let own_path = body.hierarchical_path();
for r in hierarchical_event_refs(body) {
let Some(owner) = crate::netlist::enclosing_instance_body(&r.var) else {
continue;
};
if owner.hierarchical_path() == own_path {
continue;
}
if let Some(owner_def) = definition_of(&owner) {
refs.push((def.clone(), owner_def, r.handle));
}
}
}
let mut forced: HashSet<String> = HashSet::new();
loop {
let mut changed = false;
for (from, owner, handle) in &refs {
if *handle && forced.insert(from.clone()) {
changed = true;
}
if (*handle || pooled.contains(from) || forced.contains(from))
&& forced.insert(owner.clone())
{
changed = true;
}
}
if !changed {
break;
}
}
let group = refs.iter().any(|(_, _, handle)| *handle);
EventPlan { forced, group }
}