vj-sim 0.3.0

Netlist, scheduler and process model of the vj SystemVerilog simulator.
Documentation
//! `$countdrivers` data (IEEE 1800-2023 D.2, Table D.1): per net bit, how
//! many drivers are at 0, 1 and x (a driver at `z` is not counted). A net
//! resolver records them next to its `%v` strengths (`vj_exec::nets`)
//! under the net's strength key; a net no resolver runs for has at most one
//! driver, so `sv_rt_net_drivers` reads its count off the net itself.

use std::cell::RefCell;
use std::collections::HashMap;

/// A bit's driver counts: at 0, at 1, at x.
pub type BitCounts = [u32; 3];

/// The counts a net no `$countdrivers` meaning exists for records (a net in
/// a bidirectional switch network): the call stops the run by name.
pub const UNCOUNTED: BitCounts = [u32::MAX; 3];

thread_local! {
    /// Each net's per-bit counts (bit 0 first), by strength key. Per thread
    /// like the runtime's other run state; emptied by
    /// [`reset_driver_counts_for_new_run`].
    static COUNTS: RefCell<HashMap<u64, Vec<BitCounts>>> = RefCell::default();
    /// Each dumped port's per-bit fixture/DUT split, by side key.
    static SIDES: RefCell<HashMap<u64, Vec<SideBit>>> = RefCell::default();
}

/// One bit of a net as the test fixture's drivers and the DUT's own drivers
/// resolve it separately (`$dumpports`, LRM 21.7.4.3.2). Index 0 is the
/// fixture side (drivers outside the dumped instance), index 1 the DUT side.
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub struct SideBit {
    /// The side's resolved possible states in the resolver's `Points`
    /// encoding: bit 0 is `z`, bit `l` (1..=7) is 0 at strength level `l`,
    /// bit `8 + l` is 1 at level `l`.
    pub points: [u16; 2],
    /// How many of the side's drivers are not at `z` on this bit.
    pub active: [u8; 2],
    /// Whether the side has any driver at all (connected).
    pub present: [bool; 2],
}

/// Forgets every net's counts (the start of a run).
pub fn reset_driver_counts_for_new_run() {
    COUNTS.with(|c| c.borrow_mut().clear());
    SIDES.with(|c| c.borrow_mut().clear());
}

/// Records the counts of net `key`.
pub fn set_driver_counts(key: u64, counts: &[BitCounts]) {
    COUNTS.with(|c| {
        let mut m = c.borrow_mut();
        match m.get_mut(&key) {
            Some(v) if v.as_slice() == counts => {}
            Some(v) => *v = counts.to_vec(),
            None => {
                m.insert(key, counts.to_vec());
            }
        }
    });
}

/// The recorded counts of bit `bit` of net `key`.
pub fn driver_counts_of(key: u64, bit: u32) -> Option<BitCounts> {
    COUNTS.with(|c| {
        c.borrow()
            .get(&key)
            .and_then(|v| v.get(bit as usize).copied())
    })
}

/// Records the fixture/DUT split of the port whose side key is `key`.
pub fn set_sides(key: u64, bits: &[SideBit]) {
    SIDES.with(|c| {
        c.borrow_mut().insert(key, bits.to_vec());
    });
}

/// The recorded split of bit `bit` of the port with side key `key`.
pub fn sides_of(key: u64, bit: u32) -> Option<SideBit> {
    SIDES.with(|c| {
        c.borrow()
            .get(&key)
            .and_then(|v| v.get(bit as usize).copied())
    })
}