Skip to main content

vj_sim/
symid.rs

1//! A symbol's identity as a map key.
2//!
3//! `sv_lang::Symbol::id()` is the symbol's hierarchical path, which is not
4//! unique: an unnamed `begin ... end` block (and the implicit block of a
5//! `for`/`foreach`) contributes nothing to a path, so a process local `k`
6//! has the same `sv_lang::SymbolId` (`top.k`) as a module variable `k`, and
7//! two `for (int i ...)` loops' `i`s -- or a `foreach` index and a later
8//! `for` index -- collide. [`SymbolId`] adds each ancestor's position in its
9//! parent scope, which tells them apart; every map in this workspace keyed
10//! by a symbol uses it, through [`SymbolUid::uid`].
11//!
12//! The key type itself is slang-free (and serializable, for the design
13//! image's VPI section); only [`SymbolUid`] needs the frontend.
14
15use serde::{Deserialize, Serialize};
16#[cfg(feature = "frontend")]
17use sv_lang::Symbol;
18
19/// A unique, owned symbol key: the hierarchical path (for display and
20/// ordering) plus the chain of member indices from the root down to the
21/// symbol (for uniqueness).
22#[derive(Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug, Serialize, Deserialize)]
23pub struct SymbolId {
24    path: String,
25    chain: Vec<u32>,
26}
27
28impl SymbolId {
29    /// A key that names no real symbol, for unit tests of code that only needs keys to
30    /// compare (it equals no key [`SymbolUid::uid`] produces).
31    pub fn synthetic(path: &str) -> SymbolId {
32        SymbolId {
33            path: path.to_string(),
34            chain: vec![u32::MAX],
35        }
36    }
37
38    /// The hierarchical path (not unique by itself -- see the module doc).
39    pub fn path(&self) -> &str {
40        &self.path
41    }
42
43    /// A key that names no symbol, for a value the simulator itself keeps in a
44    /// symbol-keyed table (a procedural assertion's `const'(expr)` capture,
45    /// `vj-codegen::sva::proc`). It never equals a real symbol's key: the
46    /// member chain is the impossible `[u32::MAX]`.
47    pub fn synthetic_tagged(tag: &str) -> SymbolId {
48        SymbolId {
49            path: format!("${tag}"),
50            chain: vec![u32::MAX],
51        }
52    }
53
54    /// A key for storage the simulator itself adds next to this symbol (a
55    /// sampled-value history cell, `crate::sva::sampled`): this symbol's
56    /// path plus `${tag}`, with the same member chain. Two symbols never
57    /// collide (their chains differ), nor do one symbol's distinct tags
58    /// (their paths differ); a derived key equals a real symbol's only if
59    /// that symbol is this one and is literally named `{name}${tag}`.
60    pub fn derive(&self, tag: &str) -> SymbolId {
61        SymbolId {
62            path: format!("{}${tag}", self.path),
63            chain: self.chain.clone(),
64        }
65    }
66
67    /// The path and member chain as one string: a unique, deterministic
68    /// text form, for building a [`Self::derive`] tag from another symbol.
69    pub fn key_string(&self) -> String {
70        let chain: Vec<String> = self.chain.iter().map(u32::to_string).collect();
71        format!("{}#{}", self.path, chain.join("."))
72    }
73}
74
75impl core::fmt::Display for SymbolId {
76    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
77        f.write_str(&self.path)
78    }
79}
80
81#[cfg(feature = "frontend")]
82/// Whether an expression of `kind` names a value: a plain name (`x`) or a
83/// hierarchical one (`b.valid`, `top.u.r`) -- either way
84/// `referenced_symbol()` is the variable/net itself (a modport port for an
85/// access through a modport), and the two lower alike.
86pub fn is_value_name(kind: sv_lang_kinds::ExpressionKind) -> bool {
87    matches!(
88        kind,
89        sv_lang_kinds::ExpressionKind::NamedValue
90            | sv_lang_kinds::ExpressionKind::HierarchicalValue
91            // A variable named where a system task's signature takes a scope
92            // or object (`$dumpports(top, fname)`'s file name).
93            | sv_lang_kinds::ExpressionKind::ArbitrarySymbol
94    )
95}
96
97#[cfg(feature = "frontend")]
98/// [`SymbolId`] for a symbol.
99pub trait SymbolUid {
100    /// This symbol's unique key.
101    fn uid(&self) -> SymbolId;
102}
103
104#[cfg(feature = "frontend")]
105type UidMemo = std::collections::HashMap<(usize, usize), SymbolId>;
106
107#[cfg(feature = "frontend")]
108thread_local! {
109    /// While a design lowers ([`UidMemoGuard`]): [`SymbolUid::uid`] by symbol
110    /// identity. `uid()` builds a hierarchical path and walks the parent
111    /// chain over FFI per call; lowering asks for the same symbols' keys
112    /// many thousands of times (`import uvm_pkg::*`).
113    static UID_MEMO: std::cell::RefCell<Option<UidMemo>> = const { std::cell::RefCell::new(None) };
114}
115
116/// Enables the [`SymbolUid::uid`] memo on this thread for one design
117/// lowering; symbols are only stable while the design lives, so the memo is
118/// dropped with the guard.
119#[cfg(feature = "frontend")]
120pub struct UidMemoGuard;
121
122#[cfg(feature = "frontend")]
123impl UidMemoGuard {
124    /// Starts an empty memo.
125    pub fn enter() -> Self {
126        UID_MEMO.with(|m| *m.borrow_mut() = Some(UidMemo::new()));
127        UidMemoGuard
128    }
129}
130
131#[cfg(feature = "frontend")]
132impl Drop for UidMemoGuard {
133    fn drop(&mut self) {
134        UID_MEMO.with(|m| *m.borrow_mut() = None);
135    }
136}
137
138/// `sym`'s identity: the `ptr` and `compilation` words that open
139/// `sv_lang::Symbol`'s `#[repr(C)]` handle (equality there is those two).
140#[cfg(feature = "frontend")]
141pub fn symbol_identity(sym: &Symbol<'_>) -> (usize, usize) {
142    const _: () =
143        assert!(std::mem::size_of::<Symbol<'static>>() >= 2 * std::mem::size_of::<usize>());
144    // SAFETY: `Symbol` is a `Copy` handle whose first two words are the
145    // pointers `PartialEq` compares; the read is in bounds (asserted above).
146    unsafe {
147        let p = sym as *const Symbol<'_> as *const usize;
148        (*p, *p.add(1))
149    }
150}
151
152#[cfg(feature = "frontend")]
153impl SymbolUid for Symbol<'_> {
154    fn uid(&self) -> SymbolId {
155        let key = symbol_identity(self);
156        if let Some(hit) = UID_MEMO.with(|m| m.borrow().as_ref().and_then(|m| m.get(&key).cloned()))
157        {
158            return hit;
159        }
160        let id = self.uid_uncached();
161        UID_MEMO.with(|m| {
162            if let Some(m) = m.borrow_mut().as_mut() {
163                m.insert(key, id.clone());
164            }
165        });
166        id
167    }
168}
169
170#[cfg(feature = "frontend")]
171trait UidUncached {
172    fn uid_uncached(&self) -> SymbolId;
173}
174
175#[cfg(feature = "frontend")]
176impl UidUncached for Symbol<'_> {
177    fn uid_uncached(&self) -> SymbolId {
178        let mut chain = Vec::new();
179        let mut cur = Some(*self);
180        while let Some(s) = cur {
181            let parent = s.parent();
182            if parent.is_none() {
183                break;
184            }
185            chain.push(s.lookup_location_before().index());
186            cur = parent;
187        }
188        chain.reverse();
189        SymbolId {
190            path: self.hierarchical_path(),
191            chain,
192        }
193    }
194}