vj 0.2.0

vj: a SystemVerilog (IEEE 1800-2023) simulator built on slang and LLVM.
//! User-defined nettypes (LRM 1800-2023 6.6.7): finding which nets of a design
//! are declared with one, and with which declaration.
//!
//! A net `wT w;` is parsed by slang as a data declaration (the name `wT` is
//! only known to be a nettype once looked up), so a net symbol carries no link
//! to its nettype. The nettype name is read back from the declaration's
//! syntax -- the data declaration of a body net, the port header of a port's
//! internal net -- and looked up in the net's own scope (an import or a
//! `$unit` declaration both resolve). An alias (`nettype wT wT2;`, 6.6.7)
//! names the nettype it renames (6.22.6): the chain is followed to the
//! declaration that holds the data type and the resolution function, and that
//! declaration is the nettype's identity.

use std::collections::HashMap;

use sv_lang::kinds::SymbolKind;
use sv_lang::nodes::{
    DataDeclarationSyntax, ImplicitAnsiPortSyntax, NetDeclarationSyntax, NetTypeDeclarationSyntax,
    PortDeclarationSyntax, PortHeaderSyntax,
};
use sv_lang::{AstNode, EvalSession, Symbol, SyntaxTree};
use vj_codegen::process_lower::SignalSlot;
use vj_sim::netlist::InstanceId;
use vj_sim::symid::{SymbolId, SymbolUid};

use crate::nets::NetId;

/// One user-defined nettype declaration, as the nets using it see it.
pub(crate) struct UserNetType<'d> {
    /// The nettype's name.
    pub name: String,
    /// Its resolution function (`with fn`), if any.
    pub func: Option<Symbol<'d>>,
    /// The data type's default value, LSB first (6.7.3: a net starts at it).
    pub init: Vec<sv_lang::Bit>,
    /// The data type is an unpacked array (`typedef real TR[5]`): `init` is one
    /// leaf element's default, and a net is a memory of the elements.
    pub data_array: bool,
}

/// The user-nettype nets of a design.
#[derive(Default)]
pub(crate) struct UserNets<'d> {
    /// Each such net's nettype, an index into `types`.
    pub by_net: HashMap<NetId, usize>,
    pub types: Vec<UserNetType<'d>>,
}

