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>,
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);
}
}
}
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)
}
}