veryl-analyzer 0.20.0

A modern hardware description language
Documentation
use crate::conv::Context;
use crate::ir::assign_table::{AssignContext, AssignTable};
use crate::ir::{
    AssignDestination, Component, Comptime, Expression, FfTable, Statement, VarId, VarIndex,
    VarSelect,
};
use indent::indent_all_by;
use std::fmt;
use std::sync::Arc;
use veryl_parser::resource_table::StrId;
use veryl_parser::token_range::TokenRange;

#[derive(Clone, Default)]
pub struct DeclarationBlock(pub Vec<Declaration>);

impl DeclarationBlock {
    pub fn new(decl: Declaration) -> Self {
        Self(vec![decl])
    }
}

#[derive(Clone)]
pub enum Declaration {
    Comb(CombDeclaration),
    Ff(Box<FfDeclaration>),
    Inst(Box<InstDeclaration>),
    Initial(InitialDeclaration),
    Final(FinalDeclaration),
    Unsupported(TokenRange),
    Null,
}

impl Declaration {
    pub fn new_comb(statements: Vec<Statement>) -> Self {
        Self::Comb(CombDeclaration { statements })
    }

    pub fn new_ff(clock: FfClock, reset: Option<FfReset>, statements: Vec<Statement>) -> Self {
        Self::Ff(Box::new(FfDeclaration {
            clock,
            reset,
            statements,
        }))
    }

    pub fn is_null(&self) -> bool {
        matches!(self, Declaration::Null)
    }

    pub fn eval_assign(&self, context: &mut Context, assign_table: &mut AssignTable) {
        match self {
            Declaration::Comb(x) => x.eval_assign(context, assign_table),
            Declaration::Ff(x) => x.eval_assign(context, assign_table),
            Declaration::Inst(x) => x.eval_assign(context, assign_table),
            Declaration::Initial(x) => x.eval_assign(context, assign_table),
            Declaration::Final(x) => x.eval_assign(context, assign_table),
            Declaration::Unsupported(_) => (),
            Declaration::Null => (),
        }
        assign_table.refernced.clear();
    }

    pub fn gather_ff(&self, context: &mut Context, table: &mut FfTable, decl: usize) {
        match self {
            Declaration::Ff(x) => x.gather_ff(context, table, decl),
            Declaration::Comb(x) => x.gather_ff_comb(context, table, decl),
            Declaration::Inst(x) => x.gather_ff(context, table, decl),
            _ => {}
        }
    }
}

impl fmt::Display for Declaration {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            Declaration::Comb(x) => x.fmt(f),
            Declaration::Ff(x) => x.fmt(f),
            Declaration::Inst(x) => x.fmt(f),
            Declaration::Initial(x) => x.fmt(f),
            Declaration::Final(x) => x.fmt(f),
            Declaration::Unsupported(_) => "/* unsupported */".fmt(f),
            Declaration::Null => "".fmt(f),
        }
    }
}

#[derive(Clone)]
pub struct CombDeclaration {
    pub statements: Vec<Statement>,
}

impl CombDeclaration {
    pub fn eval_assign(&self, context: &mut Context, assign_table: &mut AssignTable) {
        for x in &self.statements {
            x.eval_assign(context, assign_table, AssignContext::Comb, &[]);
        }
    }

    pub fn gather_ff_comb(&self, context: &mut Context, table: &mut FfTable, decl: usize) {
        for x in &self.statements {
            x.gather_ff_comb_assign(context, table, decl);
        }
    }
}

impl fmt::Display for CombDeclaration {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        let mut ret = "comb {\n".to_string();

        for x in &self.statements {
            let text = format!("{}\n", x);
            ret.push_str(&indent_all_by(2, text));
        }

        ret.push('}');
        ret.fmt(f)
    }
}

#[derive(Clone)]
pub struct FfClock {
    pub id: VarId,
    pub index: VarIndex,
    pub select: VarSelect,
    pub comptime: Comptime,
}

impl fmt::Display for FfClock {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        format!("{}{}{}", self.id, self.index, self.select).fmt(f)
    }
}

#[derive(Clone)]
pub struct FfReset {
    pub id: VarId,
    pub index: VarIndex,
    pub select: VarSelect,
    pub comptime: Comptime,
}

impl fmt::Display for FfReset {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        format!("{}{}{}", self.id, self.index, self.select).fmt(f)
    }
}

#[derive(Clone)]
pub struct FfDeclaration {
    pub clock: FfClock,
    pub reset: Option<FfReset>,
    pub statements: Vec<Statement>,
}

impl FfDeclaration {
    pub fn eval_assign(&self, context: &mut Context, assign_table: &mut AssignTable) {
        for x in &self.statements {
            x.eval_assign(context, assign_table, AssignContext::Ff, &[]);
        }
    }