/// The name a net's declaration gives as its type, when that is a plain or
/// scope-qualified identifier (a candidate nettype name).
///
/// Also the number of unpacked dimensions the declarator itself adds: a net
/// `realNet n[0:1]` is an array of two nets of nettype `realNet` (6.6.7), not
/// one net whose data type is an array.
pub(crate) fn declared_type_name(
    net: &Symbol<'_>,
    trees: &[SyntaxTree],
) -> Option<(String, usize)> {
    for tree in trees {
        let Some(mut node) = net.syntax(tree) else {
            continue;
        };
        loop {
            // A net declared with a net-type keyword is never a user nettype
            // (`wire`, `interconnect`, ...).
            if NetDeclarationSyntax::cast(node).is_some() {
                return None;
            }
            let name = net.name();
            let (ty, dims) = if let Some(d) = DataDeclarationSyntax::cast(node) {
                let dims = d
                    .declarators()
                    .iter()
                    .find(|x| x.name().is_some_and(|t| t.text() == name))
                    .map_or(0, |x| x.dimensions().len());
                (Some(without_comments(&d.r#type().syntax().text())), dims)
            } else if let Some(p) = ImplicitAnsiPortSyntax::cast(node) {
                (header_type(p.header()), p.declarator().dimensions().len())
            } else if let Some(p) = PortDeclarationSyntax::cast(node) {
                let dims = p
                    .declarators()
                    .iter()
                    .find(|x| x.name().is_some_and(|t| t.text() == name))
                    .map_or(0, |x| x.dimensions().len());
                (header_type(p.header()), dims)
            } else {
                node = node.parent()?;
                continue;
            };
            let text = ty?.trim().to_string();
            let ident = !text.is_empty()
                && text
                    .chars()
                    .all(|c| c.is_ascii_alphanumeric() || matches!(c, '_' | '$' | ':'))
                && text.chars().next().is_some_and(|c| !c.is_ascii_digit());
            return ident.then_some((text, dims));
        }
    }
    None
}

/// `text` with its `//` and `/* */` comments removed (LRM 5.4). A syntax
/// node's text carries its leading trivia: the comment that closes the
/// previous line (`wTsum a; // note`) would otherwise sit in front of the
/// nettype name of the next declaration and hide that it is one.
fn without_comments(text: &str) -> String {
    let mut out = String::with_capacity(text.len());
    let mut rest = text;
    while let Some(i) = rest.find("/") {
        out.push_str(&rest[..i]);
        let tail = &rest[i..];
        if tail.starts_with("//") {
            rest = tail.find('\n').map_or("", |n| &tail[n..]);
        } else if let Some(body) = tail.strip_prefix("/*") {
            rest = body.find("*/").map_or("", |n| &body[n + 2..]);
        } else {
            out.push('/');
            rest = &tail[1..];
        }
    }
    out.push_str(rest);
    out
}

/// A port header's data type text, unless it names a net type or interface.
fn header_type(h: PortHeaderSyntax<'_>) -> Option<String> {
    match h {
        PortHeaderSyntax::VariablePortHeader(h) => {
            Some(without_comments(&h.data_type().syntax().text()))
        }
        PortHeaderSyntax::NetPortHeader(h) if h.net_type().is_none() => {
            Some(without_comments(&h.data_type().syntax().text()))
        }
        _ => None,
    }
}

/// `name` looked up from `scope` as a user nettype, followed through aliases
/// to the declaration that holds the data type: that declaration's symbol.
fn declaration<'e>(
    eval: &'e EvalSession<'_>,
    scope: Symbol<'e>,
    name: &str,
    trees: &[SyntaxTree],
) -> Option<Symbol<'e>> {
    let mut sym = eval.lookup_name(scope, name)?;
    for _ in 0..32 {
        if sym.kind() != SymbolKind::NetType || sym.net_type_is_built_in() {
            return None;
        }
        if sym.net_type_resolution_function().is_some() {
            return Some(sym);
        }
        // An alias: `nettype <other> <this>;` with no function of its own.
        let Some(target) = alias_target(&sym, trees) else {
            return Some(sym);
        };
        let Some(scope) = sym.parent() else {
            return Some(sym);
        };
        match eval.lookup_name(scope, &target) {
            Some(next) if next.kind() == SymbolKind::NetType && !next.net_type_is_built_in() => {
                sym = next
            }
            _ => return Some(sym),
        }
    }
    Some(sym)
}

/// The text of the data type a nettype declaration names, if it is an
/// identifier (an alias target or a named data type).
pub(crate) fn alias_target(nettype: &Symbol<'_>, trees: &[SyntaxTree]) -> Option<String> {
    for tree in trees {
        let Some(node) = nettype.syntax(tree) else {
            continue;
        };
        let mut node = node;
        loop {
            if let Some(d) = NetTypeDeclarationSyntax::cast(node) {
                return Some(
                    without_comments(&d.r#type().syntax().text())
                        .trim()
                        .to_string(),
                );
            }
            node = node.parent()?;
        }
    }
    None
}

/// Every net of `instances` declared with a user-defined nettype.
pub(crate) fn user_net_types<'e>(
    eval: &'e EvalSession<'_>,
    instances: &[(InstanceId, Symbol<'e>, Symbol<'e>, Option<InstanceId>)],
    signals: &HashMap<InstanceId, HashMap<SymbolId, SignalSlot>>,
    trees: &[SyntaxTree],
) -> Result<UserNets<'e>, String> {
    let mut out = UserNets::default();
    // (declaring scope, written name) -> the nettype declaration, for every
    // lookup already done.
    let mut looked: HashMap<(SymbolId, String), Option<usize>> = HashMap::new();
    let mut by_decl: HashMap<SymbolId, usize> = HashMap::new();
    for (id, body, _, _) in instances {
        let own = &signals[id];
        for m in vj_sim::netlist::module_items(body) {
            if m.kind() != SymbolKind::Net || !own.contains_key(&m.uid()) {
                continue;
            }
            let Some((name, dims)) = declared_type_name(&m, trees) else {
                continue;
            };
            let key = (body.uid(), name.clone());
            let found = match looked.get(&key) {
                Some(f) => *f,
                None => {
                    let f = match declaration(eval, *body, &name, trees) {
                        Some(decl) => match by_decl.get(&decl.uid()) {
                            Some(&i) => Some(i),
                            None => {
                                let mut ty = m.value_type().ok_or_else(|| {
                                    format!(
                                        "{}: a net of nettype `{name}` has no data type",
                                        m.hierarchical_path()
                                    )
                                })?;
                                for _ in 0..dims {
                                    ty = ty.element_type().ok_or_else(|| {
                                        format!(
                                            "{}: cannot find the element type of an array of `{name}` nets",
                                            m.hierarchical_path()
                                        )
                                    })?;
                                }
                                // An array-valued nettype (`typedef real TR[5];
                                // nettype TR wTR;`): each net is a memory of
                                // the leaf element's rows, every row starting
                                // at the leaf type's default (6.7.3).
                                let mut data_array = false;
                                while ty.is_unpacked_array() {
                                    data_array = true;
                                    ty = ty.element_type().ok_or_else(|| {
                                        format!(
                                            "{}: cannot find the element type of nettype `{name}`'s array data type",
                                            m.hierarchical_path()
                                        )
                                    })?;
                                }
                                vj_sim::state::storage::storage_width(&ty).ok_or_else(|| {
                                    format!(
                                        "{}: the data type of nettype `{name}` has no stored form",
                                        m.hierarchical_path()
                                    )
                                })?;
                                let init = vj_sim::state::storage::default_bits(&ty)
                                        .ok_or_else(|| {
                                            format!(
                                                "{}: the data type of nettype `{name}` has no default value",
                                                m.hierarchical_path()
                                            )
                                        })?;
                                out.types.push(UserNetType {
                                    name: name.clone(),
                                    func: decl.net_type_resolution_function(),
                                    init,
                                    data_array,
                                });
                                by_decl.insert(decl.uid(), out.types.len() - 1);
                                Some(out.types.len() - 1)
                            }
                        },
                        None => None,
                    };
                    looked.insert(key, f);
                    f
                }
            };
            if let Some(t) = found {
                out.by_net.insert((*id, m.uid()), t);
            }
        }
    }
    Ok(out)
}