sv_sim_sim/state/event_pool.rs
1//! The event pool: event handles (LRM 1800-2023 15.5.2 - 15.5.5).
2//!
3//! A named event is an object; an `event` variable holds a handle to one. The
4//! handle can be re-pointed by assignment (`e1 = e2` merges: both then name
5//! the same object), cleared (`e = null`), compared (`e1 == e2`, `e == null`),
6//! passed to a task, and an automatic `event` (a local of an automatic block
7//! or task, an automatic task's formal, a local `event` array) is a new
8//! object each time it is declared.
9//!
10//! None of that fits an event being one fixed STATE cell, so an instance body
11//! that uses any of it ([`event_pool`]) keeps all its events in one hidden
12//! memory of 128-bit event cells -- the layout of an event array's rows
13//! (`state::EVENT_CELL_WIDTH`) -- addressed by an object id, the row number:
14//!
15//! - row 0 holds the dynamic allocation counter; id 0 is `null`;
16//! - rows `1..=S` are the body's static event variables (`S` =
17//! [`EventPool::static_rows`]), an array's elements consecutive;
18//! - rows `S+1..=2S` are the *handle cells* of those, row `S + id`: a static
19//! event variable's own handle, 0 until it is assigned (it names itself),
20//! then the target's id (all ones: `null`);
21//! - the rest ([`EventPool::dynamic_base`] on) are the objects the automatic
22//! events of the running processes are allocated, one per declaration
23//! executed ([`DYNAMIC_ROWS`] in all).
24//!
25//! An automatic event variable is a 64-bit object id in the process frame.
26//! A wait, a trigger, `.triggered` and `->>` all resolve the event expression
27//! to an object id and act on that row (the per-element machinery of an
28//! event array's rows).
29//!
30//! The pool's legacy per-variable cells are still laid out (so every other
31//! consumer of the signal map keeps working), but a body in pool mode reads
32//! and writes its events only through the pool: a hierarchical reference
33//! from ANOTHER instance's code to such an event does not see the pool.
34
35use sv_lang::kinds::{ExpressionKind, SymbolKind};
36use sv_lang::{Symbol, Type};
37
38use crate::symid::SymbolId;
39
40/// Rows of the pool for the automatic events allocated at run time. An
41/// allocation past them is a loud runtime error, never a reused object.
42pub const DYNAMIC_ROWS: u32 = 1 << 14;
43
44/// One static event variable (or array of them) of the pool.
45#[derive(Clone, Copy)]
46pub struct PoolStatic<'d> {
47 pub var: Symbol<'d>,
48 /// The object id of its (first) event.
49 pub first: u32,
50 /// How many events: 1, or an array's element count.
51 pub count: u32,
52}
53
54/// An instance body's event pool.
55pub struct EventPool<'d> {
56 pub statics: Vec<PoolStatic<'d>>,
57 /// `S`: the number of static event objects.
58 pub static_rows: u32,
59}
60
61impl EventPool<'_> {
62 /// Rows of the pool memory.
63 pub fn total_rows(&self) -> u32 {
64 self.dynamic_base() + DYNAMIC_ROWS
65 }
66
67 /// The first dynamic object id.
68 pub fn dynamic_base(&self) -> u32 {
69 2 * self.static_rows + 1
70 }
71}
72
73/// The signal-map key of the pool memory itself.
74pub fn pool_key() -> SymbolId {
75 SymbolId::synthetic_tagged("event_pool")
76}
77
78/// The signal-map key of the pool entry of static event variable `var`: a
79/// memory entry sharing the pool's slot, `num_rows` its event count and
80/// `lower_index` the id of its first event.
81pub fn static_key(var: &SymbolId) -> SymbolId {
82 var.derive("event_pool_static")
83}
84
85/// The signal-map key of the pool entry as seen from static event variable
86/// `var`: the pool's memory (`lower_index` = `S`, the static count) under a
87/// key unique to the variable, so another instance that names `var`
88/// hierarchically (`s.ev`) can register the pool of the instance that owns it
89/// beside [`static_key`] (the pool's own key, [`pool_key`], is the same in
90/// every body).
91pub fn pool_of_key(var: &SymbolId) -> SymbolId {
92 var.derive("event_pool_of")
93}
94
95/// The number of events `ty` holds when it is an event or a fixed-size
96/// array of events; `None` for any other type.
97pub fn event_count(ty: &Type<'_>) -> Option<u32> {
98 let mut rows: u32 = 1;
99 let mut cur = *ty;
100 while cur.is_unpacked_array() && !super::storage::is_collection(&cur) {
101 rows = rows.checked_mul(cur.fixed_unpacked_array_range().width())?;
102 cur = cur.element_type()?;
103 }
104 cur.is_event().then_some(rows)
105}
106
107/// The pool of `body`'s events, when its code needs one: it declares an
108/// event formal or an automatic event (variable or array), assigns an event
109/// or compares one.
110pub fn event_pool<'d>(body: &Symbol<'d>) -> Option<EventPool<'d>> {
111 if !needs_pool(body) {
112 return None;
113 }
114 let mut statics = Vec::new();
115 let mut next: u32 = 1;
116 for v in crate::netlist::state_items(body) {
117 if !matches!(v.kind(), SymbolKind::Variable | SymbolKind::FormalArgument) {
118 continue;
119 }
120 let Some(count) = v.value_type().as_ref().and_then(event_count) else {
121 continue;
122 };
123 statics.push(PoolStatic {
124 var: v,
125 first: next,
126 count,
127 });
128 next += count;
129 }
130 Some(EventPool {
131 statics,
132 static_rows: next - 1,
133 })
134}
135
136/// The static event variable that event expression `e` names through a
137/// hierarchical reference (`s.ev`, `s.ea[i]`) when its instance keeps its
138/// events in a pool ([`event_pool`]); `None` for anything else.
139///
140/// A pool-mode body reads and writes its events only through the pool (module
141/// docs), so another instance's trigger, `.triggered` or wait on the legacy
142/// cell would be silently lost: the callers refuse the construct by name.
143pub fn hierarchical_pool_event<'d>(e: &sv_lang::Expression<'d>) -> Option<Symbol<'d>> {
144 let mut e = *e;
145 loop {
146 if e.kind() == ExpressionKind::Conversion
147 && let Some(inner) = e.conversion_operand()
148 {
149 e = inner;
150 } else if e.kind() == ExpressionKind::ElementSelect
151 && let Some(base) = e.select_value()
152 {
153 e = base;
154 } else {
155 break;
156 }
157 }
158 if e.kind() != ExpressionKind::HierarchicalValue {
159 return None;
160 }
161 let sym = e.referenced_symbol()?;
162 if !sym.value_type().as_ref().and_then(event_count).is_some() {
163 return None;
164 }
165 let owner = crate::netlist::enclosing_instance_body(&sym)?;
166 event_pool(&owner).is_some().then_some(sym)
167}
168
169fn needs_pool(body: &Symbol<'_>) -> bool {
170 let automatic_event = |v: &Symbol<'_>| {
171 matches!(v.kind(), SymbolKind::Variable | SymbolKind::FormalArgument)
172 && v.value_type().as_ref().and_then(event_count).is_some()
173 && (v.kind() == SymbolKind::FormalArgument
174 || v.variable_lifetime() != sv_lang::VariableLifetime::Static)
175 };
176 for m in crate::netlist::module_items(body) {
177 let kind = m.kind();
178 if kind == SymbolKind::Subroutine && m.members().any(|v| automatic_event(&v)) {
179 return true;
180 }
181 if kind != SymbolKind::Subroutine && kind != SymbolKind::ProceduralBlock {
182 continue;
183 }
184 let Some(code) = crate::process::cfg::proc_body(&m) else {
185 continue;
186 };
187 let mut stack = vec![code.as_sem_node()];
188 while let Some(n) = stack.pop() {
189 if let Some(s) = n.as_statement()
190 && crate::process::cfg::block_scopes(&s)
191 .iter()
192 .any(|block| block.members().any(|v| automatic_event(&v)))
193 {
194 return true;
195 }
196 // `if (e)`, `while (e)`: an event as a condition (15.5.5.3).
197 if let Some(s) = n.as_statement()
198 && let Some(mut c) = s.condition()
199 {
200 while c.kind() == ExpressionKind::Conversion
201 && let Some(x) = c.conversion_operand()
202 {
203 c = x;
204 }
205 if c.expr_type().is_some_and(|t| t.is_event()) {
206 return true;
207 }
208 }
209 if let Some(e) = n.as_expression() {
210 let event_typed = |x: Option<sv_lang::Expression<'_>>| {
211 x.and_then(|x| x.expr_type()).is_some_and(|t| t.is_event())
212 };
213 match e.kind() {
214 ExpressionKind::Assignment if event_typed(e.left()) => return true,
215 ExpressionKind::BinaryOp if event_typed(e.left()) || event_typed(e.right()) => {
216 return true;
217 }
218 ExpressionKind::UnaryOp if event_typed(e.operand()) => return true,
219 // `if (e)`: an event in a Boolean context (15.5.5.3).
220 ExpressionKind::Conversion
221 if event_typed(e.conversion_operand()) && !event_typed(Some(e)) =>
222 {
223 return true;
224 }
225 _ => {}
226 }
227 }
228 stack.extend(crate::process::cfg::live_children(&n));
229 }
230 }
231 false
232}