    pub fn gather_ff(&self, context: &mut Context, table: &mut FfTable, decl: usize) {
        for x in &self.statements {
            x.gather_ff(context, table, decl);
        }
    }
}

impl fmt::Display for FfDeclaration {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        let mut ret = if let Some(x) = &self.reset {
            format!("ff ({}, {}) {{\n", self.clock, x)
        } else {
            format!("ff ({}) {{\n", self.clock)
        };

        for x in &self.statements {
            let text = format!("{}\n", x);
            ret.push_str(&indent_all_by(2, text));
        }

        ret.push('}');
        ret.fmt(f)
    }
}

#[derive(Clone)]
pub struct InstInput {
    pub id: VarId,
    pub expr: Expression,
}

impl fmt::Display for InstInput {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{} <- {}", self.id, self.expr)
    }
}

#[derive(Clone)]
pub struct InstOutput {
    pub id: VarId,
    pub dst: Vec<AssignDestination>,
}

impl fmt::Display for InstOutput {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        let mut ret = format!("{} -> ", self.id);

        if self.dst.len() == 1 {
            ret.push_str(&format!("{}", self.dst[0]));
        } else if !self.dst.is_empty() {
            ret.push_str(&format!("{{{}", self.dst[0]));
            for d in &self.dst[1..] {
                ret.push_str(&format!(", {}", d));
            }
            ret.push('}');
        }

        ret.fmt(f)
    }
}

#[derive(Clone)]
pub struct InstDeclaration {
    pub name: StrId,
    pub inputs: Vec<InstInput>,
    pub outputs: Vec<InstOutput>,
    /// `Arc`-shared: without it, repeated instantiations of the same generic
    /// module each deep-clone the `Component`, blowing pass2 memory.
    pub component: Arc<Component>,
}

impl InstDeclaration {
    pub fn eval_assign(&self, context: &mut Context, assign_table: &mut AssignTable) {
        for x in &self.outputs {
            for dst in &x.dst {
                dst.eval_assign(context, assign_table, AssignContext::Ff);
            }
        }

        if let Component::SystemVerilog(x) = self.component.as_ref() {
            for dst in &x.connects {
                dst.eval_assign(context, assign_table, AssignContext::SystemVerilog);
            }
        }
    }

    /// Register the variables read by each instance input expression so
    /// downstream analyses (e.g. comb-to-FF hoist reach) can see that
    /// those reads happen in a comb context (effectively a continuous-
    /// assign of the expression to the child port).  Tagged
    /// `from_ff=false` so the `is_ff` classifier ignores them.
    pub fn gather_ff(&self, context: &mut Context, table: &mut FfTable, decl: usize) {
        for input in &self.inputs {
            input.expr.gather_ff(context, table, decl, None, false);
        }
    }
}

impl fmt::Display for InstDeclaration {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        let mut ret = format!("inst {} (\n", self.name);

        for x in &self.inputs {
            let text = format!("{};\n", x);
            ret.push_str(&indent_all_by(2, text));
        }

        for x in &self.outputs {
            let text = format!("{};\n", x);
            ret.push_str(&indent_all_by(2, text));
        }

        ret.push_str(") {\n");

        let text = format!("{}\n", self.component);
        ret.push_str(&indent_all_by(2, text));

        ret.push('}');
        ret.fmt(f)
    }
}

#[derive(Clone)]
pub struct InitialDeclaration {
    pub statements: Vec<Statement>,
}

impl InitialDeclaration {
    pub fn eval_assign(&self, context: &mut Context, assign_table: &mut AssignTable) {
        for x in &self.statements {
            x.eval_assign(context, assign_table, AssignContext::Initial, &[]);
        }
    }
}

impl fmt::Display for InitialDeclaration {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        let mut ret = "initial {\n".to_string();

        for x in &self.statements {
            let text = format!("{}\n", x);
            ret.push_str(&indent_all_by(2, text));
        }

        ret.push('}');
        ret.fmt(f)
    }
}

#[derive(Clone)]
pub struct FinalDeclaration {
    pub statements: Vec<Statement>,
}

impl FinalDeclaration {
    pub fn eval_assign(&self, context: &mut Context, assign_table: &mut AssignTable) {
        for x in &self.statements {
            x.eval_assign(context, assign_table, AssignContext::Final, &[]);
        }
    }
}

impl fmt::Display for FinalDeclaration {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        let mut ret = "final {\n".to_string();

        for x in &self.statements {
            let text = format!("{}\n", x);
            ret.push_str(&indent_all_by(2, text));
        }

        ret.push('}');
        ret.fmt(f)
    }
}