Skip to main content

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}