Skip to main content

vj_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 peer `j`'s pool (group mode, see
79/// `event_plan::group_mode`): the pool memory of the `j`-th pool-mode instance,
80/// as that instance's tag `j + 1` names it. Registered in EVERY pool-mode
81/// instance (its own pool under its own tag too).
82pub fn peer_key(j: u32) -> SymbolId {
83    SymbolId::synthetic_tagged(&format!("event_pool_peer{j}"))
84}
85
86/// The signal-map key of the pool entry of static event variable `var`: a
87/// memory entry sharing the pool's slot, `num_rows` its event count and
88/// `lower_index` the id of its first event.
89pub fn static_key(var: &SymbolId) -> SymbolId {
90    var.derive("event_pool_static")
91}
92
93/// The signal-map key of the pool entry as seen from static event variable
94/// `var`: the pool's memory (`lower_index` = `S`, the static count) under a
95/// key unique to the variable, so another instance that names `var`
96/// hierarchically (`s.ev`) can register the pool of the instance that owns it
97/// beside [`static_key`] (the pool's own key, [`pool_key`], is the same in
98/// every body).
99pub fn pool_of_key(var: &SymbolId) -> SymbolId {
100    var.derive("event_pool_of")
101}
102
103/// The number of events `ty` holds when it is an event or a fixed-size
104/// array of events; `None` for any other type.
105pub fn event_count(ty: &Type<'_>) -> Option<u32> {
106    let mut rows: u32 = 1;
107    let mut cur = *ty;
108    while cur.is_unpacked_array() && !super::storage::is_collection(&cur) {
109        rows = rows.checked_mul(cur.fixed_unpacked_array_range().width())?;
110        cur = cur.element_type()?;
111    }
112    cur.is_event().then_some(rows)
113}
114
115/// The pool of `body`'s events, when its code needs one: it declares an
116/// event formal or an automatic event (variable or array), assigns an event
117/// or compares one.
118pub fn event_pool<'d>(body: &Symbol<'d>) -> Option<EventPool<'d>> {
119    if !needs_pool(body) && !super::event_plan::plan_forces_pool(body) {
120        return None;
121    }
122    let mut statics = Vec::new();
123    let mut next: u32 = 1;
124    for v in crate::netlist::state_items(body) {
125        if !matches!(v.kind(), SymbolKind::Variable | SymbolKind::FormalArgument) {
126            continue;
127        }
128        let Some(count) = v.value_type().as_ref().and_then(event_count) else {
129            continue;
130        };
131        statics.push(PoolStatic {
132            var: v,
133            first: next,
134            count,
135        });
136        next += count;
137    }
138    Some(EventPool {
139        statics,
140        static_rows: next - 1,
141    })
142}
143
144/// The static event variable that event expression `e` names through a
145/// hierarchical reference (`s.ev`, `s.ea[i]`) when its instance keeps its
146/// events in a pool ([`event_pool`]); `None` for anything else.
147///
148/// A pool-mode body reads and writes its events only through the pool (module
149/// docs), so another instance's trigger, `.triggered` or wait on the legacy
150/// cell would be silently lost: the callers refuse the construct by name.
151pub fn hierarchical_pool_event<'d>(e: &sv_lang::Expression<'d>) -> Option<Symbol<'d>> {
152    let mut e = *e;
153    loop {
154        if e.kind() == ExpressionKind::Conversion
155            && let Some(inner) = e.conversion_operand()
156        {
157            e = inner;
158        } else if e.kind() == ExpressionKind::ElementSelect
159            && let Some(base) = e.select_value()
160        {
161            e = base;
162        } else {
163            break;
164        }
165    }
166    if e.kind() != ExpressionKind::HierarchicalValue {
167        return None;
168    }
169    let sym = e.referenced_symbol()?;
170    if !sym.value_type().as_ref().and_then(event_count).is_some() {
171        return None;
172    }
173    let owner = crate::netlist::enclosing_instance_body(&sym)?;
174    event_pool(&owner).is_some().then_some(sym)
175}
176
177pub(super) fn needs_pool(body: &Symbol<'_>) -> bool {
178    let automatic_event = |v: &Symbol<'_>| {
179        matches!(v.kind(), SymbolKind::Variable | SymbolKind::FormalArgument)
180            && v.value_type().as_ref().and_then(event_count).is_some()
181            && (v.kind() == SymbolKind::FormalArgument
182                || v.variable_lifetime() != sv_lang::VariableLifetime::Static)
183    };
184    for m in crate::netlist::module_items(body) {
185        let kind = m.kind();
186        if kind == SymbolKind::Subroutine && m.members().any(|v| automatic_event(&v)) {
187            return true;
188        }
189        if kind != SymbolKind::Subroutine && kind != SymbolKind::ProceduralBlock {
190            continue;
191        }
192        let Some(code) = crate::process::cfg::proc_body(&m) else {
193            continue;
194        };
195        let mut stack = vec![code.as_sem_node()];
196        while let Some(n) = stack.pop() {
197            if let Some(s) = n.as_statement()
198                && crate::process::cfg::block_scopes(&s)
199                    .iter()
200                    .any(|block| block.members().any(|v| automatic_event(&v)))
201            {
202                return true;
203            }
204            // `if (e)`, `while (e)`: an event as a condition (15.5.5.3).
205            if let Some(s) = n.as_statement()
206                && let Some(mut c) = s.condition()
207            {
208                while c.kind() == ExpressionKind::Conversion
209                    && let Some(x) = c.conversion_operand()
210                {
211                    c = x;
212                }
213                if c.expr_type().is_some_and(|t| t.is_event()) {
214                    return true;
215                }
216            }
217            if let Some(e) = n.as_expression() {
218                let event_typed = |x: Option<sv_lang::Expression<'_>>| {
219                    x.and_then(|x| x.expr_type()).is_some_and(|t| t.is_event())
220                };
221                match e.kind() {
222                    ExpressionKind::Assignment if event_typed(e.left()) => return true,
223                    ExpressionKind::BinaryOp if event_typed(e.left()) || event_typed(e.right()) => {
224                        return true;
225                    }
226                    ExpressionKind::UnaryOp if event_typed(e.operand()) => return true,
227                    // `if (e)`: an event in a Boolean context (15.5.5.3).
228                    ExpressionKind::Conversion
229                        if event_typed(e.conversion_operand()) && !event_typed(Some(e)) =>
230                    {
231                        return true;
232                    }
233                    _ => {}
234                }
235            }
236            stack.extend(crate::process::cfg::live_children(&n));
237        }
238    }
239    false
240}