vj-sim 0.3.0

Netlist, scheduler and process model of the vj SystemVerilog simulator.
Documentation
//! Virtual interfaces (LRM 1800-2023 ยง25.9).
//!
//! A virtual interface value is an instance id: 1 + the instance's position
//! among every instance of its interface's definition in the design
//! ([`interface_instances`], hierarchy order), 0 for `null`. `vif.m`
//! names member `m` of whichever of those instances the id selects
//! ([`member_targets`] gives every candidate's `m`, in id order).

use sv_lang::{Expression, Symbol};
use sv_lang_kinds::{ExpressionKind, SymbolKind};

/// Every instance of the interface definition named `definition` in
/// `any`'s design, in hierarchy order (depth first, declaration order,
/// array elements in order).
pub fn interface_instances<'d>(any: &Symbol<'d>, definition: &str) -> Vec<Symbol<'d>> {
    fn visit<'d>(inst: Symbol<'d>, definition: &str, out: &mut Vec<Symbol<'d>>) {
        if inst.kind() == SymbolKind::InstanceArray {
            for e in inst.instance_array_elements() {
                visit(e, definition, out);
            }
            return;
        }
        if inst.kind() != SymbolKind::Instance {
            return;
        }
        if inst
            .instance_definition()
            .is_some_and(|d| d.name() == definition)
        {
            out.push(inst);
        }
        if let Some(body) = inst.instance_body() {
            for m in super::module_items(&body) {
                visit(m, definition, out);
            }
        }
    }
    let mut root = *any;
    while let Some(p) = root.parent() {
        root = p;
    }
    let mut out = Vec::new();
    for top in root.members() {
        visit(top, definition, &mut out);
    }
    out
}

/// The virtual-interface value of interface instance `inst` (see the module
/// doc); `None` if it is not an instance of a definition.
pub fn instance_id(inst: &Symbol<'_>) -> Option<u64> {
    let def = inst.instance_definition()?;
    let pos = interface_instances(inst, def.name())
        .iter()
        .position(|c| c.hierarchical_path() == inst.hierarchical_path())?;
    Some(pos as u64 + 1)
}

/// The interface definition a virtual interface's member `member` (its
/// canonical symbol, as `vif.m` names it) belongs to.
fn definition_of_member(member: &Symbol<'_>) -> Option<String> {
    let mut cur = member.parent();
    while let Some(s) = cur {
        if s.kind() == SymbolKind::InstanceBody {
            return Some(s.name().to_string());
        }
        cur = s.parent();
    }
    None
}

/// Whether `e` is `vif.m`: a member access through a virtual interface.
pub fn is_member_access(e: &Expression<'_>) -> bool {
    e.kind() == ExpressionKind::MemberAccess
        && e.operand()
            .or_else(|| e.select_value())
            .and_then(|b| b.expr_type())
            .is_some_and(|t| t.is_virtual_interface())
}

/// The virtual interface `vif.m` reads (`e`'s operand).
pub fn base<'d>(e: &Expression<'d>) -> Option<Expression<'d>> {
    e.operand().or_else(|| e.select_value())
}

/// For `vif.m` (`e`): member `m` of every candidate instance, in id order
/// (`None` for a candidate lacking it). `None` if `e` is not such an access.
pub fn member_targets<'d>(e: &Expression<'d>) -> Option<Vec<Option<Symbol<'d>>>> {
    if !is_member_access(e) {
        return None;
    }
    let member = e.referenced_symbol()?;
    let def = definition_of_member(&member)?;
    // The member's names below the interface body (`cb`, `d` for a
    // clocking block's `cb.d`; `mp`, `d` through a modport).
    let mut path = Vec::new();
    let mut cur = Some(member);
    while let Some(s) = cur {
        if s.kind() == SymbolKind::InstanceBody {
            break;
        }
        path.push(s.name().to_string());
        cur = s.parent();
    }
    path.reverse();
    Some(
        interface_instances(&member, &def)
            .into_iter()
            .map(|inst| {
                let mut at = inst.instance_body()?;
                for name in &path {
                    at = at.find(name)?;
                }
                Some(at)
            })
            .map(|m| {
                // A modport port names the interface's own member.
                m.map(|s| {
                    if s.kind() == SymbolKind::ModportPort {
                        s.modport_port_internal_symbol().unwrap_or(s)
                    } else {
                        s
                    }
                })
            })
            .collect(),
    )
}

/// For a call through a virtual interface (`vif.t(...)`, `e`): the
/// virtual interface (the call's first operand) and every candidate
/// instance's own `t`, in id order. `None` if `e` is not such a call.
pub fn call_targets<'d>(e: &Expression<'d>) -> Option<(Expression<'d>, Vec<Option<Symbol<'d>>>)> {
    if e.kind() != ExpressionKind::Call || e.is_system_call() == Some(true) {
        return None;
    }
    let callee = e.call_subroutine()?;
    let operands = e.operands();
    let vif = *operands.first()?;
    if !vif.expr_type().is_some_and(|t| t.is_virtual_interface())
        || callee.subroutine_arguments().count() + 1 != operands.len()
    {
        return None;
    }
    let def = definition_of_member(&callee)?;
    Some((
        vif,
        interface_instances(&callee, &def)
            .into_iter()
            .map(|inst| inst.instance_body().and_then(|b| b.find(callee.name())))
            .collect(),
    ))
}