vj-sim 0.1.0

Netlist, scheduler and process model of the vj SystemVerilog simulator.
Documentation
//! Task 8 (NL-076, NL-082): CDC classification โ€” turning a read edge
//! [`super::graph::partition`] deliberately left un-unioned (both sides
//! already carrying a resolved, differing clock domain) into a recorded
//! [`super::CdcEdge`], never a merge (ARCHITECTURE ยง4 item 4; Decision 5).

use std::collections::{HashMap, HashSet};

use crate::netlist::InstantiatedSignalId;

use super::graph::{DriverGraph, Partition};
use super::{CdcClassification, CdcConfidence, CdcEdge, ClockDomainId, RegionId};

/// For every read edge `partition` left un-unioned (`net`'s region !=
/// `process`'s region), records a `CdcEdge` iff BOTH regions carry a
/// resolved clock domain and those domains DIFFER (a genuine cross-domain
/// synchronizer, NL-082's `StaticallyProven`/`Async` case) โ€” a read edge
/// left un-unioned because one side has no clock domain at all can only
/// happen if `partition`'s fixed-point iteration was cut short by
/// `max_partition_merge_passes` (a documented, un-tested-this-wave
/// degenerate case); it is silently skipped here rather than misreported
/// as a proven CDC crossing. Two regions that share the SAME domain (a
/// same-clock register-to-register path, NL-076's fixture) never get an
/// edge either โ€” that is not a crossing at all.
pub(crate) fn classify_cdc_edges(graph: &DriverGraph, partitions: &[Partition]) -> Vec<CdcEdge> {
    let mut region_of: HashMap<InstantiatedSignalId, RegionId> = HashMap::new();
    let mut domain_of_region: HashMap<RegionId, Option<ClockDomainId>> = HashMap::new();
    for p in partitions {
        domain_of_region.insert(p.id, p.clock_domain);
        for &n in p.nets.iter().chain(p.processes.iter()) {
            region_of.insert(n, p.id);
        }
    }

    let mut edges = Vec::new();
    let mut seen: HashSet<(RegionId, RegionId, InstantiatedSignalId)> = HashSet::new();
    for &(net_sig, proc_sig) in graph.read_edges() {
        let (Some(&net_region), Some(&proc_region)) =
            (region_of.get(&net_sig), region_of.get(&proc_sig))
        else {
            continue;
        };
        if net_region == proc_region {
            continue;
        }
        let net_domain = domain_of_region.get(&net_region).copied().flatten();
        let proc_domain = domain_of_region.get(&proc_region).copied().flatten();
        let (Some(a), Some(b)) = (net_domain, proc_domain) else {
            continue;
        };
        if a == b {
            continue;
        }
        let key = (net_region, proc_region, net_sig);
        if seen.insert(key) {
            edges.push(CdcEdge {
                from_region: net_region,
                to_region: proc_region,
                signal: net_sig,
                classification: CdcClassification::Async,
                confidence: CdcConfidence::StaticallyProven,
            });
        }
    }
    edges
}