Skip to main content

vj_sim/state/
force.rs

1//! `force`/`release` (LRM 1800-2023 §10.6.2): each engine's table of forced
2//! cells ([`ForceTable`], owned by its `VcState`), applied inside the one
3//! write primitive (`write::commit_write_at`) so that every writer -- a
4//! blocking or nonblocking assignment, a continuous assignment, a port
5//! connection -- is overridden the same way.
6//!
7//! A forced cell keeps its forced bits whatever is written to it; the rest
8//! of a write still lands (a `force` of a net's bit-select forces only those
9//! bits). Every write is also remembered as the cell's *driven* value, so a
10//! released net (or a variable with a continuous driver) goes back to what
11//! its drivers now say, while any other released variable keeps the value
12//! it has until its next assignment.
13//!
14//! A procedural `assign` (§10.6.1) is a second, lower layer of the same
15//! mechanism: it overrides every procedural write to the variable, and a
16//! `force` overrides it in turn (§10.6.2). Releasing a `force` while an
17//! `assign` is still active brings the assigned value back; a `deassign`
18//! under an active `force` changes nothing visible until the `release`, and
19//! the variable then keeps the value it had.
20
21use std::collections::HashMap;
22
23use super::{FourStateValue, StateCellId};
24
25/// A `force`/`assign` statement in one instance: `(instance handle, source
26/// offset)` -- two instances of a module share their statements' offsets.
27pub type Site = (u64, u64);
28
29/// The bits one `force` (or one procedural `assign`) layer holds.
30struct Layer {
31    /// Bits it holds, LSB first, one bit per value bit.
32    mask: Vec<u64>,
33    /// Its value (only its `mask` bits matter).
34    value: FourStateValue,
35    /// The statements whose drivers keep re-applying it while it holds.
36    sites: Vec<Site>,
37}
38
39impl Layer {
40    fn new(width_words: usize, value: FourStateValue) -> Self {
41        Layer {
42            mask: vec![0; width_words],
43            value,
44            sites: Vec::new(),
45        }
46    }
47
48    fn is_empty(&self) -> bool {
49        self.mask.iter().all(|&m| m == 0)
50    }
51}
52
53struct Forced {
54    /// The last value the design itself wrote.
55    driven: FourStateValue,
56    /// Released, the cell goes back to `driven` (a net, or a variable with
57    /// a continuous driver).
58    restores: bool,
59    /// A procedural `assign` (§10.6.1).
60    assign: Option<Layer>,
61    /// A `force` (§10.6.2), over the `assign`.
62    force: Option<Layer>,
63}
64
65impl Forced {
66    /// What the cell reads as: the driven value under the `assign`, under
67    /// the `force`.
68    fn visible(&self) -> FourStateValue {
69        let mut v = self.driven.clone();
70        for layer in [&self.assign, &self.force].into_iter().flatten() {
71            v = merge(&v, &layer.value, &layer.mask);
72        }
73        v
74    }
75}
76
77/// One engine's forced and procedurally assigned cells.
78#[derive(Default)]
79pub struct ForceTable {
80    forced: HashMap<StateCellId, Forced>,
81    /// Forced rows of unpacked-array cells (VPI `vpi_put_value` with
82    /// `vpiForceFlag` on an element), keyed by the memory's cell and the
83    /// row's word offset in the wide planes. Kept apart from `forced`: a
84    /// whole-memory write is a row-by-row write, each filtered by its row.
85    rows: HashMap<(StateCellId, u32), Forced>,
86    /// Statement sites currently in effect.
87    active_sites: HashMap<Site, usize>,
88    /// Set while a `force`/`assign` itself writes its cell.
89    bypass: bool,
90}
91
92impl ForceTable {
93    /// The value a write of `new` to `cell` actually stores: `new` with the
94    /// cell's assigned and forced bits, if it has any (see the module doc).
95    pub(crate) fn filter(&mut self, cell: StateCellId, new: FourStateValue) -> FourStateValue {
96        if self.bypass {
97            return new;
98        }
99        match self.forced.get_mut(&cell) {
100            Some(e) => {
101                e.driven = new;
102                e.visible()
103            }
104            None => new,
105        }
106    }
107
108    /// [`Self::filter`] for a write of the memory row at word offset `at`.
109    pub(crate) fn filter_row(
110        &mut self,
111        cell: StateCellId,
112        at: u32,
113        new: FourStateValue,
114    ) -> FourStateValue {
115        if self.bypass {
116            return new;
117        }
118        match self.rows.get_mut(&(cell, at)) {
119            Some(e) => {
120                e.driven = new;
121                e.visible()
122            }
123            None => new,
124        }
125    }
126
127    /// Whether `cell` is forced or procedurally assigned (the write path
128    /// filters exactly these cells).
129    pub(crate) fn contains(&self, cell: StateCellId) -> bool {
130        self.forced.contains_key(&cell)
131    }
132
133    /// The last value the design itself wrote to the forced `cell`
134    /// (`$countdrivers` counts what the drivers say, not the forced value).
135    pub(crate) fn driven_value(&self, cell: StateCellId) -> Option<FourStateValue> {
136        self.forced.get(&cell).map(|e| e.driven.clone())
137    }
138
139    /// Whether no cell is forced (the write path's fast check).
140    pub(crate) fn is_empty(&self) -> bool {
141        self.forced.is_empty() && self.rows.is_empty()
142    }
143
144    /// Forces (`procedural`: procedurally assigns) the `mask` bits of `cell`
145    /// to those of `value` (both full-width), on behalf of the statement at
146    /// `site`; `current` is the cell's value now. Returns the value to write,
147    /// with [`Self::set_bypass`] on.
148    #[allow(clippy::too_many_arguments)]
149    pub fn force(
150        &mut self,
151        cell: StateCellId,
152        value: FourStateValue,
153        mask: Vec<u64>,
154        restores: bool,
155        site: Site,
156        current: FourStateValue,
157        procedural: bool,
158    ) -> FourStateValue {
159        let e = self.forced.entry(cell).or_insert_with(|| Forced {
160            driven: current,
161            restores,
162            assign: None,
163            force: None,
164        });
165        apply_force(e, &mut self.active_sites, value, mask, site, procedural)
166    }
167
168    /// [`Self::force`] for the memory row of `cell` at word offset `at`.
169    #[allow(clippy::too_many_arguments)]
170    pub fn force_row(
171        &mut self,
172        cell: StateCellId,
173        at: u32,
174        value: FourStateValue,
175        mask: Vec<u64>,
176        restores: bool,
177        site: Site,
178        current: FourStateValue,
179        procedural: bool,
180    ) -> FourStateValue {
181        let e = self.rows.entry((cell, at)).or_insert_with(|| Forced {
182            driven: current,
183            restores,
184            assign: None,
185            force: None,
186        });
187        apply_force(e, &mut self.active_sites, value, mask, site, procedural)
188    }
189
190    /// Releases (`procedural`: deassigns) the `mask` bits of `cell`. Returns
191    /// the value the cell now reads as when that changes it: a released net
192    /// (or continuously driven variable) goes back to its driven value, and
193    /// a variable whose `force` ends while an `assign` still holds takes the
194    /// assigned value. `None` otherwise: a released or deassigned variable
195    /// keeps its value, and a cell with nothing to release is untouched.
196    pub fn release(
197        &mut self,
198        cell: StateCellId,
199        mask: &[u64],
200        procedural: bool,
201    ) -> Option<FourStateValue> {
202        let e = self.forced.get_mut(&cell)?;
203        let out = apply_release(e, &mut self.active_sites, mask, procedural);
204        if e.assign.is_none() && e.force.is_none() {
205            self.forced.remove(&cell);
206        }
207        out
208    }
209
210    /// [`Self::release`] for the memory row of `cell` at word offset `at`.
211    pub fn release_row(
212        &mut self,
213        cell: StateCellId,
214        at: u32,
215        mask: &[u64],
216        procedural: bool,
217    ) -> Option<FourStateValue> {
218        let e = self.rows.get_mut(&(cell, at))?;
219        let out = apply_release(e, &mut self.active_sites, mask, procedural);
220        if e.assign.is_none() && e.force.is_none() {
221            self.rows.remove(&(cell, at));
222        }
223        out
224    }
225
226    /// Whether the `force` statement at `site` is still in effect (its
227    /// driver re-forces the target when an operand changes).
228    pub fn site_active(&self, site: Site) -> bool {
229        self.active_sites.contains_key(&site)
230    }
231
232    /// Turns forcing off (a `force` writing its own cell) or back on.
233    pub fn set_bypass(&mut self, on: bool) {
234        self.bypass = on;
235    }
236}
237
238/// Adds a `force` (`procedural`: `assign`) layer of `mask` bits of `value` to
239/// `e`; returns what the entry now reads as.
240fn apply_force(
241    e: &mut Forced,
242    active_sites: &mut HashMap<Site, usize>,
243    value: FourStateValue,
244    mask: Vec<u64>,
245    site: Site,
246    procedural: bool,
247) -> FourStateValue {
248    let slot = if procedural {
249        &mut e.assign
250    } else {
251        &mut e.force
252    };
253    let layer = slot.get_or_insert_with(|| Layer::new(mask.len(), value.clone()));
254    layer.value = merge(&layer.value, &value, &mask);
255    for (m, n) in layer.mask.iter_mut().zip(&mask) {
256        *m |= n;
257    }
258    if !layer.sites.contains(&site) {
259        layer.sites.push(site);
260        *active_sites.entry(site).or_insert(0) += 1;
261    }
262    e.visible()
263}
264
265/// Releases the `mask` bits of `e`'s `force` (`procedural`: `assign`) layer;
266/// the value it now reads as when that changed it (see
267/// [`ForceTable::release`]).
268fn apply_release(
269    e: &mut Forced,
270    active_sites: &mut HashMap<Site, usize>,
271    mask: &[u64],
272    procedural: bool,
273) -> Option<FourStateValue> {
274    let before = e.visible();
275    let slot = if procedural {
276        &mut e.assign
277    } else {
278        &mut e.force
279    };
280    let layer = slot.as_mut()?;
281    for (m, r) in layer.mask.iter_mut().zip(mask) {
282        *m &= !r;
283    }
284    let mut freed = Vec::new();
285    if layer.is_empty() {
286        freed = std::mem::take(&mut layer.sites);
287        *slot = None;
288    }
289    for site in freed {
290        if let Some(n) = active_sites.get_mut(&site) {
291            *n -= 1;
292            if *n == 0 {
293                active_sites.remove(&site);
294            }
295        }
296    }
297    // A variable that nothing drives continuously keeps what it reads as
298    // now for the bits no layer holds any more (until its next write).
299    if !e.restores {
300        e.driven = before.clone();
301    }
302    let after = e.visible();
303    (!same(&after, &before)).then_some(after)
304}
305
306/// Whether two values hold the same bits.
307fn same(a: &FourStateValue, b: &FourStateValue) -> bool {
308    match (a, b) {
309        (FourStateValue::Narrow(x), FourStateValue::Narrow(y)) => x == y,
310        (
311            FourStateValue::Wide { aval: xa, bval: xb },
312            FourStateValue::Wide { aval: ya, bval: yb },
313        ) => xa == ya && xb == yb,
314        _ => false,
315    }
316}
317
318/// `base` with the bits set in `mask` taken from `forced`.
319fn merge(base: &FourStateValue, forced: &FourStateValue, mask: &[u64]) -> FourStateValue {
320    match (base, forced) {
321        (FourStateValue::Narrow(b), FourStateValue::Narrow(v)) => {
322            let m = u128::from(mask.first().copied().unwrap_or(0));
323            let m = m | (m << 64);
324            FourStateValue::Narrow((b & !m) | (v & m))
325        }
326        (
327            FourStateValue::Wide { aval: ba, bval: bb },
328            FourStateValue::Wide { aval: fa, bval: fb },
329        ) => {
330            let pick = |b: &[u64], f: &[u64]| -> Vec<u64> {
331                b.iter()
332                    .enumerate()
333                    .map(|(i, &w)| {
334                        let m = mask.get(i).copied().unwrap_or(0);
335                        (w & !m) | (f.get(i).copied().unwrap_or(0) & m)
336                    })
337                    .collect()
338            };
339            FourStateValue::Wide {
340                aval: pick(ba, fa),
341                bval: pick(bb, fb),
342            }
343        }
344        _ => base.clone(),
345    }
346}
347
348#[cfg(test)]
349mod tests {
350    use super::*;
351
352    #[test]
353    fn forced_bits_win_and_a_released_net_returns_to_its_driven_value() {
354        let mut t = ForceTable::default();
355        let cell = StateCellId(3);
356        let now = FourStateValue::Narrow(0x5);
357        let written = t.force(
358            cell,
359            FourStateValue::Narrow(0xA0),
360            vec![0xF0],
361            true,
362            (9, 7),
363            now,
364            false,
365        );
366        assert!(matches!(written, FourStateValue::Narrow(0xA5)));
367        assert!(t.site_active((9, 7)));
368        assert!(
369            !t.site_active((8, 7)),
370            "another instance's statement is not in effect"
371        );
372        // A driver writes 0x33: the low nibble lands, the high stays forced.
373        assert!(matches!(
374            t.filter(cell, FourStateValue::Narrow(0x33)),
375            FourStateValue::Narrow(0xA3)
376        ));
377        assert!(matches!(
378            t.release(cell, &[0xF0], false),
379            Some(FourStateValue::Narrow(0x33))
380        ));
381        assert!(!t.site_active((9, 7)));
382        assert!(matches!(
383            t.filter(cell, FourStateValue::Narrow(0x44)),
384            FourStateValue::Narrow(0x44)
385        ));
386    }
387
388    #[test]
389    fn a_released_variable_keeps_its_value() {
390        let mut t = ForceTable::default();
391        let cell = StateCellId(4);
392        t.force(
393            cell,
394            FourStateValue::Narrow(9),
395            vec![0xF],
396            false,
397            (1, 1),
398            FourStateValue::Narrow(2),
399            false,
400        );
401        assert!(matches!(
402            t.filter(cell, FourStateValue::Narrow(7)),
403            FourStateValue::Narrow(9)
404        ));
405        assert!(t.release(cell, &[0xF], false).is_none());
406        // It keeps 9 until its next write.
407        assert!(matches!(
408            t.filter(cell, FourStateValue::Narrow(7)),
409            FourStateValue::Narrow(7)
410        ));
411    }
412
413    /// §10.6.2: `force` overrides a procedural `assign`; releasing it brings
414    /// the assigned value back, and a `deassign` under the `force` changes
415    /// nothing visible until the `release`, after which the variable keeps
416    /// the value it had.
417    #[test]
418    fn force_overrides_assign_and_release_restores_it() {
419        let mut t = ForceTable::default();
420        let cell = StateCellId(5);
421        let n = |v: u128| FourStateValue::Narrow(v);
422        let assign = (1, 10);
423        let force = (1, 20);
424        assert!(matches!(
425            t.force(cell, n(3), vec![0xF], false, assign, n(1), true),
426            FourStateValue::Narrow(3)
427        ));
428        assert!(
429            matches!(t.filter(cell, n(8)), FourStateValue::Narrow(3)),
430            "assign beats writes"
431        );
432        assert!(matches!(
433            t.force(cell, n(5), vec![0xF], false, force, n(3), false),
434            FourStateValue::Narrow(5)
435        ));
436        // The assign's driver re-applies a new value: still under the force.
437        assert!(matches!(
438            t.force(cell, n(6), vec![0xF], false, assign, n(5), true),
439            FourStateValue::Narrow(5)
440        ));
441        assert!(
442            matches!(
443                t.release(cell, &[0xF], false),
444                Some(FourStateValue::Narrow(6))
445            ),
446            "the assign shows through"
447        );
448        assert!(t.site_active(assign) && !t.site_active(force));
449        // Force again, then deassign under it: nothing visible changes.
450        t.force(cell, n(9), vec![0xF], false, force, n(6), false);
451        assert!(t.release(cell, &[0xF], true).is_none());
452        assert!(t.release(cell, &[0xF], false).is_none(), "keeps 9");
453        assert!(
454            matches!(t.filter(cell, n(2)), FourStateValue::Narrow(2)),
455            "nothing holds it now"
456        );
457    }
458}