vj_sim/state/
event_plan.rs1use std::cell::RefCell;
17use std::collections::HashSet;
18use std::rc::Rc;
19
20use sv_lang::kinds::{ExpressionKind, SymbolKind};
21use sv_lang::{Expression, KnownSystemName, Symbol};
22
23use super::event_pool::{event_count, needs_pool};
24
25#[derive(Default)]
26struct EventPlan {
27 forced: HashSet<String>,
29 group: bool,
33}
34
35thread_local! {
36 static PLAN: RefCell<Rc<EventPlan>> = RefCell::new(Rc::default());
37}
38
39pub struct EventPlanScope(Rc<EventPlan>);
42
43impl EventPlanScope {
44 pub fn enter(design: &sv_lang::Design) -> Self {
45 let plan = Rc::new(compute_plan(design));
46 EventPlanScope(PLAN.with(|p| std::mem::replace(&mut *p.borrow_mut(), plan)))
47 }
48}
49
50impl Drop for EventPlanScope {
51 fn drop(&mut self) {
52 let outer = std::mem::take(&mut self.0);
53 PLAN.with(|p| *p.borrow_mut() = outer);
54 }
55}
56
57fn definition_of(body: &Symbol<'_>) -> Option<String> {
59 if body.kind() != SymbolKind::InstanceBody {
60 return None;
61 }
62 body.instance_body_parent_instance()?
63 .instance_definition()
64 .map(|d| d.name().to_string())
65}
66
67pub fn group_mode() -> bool {
73 PLAN.with(|p| p.borrow().group)
74}
75
76pub(super) fn plan_forces_pool(body: &Symbol<'_>) -> bool {
78 PLAN.with(|p| {
79 let p = p.borrow();
80 !p.forced.is_empty() && definition_of(body).is_some_and(|d| p.forced.contains(&d))
81 })
82}
83
84struct EventRef<'d> {
88 var: Symbol<'d>,
89 handle: bool,
90}
91
92fn hierarchical_event_operand<'d>(e: &Expression<'d>) -> Option<Symbol<'d>> {
95 let mut e = *e;
96 loop {
97 if e.kind() == ExpressionKind::Conversion
98 && let Some(inner) = e.conversion_operand()
99 {
100 e = inner;
101 } else if e.kind() == ExpressionKind::ElementSelect
102 && let Some(base) = e.select_value()
103 {
104 e = base;
105 } else {
106 break;
107 }
108 }
109 if e.kind() != ExpressionKind::HierarchicalValue {
110 return None;
111 }
112 let sym = e.referenced_symbol()?;
113 sym.value_type().as_ref().and_then(event_count)?;
114 Some(sym)
115}
116
117fn note<'d>(e: Option<Expression<'d>>, handle: bool, out: &mut Vec<EventRef<'d>>) {
118 if let Some(var) = e.as_ref().and_then(hierarchical_event_operand) {
119 out.push(EventRef { var, handle });
120 }
121}
122
123fn hierarchical_event_refs<'d>(body: &Symbol<'d>) -> Vec<EventRef<'d>> {
125 let mut out = Vec::new();
126 for m in crate::netlist::module_items(body) {
127 let kind = m.kind();
128 if kind == SymbolKind::Variable && m.value_type().as_ref().and_then(event_count).is_some() {
129 note(m.initializer(), true, &mut out);
130 }
131 if kind != SymbolKind::Subroutine && kind != SymbolKind::ProceduralBlock {
132 continue;
133 }
134 let Some(code) = crate::process::cfg::proc_body(&m) else {
135 continue;
136 };
137 let mut stack = vec![code.as_sem_node()];
138 while let Some(n) = stack.pop() {
139 if let Some(s) = n.as_statement() {
140 note(s.condition(), true, &mut out);
141 for block in crate::process::cfg::block_scopes(&s) {
142 for v in block.members() {
143 if v.value_type().as_ref().and_then(event_count).is_some() {
144 note(v.initializer(), true, &mut out);
145 }
146 }
147 }
148 }
149 let kids = crate::process::cfg::live_children(&n);
150 if let Some(e) = n.as_expression() {
151 note(Some(e), false, &mut out);
152 let is_triggered = e.kind() == ExpressionKind::Call
157 && e.system_subroutine()
158 .is_some_and(|s| s.known_name_id() == KnownSystemName::Triggered);
159 if !is_triggered
160 && matches!(
161 e.kind(),
162 ExpressionKind::Assignment
163 | ExpressionKind::BinaryOp
164 | ExpressionKind::UnaryOp
165 | ExpressionKind::Conversion
166 | ExpressionKind::Call
167 )
168 {
169 for k in &kids {
170 note(k.as_expression(), true, &mut out);
171 }
172 }
173 }
174 stack.extend(kids);
175 }
176 }
177 out
178}
179
180fn collect_bodies<'d>(inst: Symbol<'d>, out: &mut Vec<Symbol<'d>>) {
181 if inst.kind() == SymbolKind::InstanceArray {
182 for e in inst.instance_array_elements() {
183 collect_bodies(e, out);
184 }
185 return;
186 }
187 if inst.kind() != SymbolKind::Instance {
188 return;
189 }
190 if let Some(body) = inst.instance_body() {
191 out.push(body);
192 for m in crate::netlist::module_items(&body) {
193 collect_bodies(m, out);
194 }
195 }
196}
197
198fn compute_plan(design: &sv_lang::Design) -> EventPlan {
199 let mut bodies = Vec::new();
200 for top in design.top_instances() {
201 collect_bodies(top, &mut bodies);
202 }
203 let mut refs: Vec<(String, String, bool)> = Vec::new();
206 let mut pooled: HashSet<String> = HashSet::new();
207 for body in &bodies {
208 let Some(def) = definition_of(body) else {
209 continue;
210 };
211 if needs_pool(body) {
212 pooled.insert(def.clone());
213 }
214 let own_path = body.hierarchical_path();
215 for r in hierarchical_event_refs(body) {
216 let Some(owner) = crate::netlist::enclosing_instance_body(&r.var) else {
217 continue;
218 };
219 if owner.hierarchical_path() == own_path {
220 continue;
221 }
222 if let Some(owner_def) = definition_of(&owner) {
223 refs.push((def.clone(), owner_def, r.handle));
224 }
225 }
226 }
227 let mut forced: HashSet<String> = HashSet::new();
230 loop {
231 let mut changed = false;
232 for (from, owner, handle) in &refs {
233 if *handle && forced.insert(from.clone()) {
234 changed = true;
235 }
236 if (*handle || pooled.contains(from) || forced.contains(from))
237 && forced.insert(owner.clone())
238 {
239 changed = true;
240 }
241 }
242 if !changed {
243 break;
244 }
245 }
246 let group = refs.iter().any(|(_, _, handle)| *handle);
247 EventPlan { forced, group }
248}