From 00b65ae57a181cf7fcb7c207429c70d2b778dfdc Mon Sep 17 00:00:00 2001 From: Jacob Lifshay Date: Wed, 3 Jun 2026 03:53:45 -0700 Subject: [PATCH] WIP: reimplement fayalite::formal and add support to the simulator --- crates/fayalite/src/expr.rs | 1 + crates/fayalite/src/expr/ops.rs | 66 +++++- crates/fayalite/src/expr/target.rs | 7 + crates/fayalite/src/firrtl.rs | 198 +++++++++--------- crates/fayalite/src/formal.rs | 186 +++++++++++++++- crates/fayalite/src/module.rs | 72 ++++++- .../src/module/transform/deduce_resets.rs | 6 + .../src/module/transform/simplify_enums.rs | 5 +- crates/fayalite/src/module/transform/visit.rs | 2 +- crates/fayalite/src/reg.rs | 3 +- .../vendor/xilinx/yosys_nextpnr_prjxray.rs | 1 + crates/fayalite/src/wire.rs | 4 +- crates/fayalite/visit_types.json | 28 ++- 13 files changed, 463 insertions(+), 116 deletions(-) diff --git a/crates/fayalite/src/expr.rs b/crates/fayalite/src/expr.rs index e235cd7..905c22b 100644 --- a/crates/fayalite/src/expr.rs +++ b/crates/fayalite/src/expr.rs @@ -227,6 +227,7 @@ expr_enum! { RegSync(Reg), RegAsync(Reg), MemPort(MemPort), + FormalInput(ops::FormalInputExpr), } } diff --git a/crates/fayalite/src/expr/ops.rs b/crates/fayalite/src/expr/ops.rs index b2e20ad..5d0b346 100644 --- a/crates/fayalite/src/expr/ops.rs +++ b/crates/fayalite/src/expr/ops.rs @@ -11,12 +11,13 @@ use crate::{ HdlPartialEqImpl, HdlPartialOrd, HdlPartialOrdImpl, NotALiteralExpr, ReduceBitsImpl, ToExpr, ToLiteralBits, ToSimValueInner, ToValueless, ValueType, Valueless, target::{ - GetTarget, Target, TargetPathArrayElement, TargetPathBundleField, + GetTarget, Target, TargetBase, TargetPathArrayElement, TargetPathBundleField, TargetPathDynArrayElement, TargetPathElement, TargetPathToTraceAsString, TargetPathTraceAsStringInner, }, value_category::ValueCategoryExpr, }, + formal::FormalInput, int::{ Bool, BoolOrIntType, DynSize, IntType, KnownSize, SInt, SIntType, SIntValue, Size, UInt, UIntType, UIntValue, @@ -4881,3 +4882,66 @@ impl ToExpr for TraceAsStringAsInner { } } } + +#[derive(Copy, Clone, PartialEq, Eq, Hash)] +pub struct FormalInputExpr { + formal_input: FormalInput, + ty: T, + target: Interned, +} + +impl fmt::Debug for FormalInputExpr { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.formal_input.fmt(f) + } +} + +impl FormalInputExpr { + pub fn new(formal_input: FormalInput) -> Self { + Self { + formal_input, + ty: T::from_canonical(formal_input.ty()), + target: Target::Base(TargetBase::FormalInput(formal_input).intern_sized()) + .intern_sized(), + } + } + pub fn formal_input(self) -> FormalInput { + self.formal_input + } +} + +impl GetTarget for FormalInputExpr { + fn target(&self) -> Option> { + Some(self.target) + } +} + +impl ToLiteralBits for FormalInputExpr { + fn to_literal_bits(&self) -> Result, NotALiteralExpr> { + Err(NotALiteralExpr) + } +} + +impl ValueType for FormalInputExpr { + type Type = T; + type ValueCategory = ValueCategoryExpr; + + fn ty(&self) -> Self::Type { + self.ty + } +} + +impl ToExpr for FormalInputExpr { + fn to_expr(&self) -> Expr { + Expr { + __enum: ExprEnum::FormalInput(FormalInputExpr { + formal_input: self.formal_input, + ty: self.formal_input.ty(), + target: self.target, + }) + .intern(), + __ty: self.ty, + __flow: self.formal_input.flow(), + } + } +} diff --git a/crates/fayalite/src/expr/target.rs b/crates/fayalite/src/expr/target.rs index 9016111..a4967e2 100644 --- a/crates/fayalite/src/expr/target.rs +++ b/crates/fayalite/src/expr/target.rs @@ -4,6 +4,7 @@ use crate::{ array::Array, bundle::{Bundle, BundleField}, expr::{Expr, Flow, ToExpr, ValueType, value_category::ValueCategoryExpr}, + formal::FormalInput, intern::{Intern, Interned}, memory::{DynPortType, MemPort}, module::{Instance, ModuleIO, TargetName}, @@ -295,6 +296,10 @@ impl_target_base! { #[is = is_instance] #[to = instance] Instance(Instance), + #[from = from] + #[is = is_formal_input] + #[to = formal_input] + FormalInput(FormalInput), } } @@ -343,6 +348,7 @@ impl TargetBase { TargetBase::RegAsync(v) => TargetName(v.scoped_name(), None), TargetBase::Wire(v) => TargetName(v.scoped_name(), None), TargetBase::Instance(v) => TargetName(v.scoped_name(), None), + TargetBase::FormalInput(v) => TargetName(v.scoped_name(), None), } } pub fn canonical_ty(&self) -> CanonicalType { @@ -354,6 +360,7 @@ impl TargetBase { TargetBase::RegAsync(v) => v.ty(), TargetBase::Wire(v) => v.ty(), TargetBase::Instance(v) => v.ty().canonical(), + TargetBase::FormalInput(v) => v.ty(), } } } diff --git a/crates/fayalite/src/firrtl.rs b/crates/fayalite/src/firrtl.rs index e2f33ae..d7c5e0b 100644 --- a/crates/fayalite/src/firrtl.rs +++ b/crates/fayalite/src/firrtl.rs @@ -14,7 +14,7 @@ use crate::{ TargetPathTraceAsStringInner, }, }, - formal::FormalKind, + formal::{FormalInput, FormalInputKind, FormalKind}, int::IntType, intern::{Intern, Interned}, memory::{PortKind, PortName}, @@ -385,77 +385,66 @@ struct BlockDefinitionsCache { cast_bits_to_enum_exprs: RefCell>, cast_bits_to_array_exprs: RefCell>, cast_bits_to_phantom_const_exprs: RefCell>, -} - -struct BlockDefinitionsState<'a> { - rc_definitions: RcDefinitions, - parent: &'a BlockDefinitions<'a>, - cache: BlockDefinitionsCache, + per_module_formal_inputs: RefCell>, } struct BlockDefinitions<'a> { - state: Option>, + rc_definitions: RcDefinitions, + parent: Option<&'a BlockDefinitions<'a>>, + cache: BlockDefinitionsCache, } impl<'a> BlockDefinitions<'a> { fn new(parent: &'a BlockDefinitions<'a>) -> Self { Self { - state: Some(BlockDefinitionsState { - rc_definitions: RcDefinitions::default(), - parent, - cache: Default::default(), - }), + rc_definitions: RcDefinitions::default(), + parent: Some(parent), + cache: Default::default(), } } - fn none() -> Self { - Self { state: None } + fn module() -> Self { + Self { + rc_definitions: RcDefinitions::default(), + parent: None, + cache: Default::default(), + } } fn get_or_write_definition( - &mut self, + &self, key: K, field: impl Fn(&BlockDefinitionsCache) -> &RefCell>, write_definition: impl FnOnce(BlockDefinitionsWriter<'_, '_>, &K) -> Result, ) -> Result { - let state = self.state.as_ref().expect("should be some"); - let mut cur_state = state; + let mut current = self; loop { - let field = field(&cur_state.cache).borrow(); + let field = field(¤t.cache).borrow(); if let Some(retval) = field.get(&key) { return Ok(retval.clone()); } - let Some(parent_state) = &cur_state.parent.state else { + let Some(parent) = current.parent else { break; }; - cur_state = parent_state; + current = parent; } let retval = write_definition(BlockDefinitionsWriter { definitions: self }, &key)?; - Ok(field(&self.state.as_ref().expect("should be some").cache) + Ok(field(&self.cache) .borrow_mut() .entry(key) .or_insert(retval) .clone()) } - fn write_out(&mut self, indent: Indent<'_>, out: &mut String) { - self.state - .as_ref() - .expect("should be some") - .rc_definitions - .write_and_clear(indent, out); + fn write_out(&self, indent: Indent<'_>, out: &mut String) { + self.rc_definitions.write_and_clear(indent, out); } } struct BlockDefinitionsWriter<'a, 'b> { - definitions: &'b mut BlockDefinitions<'a>, + definitions: &'b BlockDefinitions<'a>, } impl BlockDefinitionsWriter<'_, '_> { - fn add_definition_line(&mut self, v: impl fmt::Display) { - self.definitions - .state - .as_ref() - .expect("should be some") - .rc_definitions - .add_definition_line(v); + fn add_definition_line(&self, v: impl fmt::Display) { + self.definitions.rc_definitions.add_definition_line(v); } } @@ -467,12 +456,6 @@ impl<'a> std::ops::Deref for BlockDefinitionsWriter<'a, '_> { } } -impl std::ops::DerefMut for BlockDefinitionsWriter<'_, '_> { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.definitions - } -} - struct EnumDef { variants: RefCell, body: String, @@ -594,6 +577,7 @@ impl TypeState { struct ModuleState { ns: Namespace, match_arm_values: HashMap, Ident>, + block_definitions: Rc>, } impl Default for ModuleState { @@ -601,6 +585,7 @@ impl Default for ModuleState { Self { ns: Default::default(), match_arm_values: Default::default(), + block_definitions: Rc::new(BlockDefinitions::module()), } } } @@ -968,7 +953,7 @@ impl<'a> Exporter<'a> { &mut self, value: Expr, to_ty: ToTy, - definitions: &mut BlockDefinitions<'_>, + definitions: &BlockDefinitions<'_>, const_ty: bool, ) -> Result { let from_ty = value.ty(); @@ -1003,7 +988,7 @@ impl<'a> Exporter<'a> { &mut self, firrtl_cast_fn: Option<&str>, value: Expr, - definitions: &mut BlockDefinitions<'_>, + definitions: &BlockDefinitions<'_>, const_ty: bool, ) -> Result { let value = self.expr(Expr::canonical(value), definitions, const_ty)?; @@ -1017,7 +1002,7 @@ impl<'a> Exporter<'a> { &mut self, base: Expr, range: Range, - definitions: &mut BlockDefinitions<'_>, + definitions: &BlockDefinitions<'_>, const_ty: bool, ) -> Result { let base_width = base.ty().width(); @@ -1035,20 +1020,19 @@ impl<'a> Exporter<'a> { fn array_literal_expr( &mut self, expr: ops::ArrayLiteral, - definitions: &mut BlockDefinitions<'_>, + definitions: &BlockDefinitions<'_>, const_ty: bool, ) -> Result { definitions.get_or_write_definition( (expr, const_ty), |c| &c.array_literal_exprs, - |mut definitions, &(expr, const_ty)| { + |definitions, &(expr, const_ty)| { let ident = self.module.ns.make_new("_array_literal_expr"); let ty_str = self.type_state.ty(expr.ty())?; let const_ = if const_ty { "const " } else { "" }; definitions.add_definition_line(format_args!("wire {ident}: {const_}{ty_str}")); for (index, element) in expr.element_values().into_iter().enumerate() { - let element = - self.expr(Expr::canonical(element), &mut definitions, const_ty)?; + let element = self.expr(Expr::canonical(element), &definitions, const_ty)?; definitions .add_definition_line(format_args!("connect {ident}[{index}], {element}")); } @@ -1062,13 +1046,13 @@ impl<'a> Exporter<'a> { fn bundle_literal_expr( &mut self, expr: ops::BundleLiteral, - definitions: &mut BlockDefinitions<'_>, + definitions: &BlockDefinitions<'_>, const_ty: bool, ) -> Result { definitions.get_or_write_definition( (expr, const_ty), |c| &c.bundle_literal_exprs, - |mut definitions, &(expr, const_ty)| { + |definitions, &(expr, const_ty)| { let ident = self.module.ns.make_new("_bundle_literal_expr"); let ty = expr.ty(); let (ty_ident, bundle_ns) = self.type_state.bundle_def(ty)?; @@ -1090,7 +1074,7 @@ impl<'a> Exporter<'a> { ); let name = bundle_ns.borrow_mut().get(name); let field_value = - self.expr(Expr::canonical(field_value), &mut definitions, const_ty)?; + self.expr(Expr::canonical(field_value), &definitions, const_ty)?; definitions .add_definition_line(format_args!("connect {ident}.{name}, {field_value}")); } @@ -1104,13 +1088,13 @@ impl<'a> Exporter<'a> { fn uninit_expr( &mut self, expr: ops::Uninit, - definitions: &mut BlockDefinitions<'_>, + definitions: &BlockDefinitions<'_>, const_ty: bool, ) -> Result { definitions.get_or_write_definition( (expr, const_ty), |c| &c.uninit_exprs, - |mut definitions, &(expr, const_ty)| { + |definitions, &(expr, const_ty)| { let ident = self.module.ns.make_new("_uninit_expr"); let ty = expr.ty(); let ty_ident = self.type_state.ty(ty)?; @@ -1124,7 +1108,7 @@ impl<'a> Exporter<'a> { fn enum_literal_expr( &mut self, expr: ops::EnumLiteral, - definitions: &mut BlockDefinitions<'_>, + definitions: &BlockDefinitions<'_>, const_ty: bool, ) -> Result { let variant_expr = expr @@ -1137,13 +1121,13 @@ impl<'a> Exporter<'a> { &mut self, value_str: String, ty: Bundle, - definitions: &mut BlockDefinitions<'_>, + definitions: &BlockDefinitions<'_>, extra_indent: Indent<'_>, ) -> Result { definitions.get_or_write_definition( (value_str, ty), |c| &c.cast_bundle_to_bits_exprs, - |mut definitions, &(ref value_str, ty)| { + |definitions, &(ref value_str, ty)| { if ty.fields().is_empty() { return Ok("UInt<0>(0)".into()); } @@ -1152,7 +1136,7 @@ impl<'a> Exporter<'a> { return self.expr_cast_to_bits( format!("{value_str}.{field_ident}"), field.ty, - &mut definitions, + &definitions, extra_indent, ); } @@ -1181,7 +1165,7 @@ impl<'a> Exporter<'a> { let field_bits = self.expr_cast_to_bits( format!("{value_str}.{field_ident}"), field.ty, - &mut definitions, + &definitions, extra_indent, )?; definitions.add_definition_line(format_args!( @@ -1210,13 +1194,13 @@ impl<'a> Exporter<'a> { &mut self, value_str: String, ty: Enum, - definitions: &mut BlockDefinitions<'_>, + definitions: &BlockDefinitions<'_>, extra_indent: Indent<'_>, ) -> Result { definitions.get_or_write_definition( (value_str, ty), |c| &c.cast_enum_to_bits_exprs, - |mut definitions, &(ref value_str, ty)| { + |definitions, &(ref value_str, ty)| { if ty.variants().is_empty() { return Ok("UInt<0>(0)".into()); } @@ -1241,7 +1225,7 @@ impl<'a> Exporter<'a> { let variant_bits = self.expr_cast_to_bits( variant_value.to_string(), variant_ty, - &mut definitions, + &definitions, extra_indent, )?; definitions.add_definition_line(format_args!( @@ -1270,13 +1254,13 @@ impl<'a> Exporter<'a> { &mut self, value_str: String, ty: Array, - definitions: &mut BlockDefinitions<'_>, + definitions: &BlockDefinitions<'_>, extra_indent: Indent<'_>, ) -> Result { definitions.get_or_write_definition( (value_str, ty), |c| &c.cast_array_to_bits_exprs, - |mut definitions, &(ref value_str, ty)| { + |definitions, &(ref value_str, ty)| { if ty.is_empty() { return Ok("UInt<0>(0)".into()); } @@ -1284,7 +1268,7 @@ impl<'a> Exporter<'a> { return self.expr_cast_to_bits( value_str.clone() + "[0]", ty.element(), - &mut definitions, + &definitions, extra_indent, ); } @@ -1299,7 +1283,7 @@ impl<'a> Exporter<'a> { let element_bits = self.expr_cast_to_bits( format!("{value_str}[{index}]"), ty.element(), - &mut definitions, + &definitions, extra_indent, )?; definitions.add_definition_line(format_args!( @@ -1328,7 +1312,7 @@ impl<'a> Exporter<'a> { &mut self, value_str: String, ty: CanonicalType, - definitions: &mut BlockDefinitions<'_>, + definitions: &BlockDefinitions<'_>, extra_indent: Indent<'_>, ) -> Result { match ty.unwrap_transparent_types() { @@ -1357,13 +1341,13 @@ impl<'a> Exporter<'a> { &mut self, value_str: String, ty: Bundle, - definitions: &mut BlockDefinitions<'_>, + definitions: &BlockDefinitions<'_>, extra_indent: Indent<'_>, ) -> Result { definitions.get_or_write_definition( (value_str, ty), |c| &c.cast_bits_to_bundle_exprs, - |mut definitions, &(ref value_str, ty)| { + |definitions, &(ref value_str, ty)| { let (ty_ident, _) = self.type_state.bundle_def(ty)?; let retval = self.module.ns.make_new("_cast_bits_to_bundle_expr"); definitions @@ -1420,7 +1404,7 @@ impl<'a> Exporter<'a> { let field_value = self.expr_cast_bits_to( format!("{flattened_ident}.{flattened_field_ident}"), field.ty, - &mut definitions, + &definitions, extra_indent, )?; definitions.add_definition_line(format_args!( @@ -1435,13 +1419,13 @@ impl<'a> Exporter<'a> { &mut self, value_str: String, ty: Enum, - definitions: &mut BlockDefinitions<'_>, + definitions: &BlockDefinitions<'_>, extra_indent: Indent<'_>, ) -> Result { definitions.get_or_write_definition( (value_str, ty), |c| &c.cast_bits_to_enum_exprs, - |mut definitions, &(ref value_str, ty)| { + |definitions, &(ref value_str, ty)| { let (ty_ident, enum_def) = self.type_state.enum_def(ty)?; let retval = self.module.ns.make_new("_cast_bits_to_enum_expr"); definitions @@ -1457,7 +1441,7 @@ impl<'a> Exporter<'a> { let variant_value = self.expr_cast_bits_to( value_str.clone(), variant_ty, - &mut definitions, + &definitions, extra_indent, )?; definitions.add_definition_line(format_args!( @@ -1507,7 +1491,7 @@ impl<'a> Exporter<'a> { let variant_value = self.expr_cast_bits_to( body_value.clone(), variant_ty, - &mut definitions, + &definitions, extra_indent, )?; definitions.add_definition_line(format_args!( @@ -1530,13 +1514,13 @@ impl<'a> Exporter<'a> { &mut self, value_str: String, ty: Array, - definitions: &mut BlockDefinitions<'_>, + definitions: &BlockDefinitions<'_>, extra_indent: Indent<'_>, ) -> Result { definitions.get_or_write_definition( (value_str, ty), |c| &c.cast_bits_to_array_exprs, - |mut definitions, &(ref value_str, ty)| { + |definitions, &(ref value_str, ty)| { let retval = self.module.ns.make_new("_cast_bits_to_array_expr"); let array_ty = self.type_state.ty(ty)?; definitions @@ -1565,7 +1549,7 @@ impl<'a> Exporter<'a> { let element_value = self.expr_cast_bits_to( format!("{flattened_ident}[{index}]"), ty.element(), - &mut definitions, + &definitions, extra_indent, )?; definitions.add_definition_line(format_args!( @@ -1580,7 +1564,7 @@ impl<'a> Exporter<'a> { &mut self, value_str: String, ty: CanonicalType, - definitions: &mut BlockDefinitions<'_>, + definitions: &BlockDefinitions<'_>, extra_indent: Indent<'_>, ) -> Result { match ty.unwrap_transparent_types() { @@ -1603,7 +1587,7 @@ impl<'a> Exporter<'a> { CanonicalType::PhantomConst(ty) => definitions.get_or_write_definition( (value_str, ty), |c| &c.cast_bits_to_phantom_const_exprs, - |mut definitions, &(ref _value_str, _ty)| { + |definitions, &(ref _value_str, _ty)| { let retval = self.module.ns.make_new("_cast_bits_to_phantom_const_expr"); definitions .add_definition_line(format_args!("{extra_indent}wire {retval}: {{}}")); @@ -1620,7 +1604,7 @@ impl<'a> Exporter<'a> { &mut self, func: &str, arg: Expr, - definitions: &mut BlockDefinitions<'_>, + definitions: &BlockDefinitions<'_>, const_ty: bool, ) -> Result { Ok(format!( @@ -1633,7 +1617,7 @@ impl<'a> Exporter<'a> { func: &str, lhs: Expr, rhs: Expr, - definitions: &mut BlockDefinitions<'_>, + definitions: &BlockDefinitions<'_>, const_ty: bool, ) -> Result { Ok(format!( @@ -1642,10 +1626,25 @@ impl<'a> Exporter<'a> { rhs = self.expr(Expr::canonical(rhs), definitions, const_ty)?, )) } + fn expr_formal_input(&mut self, formal_input: FormalInput, const_ty: bool) -> Result { + let definitions = self.module.block_definitions.clone(); + definitions.get_or_write_definition( + (formal_input, const_ty), + |c| &c.per_module_formal_inputs, + |definitions, &(formal_input, const_ty)| match formal_input.kind() { + FormalInputKind::FormalGlobalClock => todo!(), + FormalInputKind::FormalReset => todo!(), + FormalInputKind::AnyConst => todo!(), + FormalInputKind::AnySeq => todo!(), + FormalInputKind::AllConst => todo!(), + FormalInputKind::AllSeq => todo!(), + }, + ) + } fn expr( &mut self, expr: Expr, - definitions: &mut BlockDefinitions<'_>, + definitions: &BlockDefinitions<'_>, const_ty: bool, ) -> Result { match *Expr::expr_enum(expr) { @@ -2012,6 +2011,7 @@ impl<'a> Exporter<'a> { let port_name = Ident::from(expr.port_name()); Ok(format!("{mem_name}.{port_name}")) } + ExprEnum::FormalInput(expr) => self.expr_formal_input(expr.formal_input(), const_ty), } } fn write_mem_init( @@ -2126,6 +2126,7 @@ impl<'a> Exporter<'a> { TargetBase::RegAsync(v) => self.module.ns.get(v.name_id()), TargetBase::Wire(v) => self.module.ns.get(v.name_id()), TargetBase::Instance(v) => self.module.ns.get(v.name_id()), + TargetBase::FormalInput(_) => unreachable!("FormalInput can't be annotated"), }; Ok(AnnotationTargetRef { base, segments }) } @@ -2241,7 +2242,7 @@ impl<'a> Exporter<'a> { &mut self, stmt_reg: StmtReg, module_name: Ident, - definitions: &mut BlockDefinitions<'_>, + definitions: &BlockDefinitions<'_>, body: &mut String, ) -> Result<()> { let StmtReg { annotations, reg } = stmt_reg; @@ -2301,8 +2302,8 @@ impl<'a> Exporter<'a> { ) .unwrap(); } - let lhs = self.expr(lhs, &mut definitions, false)?; - let rhs = self.expr(rhs, &mut definitions, false)?; + let lhs = self.expr(lhs, &definitions, false)?; + let rhs = self.expr(rhs, &definitions, false)?; writeln!( body, "{indent}connect {lhs}, {rhs}{}", @@ -2318,9 +2319,9 @@ impl<'a> Exporter<'a> { text, source_location, }) => { - let clk = self.expr(Expr::canonical(clk), &mut definitions, false)?; - let pred = self.expr(Expr::canonical(pred), &mut definitions, false)?; - let en = self.expr(Expr::canonical(en), &mut definitions, false)?; + let clk = self.expr(Expr::canonical(clk), &definitions, false)?; + let pred = self.expr(Expr::canonical(pred), &definitions, false)?; + let en = self.expr(Expr::canonical(en), &definitions, false)?; let kind = match kind { FormalKind::Assert => "assert", FormalKind::Assume => "assume", @@ -2345,7 +2346,7 @@ impl<'a> Exporter<'a> { let mut when = "when"; let mut pushed_indent; loop { - let cond_str = self.expr(Expr::canonical(cond), &mut definitions, false)?; + let cond_str = self.expr(Expr::canonical(cond), &definitions, false)?; writeln!( body, "{indent}{when} {cond_str}:{}", @@ -2388,7 +2389,7 @@ impl<'a> Exporter<'a> { writeln!( body, "{indent}match {}:{}", - self.expr(Expr::canonical(expr), &mut definitions, false)?, + self.expr(Expr::canonical(expr), &definitions, false)?, FileInfo::new(source_location), ) .unwrap(); @@ -2442,13 +2443,13 @@ impl<'a> Exporter<'a> { .unwrap(); } Stmt::Declaration(StmtDeclaration::Reg(stmt_reg)) => { - self.stmt_reg(stmt_reg, module_name, &mut definitions, &mut body)?; + self.stmt_reg(stmt_reg, module_name, &definitions, &mut body)?; } Stmt::Declaration(StmtDeclaration::RegSync(stmt_reg)) => { - self.stmt_reg(stmt_reg, module_name, &mut definitions, &mut body)?; + self.stmt_reg(stmt_reg, module_name, &definitions, &mut body)?; } Stmt::Declaration(StmtDeclaration::RegAsync(stmt_reg)) => { - self.stmt_reg(stmt_reg, module_name, &mut definitions, &mut body)?; + self.stmt_reg(stmt_reg, module_name, &definitions, &mut body)?; } Stmt::Declaration(StmtDeclaration::Instance(StmtInstance { annotations, @@ -2475,6 +2476,7 @@ impl<'a> Exporter<'a> { } fn module(&mut self, module: Interned>) -> Result { self.module = ModuleState::default(); + let module_definitions = self.module.block_definitions.clone(); let indent = self.indent; let module_name = self.global_ns.get(module.name_id()); let mut body = String::new(); @@ -2543,12 +2545,10 @@ impl<'a> Exporter<'a> { "extmodule" } ModuleBody::Normal(NormalModuleBody { body: top_block }) => { - body.push_str(&self.block( - module, - top_block, - &module_indent, - &BlockDefinitions::none(), - )?); + let body_str = + self.block(module, top_block, &module_indent, &module_definitions)?; + module_definitions.write_out(indent, &mut body); + body.push_str(&body_str); "module" } }; diff --git a/crates/fayalite/src/formal.rs b/crates/fayalite/src/formal.rs index 17d3122..850972a 100644 --- a/crates/fayalite/src/formal.rs +++ b/crates/fayalite/src/formal.rs @@ -1,11 +1,195 @@ // SPDX-License-Identifier: LGPL-3.0-or-later // See Notices.txt for copyright information use crate::{ + expr::target::{GetTarget, Target}, int::BoolOrIntType, intern::{Intern, Interned, Memoize}, + module::{NameId, NameIdOrGlobal, ScopedNameId}, prelude::*, }; -use std::sync::OnceLock; +use std::{fmt, sync::OnceLock}; + +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +pub enum FormalInputKind { + FormalGlobalClock, + FormalReset, + AnyConst, + AnySeq, + AllConst, + AllSeq, +} + +impl FormalInputKind { + pub fn fixed_ty(self) -> Option { + match self { + Self::FormalGlobalClock => Some(Clock.into()), + Self::FormalReset => Some(SyncReset.into()), + Self::AnyConst => None, + Self::AnySeq => None, + Self::AllConst => None, + Self::AllSeq => None, + } + } + pub fn fixed_id(self) -> Option { + struct Cache { + formal_global_clock: crate::module::Id, + formal_reset: crate::module::Id, + } + static CACHE: OnceLock = OnceLock::new(); + let cache = || { + CACHE.get_or_init( + #[cold] + || Cache { + formal_global_clock: crate::module::Id::new(), + formal_reset: crate::module::Id::new(), + }, + ) + }; + match self { + Self::FormalGlobalClock => Some(cache().formal_global_clock), + Self::FormalReset => Some(cache().formal_reset), + Self::AnyConst => None, + Self::AnySeq => None, + Self::AllConst => None, + Self::AllSeq => None, + } + } + pub fn fixed_source_location(self) -> Option { + match self { + Self::FormalGlobalClock | Self::FormalReset => Some(SourceLocation::builtin()), + Self::AnyConst | Self::AnySeq | Self::AllConst | Self::AllSeq => None, + } + } + pub fn name(self) -> &'static str { + match self { + Self::FormalGlobalClock => "formal_global_clock", + Self::FormalReset => "formal_reset", + Self::AnyConst => "any_const", + Self::AnySeq => "any_seq", + Self::AllConst => "all_const", + Self::AllSeq => "all_seq", + } + } + pub fn interned_name(self) -> Interned { + macro_rules! impl_interned_name { + ($($variant:ident,)*) => { + match self { + $(Self::$variant => { + static CACHE: OnceLock> = OnceLock::new(); + *CACHE.get_or_init(|| Self::$variant.name().intern()) + })* + } + }; + } + impl_interned_name! { + FormalGlobalClock, + FormalReset, + AnyConst, + AnySeq, + AllConst, + AllSeq, + } + } +} + +#[derive(Clone, PartialEq, Eq, Hash)] +struct FormalInputData { + kind: FormalInputKind, + name_id: NameId, + ty: CanonicalType, + source_location: SourceLocation, +} + +#[derive(Copy, Clone, PartialEq, Eq, Hash)] +pub struct FormalInput(Interned); + +impl fmt::Debug for FormalInput { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + if self.kind().fixed_ty().is_some() { + f.write_str(&self.name()) + } else { + f.debug_tuple(&self.name()).field(&self.0.ty).finish() + } + } +} + +impl FormalInput { + #[track_caller] + pub fn new( + kind: FormalInputKind, + name_id: NameId, + ty: CanonicalType, + source_location: SourceLocation, + ) -> Self { + let NameId(name, id) = name_id; + assert_eq!(kind.interned_name(), name); + if let Some(fixed_ty) = kind.fixed_ty() { + assert_eq!(ty, fixed_ty); + } else { + assert!( + ty.is_castable_from_bits(), + "{name} type must be castable from bits. got:\n{ty:#?}", + ); + } + if let Some(fixed_source_location) = kind.fixed_source_location() { + assert_eq!(source_location, fixed_source_location); + } + if let Some(fixed_id) = kind.fixed_id() { + assert_eq!(id, fixed_id); + } + Self( + FormalInputData { + kind, + name_id, + ty, + source_location, + } + .intern_sized(), + ) + } + pub fn kind(self) -> FormalInputKind { + self.0.kind + } + pub fn name(self) -> Interned { + self.0.name_id.0 + } + pub fn name_id(self) -> NameId { + self.0.name_id + } + pub fn scoped_name(self) -> ScopedNameId { + ScopedNameId(NameIdOrGlobal::Global, self.name_id()) + } + pub fn source_location(self) -> SourceLocation { + self.0.source_location + } + pub(crate) fn must_connect_to(self) -> bool { + false + } + pub(crate) fn flow(self) -> crate::expr::Flow { + crate::expr::Flow::Source + } +} + +impl ValueType for FormalInput { + type Type = CanonicalType; + type ValueCategory = crate::expr::value_category::ValueCategoryExpr; + + fn ty(&self) -> Self::Type { + self.0.ty + } +} + +impl ToExpr for FormalInput { + fn to_expr(&self) -> Expr { + crate::expr::ops::FormalInputExpr::new(*self).to_expr() + } +} + +impl GetTarget for FormalInput { + fn target(&self) -> Option> { + Some(Target::from(*self).intern_sized()) + } +} #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] pub enum FormalKind { diff --git a/crates/fayalite/src/module.rs b/crates/fayalite/src/module.rs index 86fdd40..ecb910d 100644 --- a/crates/fayalite/src/module.rs +++ b/crates/fayalite/src/module.rs @@ -727,7 +727,57 @@ impl fmt::Display for NameId { } #[derive(Copy, Clone, Eq, PartialEq, Hash)] -pub struct ScopedNameId(pub NameId, pub NameId); +pub enum NameIdOrGlobal { + Global, + NameId(NameId), +} + +impl NameIdOrGlobal { + pub fn name_id(self) -> Option { + match self { + Self::Global => None, + Self::NameId(v) => Some(v), + } + } + #[track_caller] + pub fn assert_is_name_id(self) { + match self { + Self::Global => panic!("expected a NameId, got NameIdOrGlobal::Global"), + Self::NameId(_) => {} + } + } + #[track_caller] + pub fn unwrap_name_id(self) -> NameId { + match self { + Self::Global => panic!("expected a NameId, got NameIdOrGlobal::Global"), + Self::NameId(v) => v, + } + } +} + +impl fmt::Debug for NameIdOrGlobal { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Display::fmt(self, f) + } +} + +impl fmt::Display for NameIdOrGlobal { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Global => f.write_str("<>"), + Self::NameId(name_id) => fmt::Display::fmt(name_id, f), + } + } +} + +impl From for NameIdOrGlobal { + fn from(value: NameId) -> Self { + Self::NameId(value) + } +} + +#[derive(Copy, Clone, Eq, PartialEq, Hash)] +pub struct ScopedNameId(pub NameIdOrGlobal, pub NameId); impl fmt::Debug for ScopedNameId { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { @@ -805,7 +855,7 @@ impl Instance { self.containing_module_name_id().0 } pub fn containing_module_name_id(self) -> NameId { - self.scoped_name.0 + self.scoped_name.0.unwrap_name_id() } pub fn name(self) -> Interned { self.name_id().0 @@ -822,11 +872,13 @@ impl Instance { pub fn source_location(self) -> SourceLocation { self.source_location } + #[track_caller] pub fn new_unchecked( scoped_name: ScopedNameId, instantiated: Interned>, source_location: SourceLocation, ) -> Self { + scoped_name.0.assert_is_name_id(); Self { scoped_name, instantiated, @@ -2118,7 +2170,8 @@ impl transform::visit::Visitor for AssertExprValidity<'_> { | ExprEnum::CastToBits(_) | ExprEnum::CastBitsTo(_) | ExprEnum::ToTraceAsString(_) - | ExprEnum::TraceAsStringAsInner(_) => v.default_visit(self), + | ExprEnum::TraceAsStringAsInner(_) + | ExprEnum::FormalInput(_) => v.default_visit(self), ExprEnum::VariantAccess(_) | ExprEnum::ModuleIO(_) | ExprEnum::Instance(_) @@ -2369,7 +2422,7 @@ impl RegBuilder>, Option>, T> ty, } = self; ModuleBuilder::with(|module_builder| { - let scoped_name = ScopedNameId(module_builder.name, NameId(name, Id::new())); + let scoped_name = ScopedNameId(module_builder.name.into(), NameId(name, Id::new())); let reg = Reg::new_unchecked(scoped_name, source_location, ty, clock_domain, init); let retval = reg.to_expr(); // convert before borrow_mut since ModuleBuilder could be reentered by T::canonical() @@ -2765,6 +2818,7 @@ pub fn annotate(target: Expr, annotations: impl IntoAnnotations) { instance, } .into(), + TargetBase::FormalInput(_) => panic!("not a valid annotation target"), }; ModuleBuilder::with(|m| { unwrap!(m.impl_.borrow_mut().body.builder_normal_body_opt()) @@ -2779,7 +2833,7 @@ pub fn annotate(target: Expr, annotations: impl IntoAnnotations) { #[track_caller] pub fn wire_with_loc(name: &str, source_location: SourceLocation, ty: T) -> Expr { ModuleBuilder::with(|m| { - let scoped_name = ScopedNameId(m.name, NameId(name.intern(), Id::new())); + let scoped_name = ScopedNameId(m.name.into(), NameId(name.intern(), Id::new())); let wire = Wire::::new_unchecked(scoped_name, source_location, ty); let retval = wire.to_expr(); let canonical_wire = wire.canonical(); @@ -2811,7 +2865,7 @@ fn incomplete_declaration( source_location: SourceLocation, ) -> Rc> { ModuleBuilder::with(|m| { - let scoped_name = ScopedNameId(m.name, NameId(name.intern(), Id::new())); + let scoped_name = ScopedNameId(m.name.into(), NameId(name.intern(), Id::new())); let retval = Rc::new(RefCell::new(IncompleteDeclaration::Incomplete { name: scoped_name, source_location, @@ -2987,7 +3041,7 @@ pub fn instance_with_loc( source_location: SourceLocation, ) -> Expr { ModuleBuilder::with(|m| { - let scoped_name = ScopedNameId(m.name, NameId(name.intern(), Id::new())); + let scoped_name = ScopedNameId(m.name.into(), NameId(name.intern(), Id::new())); let instance = Instance:: { scoped_name, instantiated, @@ -3026,7 +3080,7 @@ fn memory_impl( source_location: SourceLocation, ) -> MemBuilder { ModuleBuilder::with(|m| { - let scoped_name = ScopedNameId(m.name, NameId(name.intern(), Id::new())); + let scoped_name = ScopedNameId(m.name.into(), NameId(name.intern(), Id::new())); let (retval, target_mem) = MemBuilder::new(scoped_name, source_location, mem_element_type); let mut impl_ = m.impl_.borrow_mut(); let body = impl_.body.builder_normal_body(); @@ -3181,7 +3235,7 @@ impl ModuleIO { NameId(self.bundle_field.name, self.id) } pub fn scoped_name(&self) -> ScopedNameId { - ScopedNameId(self.containing_module_name, self.name_id()) + ScopedNameId(self.containing_module_name.into(), self.name_id()) } pub fn source_location(&self) -> SourceLocation { self.source_location diff --git a/crates/fayalite/src/module/transform/deduce_resets.rs b/crates/fayalite/src/module/transform/deduce_resets.rs index 4595e84..7141de9 100644 --- a/crates/fayalite/src/module/transform/deduce_resets.rs +++ b/crates/fayalite/src/module/transform/deduce_resets.rs @@ -1215,6 +1215,7 @@ impl RunPass

for ExprEnum { ExprEnum::RegSync(expr) => reg_expr_run_pass(expr, pass_args), ExprEnum::RegAsync(expr) => reg_expr_run_pass(expr, pass_args), ExprEnum::MemPort(expr) => Ok(expr.run_pass(pass_args)?.map(ExprEnum::from)), + ExprEnum::FormalInput(expr) => Ok(expr.run_pass(pass_args)?.map(ExprEnum::from)), } } } @@ -1932,6 +1933,8 @@ impl_run_pass_copy!([] SVAttributeAnnotation); impl_run_pass_copy!([] UInt); impl_run_pass_copy!([] usize); impl_run_pass_copy!([] FormalKind); +impl_run_pass_copy!([] crate::formal::FormalInput); +impl_run_pass_copy!([] ops::FormalInputExpr); impl_run_pass_copy!([] PhantomConst); macro_rules! impl_run_pass_for_struct { @@ -2248,6 +2251,9 @@ impl RunPass

for TargetBase { &TargetBase::RegAsync(v) => v.into(), TargetBase::Wire(v) => return Ok(v.run_pass(pass_args)?.map(TargetBase::Wire)), TargetBase::Instance(v) => return Ok(v.run_pass(pass_args)?.map(TargetBase::Instance)), + TargetBase::FormalInput(v) => { + return Ok(v.run_pass(pass_args)?.map(TargetBase::FormalInput)); + } }; Ok(reg.run_pass(pass_args)?.map(|reg| match reg { AnyReg::Reg(reg) => TargetBase::Reg(reg), diff --git a/crates/fayalite/src/module/transform/simplify_enums.rs b/crates/fayalite/src/module/transform/simplify_enums.rs index c52939c..866402c 100644 --- a/crates/fayalite/src/module/transform/simplify_enums.rs +++ b/crates/fayalite/src/module/transform/simplify_enums.rs @@ -101,7 +101,7 @@ struct ModuleState { impl ModuleState { fn gen_name(&mut self, name: &str) -> ScopedNameId { - ScopedNameId(self.module_name, NameId(name.intern(), Id::new())) + ScopedNameId(self.module_name.into(), NameId(name.intern(), Id::new())) } } @@ -824,7 +824,8 @@ impl Folder for State { | ExprEnum::Wire(_) | ExprEnum::Reg(_) | ExprEnum::RegSync(_) - | ExprEnum::RegAsync(_) => op.default_fold(self)?, + | ExprEnum::RegAsync(_) + | ExprEnum::FormalInput(_) => op.default_fold(self)?, }; self.module_state_stack .last_mut() diff --git a/crates/fayalite/src/module/transform/visit.rs b/crates/fayalite/src/module/transform/visit.rs index 7b4cd2a..1fd22f8 100644 --- a/crates/fayalite/src/module/transform/visit.rs +++ b/crates/fayalite/src/module/transform/visit.rs @@ -18,7 +18,7 @@ use crate::{ TargetPathTraceAsStringInner, }, }, - formal::FormalKind, + formal::{FormalInput, FormalInputKind, FormalKind}, int::{Bool, SIntType, SIntValue, Size, UIntType, UIntValue}, intern::{Intern, Interned}, memory::{Mem, MemPort, PortKind, PortName, PortType, ReadUnderWrite}, diff --git a/crates/fayalite/src/reg.rs b/crates/fayalite/src/reg.rs index 7f50655..2e4ab16 100644 --- a/crates/fayalite/src/reg.rs +++ b/crates/fayalite/src/reg.rs @@ -79,6 +79,7 @@ impl Reg { if let Some(init) = init { assert_eq!(ty, init.ty(), "register's type must match init type"); } + scoped_name.0.assert_is_name_id(); Self { name: scoped_name, source_location, @@ -94,7 +95,7 @@ impl Reg { self.containing_module_name_id().0 } pub fn containing_module_name_id(&self) -> NameId { - self.name.0 + self.name.0.unwrap_name_id() } pub fn name(&self) -> Interned { self.name_id().0 diff --git a/crates/fayalite/src/vendor/xilinx/yosys_nextpnr_prjxray.rs b/crates/fayalite/src/vendor/xilinx/yosys_nextpnr_prjxray.rs index 2d498e7..0f09979 100644 --- a/crates/fayalite/src/vendor/xilinx/yosys_nextpnr_prjxray.rs +++ b/crates/fayalite/src/vendor/xilinx/yosys_nextpnr_prjxray.rs @@ -595,6 +595,7 @@ impl Visitor for XdcFileWriter { v, instance.source_location(), )? {}, + TargetBase::FormalInput(_) => unreachable!("FormalInput can't be annotated"), } } } diff --git a/crates/fayalite/src/wire.rs b/crates/fayalite/src/wire.rs index a350d9a..d167c5f 100644 --- a/crates/fayalite/src/wire.rs +++ b/crates/fayalite/src/wire.rs @@ -58,11 +58,13 @@ impl Wire { ty: T::from_canonical(ty), } } + #[track_caller] pub fn new_unchecked( scoped_name: ScopedNameId, source_location: SourceLocation, ty: T, ) -> Self { + scoped_name.0.assert_is_name_id(); Self { name: scoped_name, source_location, @@ -76,7 +78,7 @@ impl Wire { self.containing_module_name_id().0 } pub fn containing_module_name_id(&self) -> NameId { - self.name.0 + self.name.0.unwrap_name_id() } pub fn name(&self) -> Interned { self.name_id().0 diff --git a/crates/fayalite/visit_types.json b/crates/fayalite/visit_types.json index f3af962..fd0e9fe 100644 --- a/crates/fayalite/visit_types.json +++ b/crates/fayalite/visit_types.json @@ -1043,6 +1043,16 @@ "fold_where": "T: Fold", "visit_where": "T: Visit" }, + "ops::FormalInputExpr": { + "data": { + "$kind": "Struct", + "$constructor": "ops::FormalInputExpr::new", + "formal_input()": "Visible" + }, + "generics": "", + "fold_where": "T: Fold", + "visit_where": "T: Visit" + }, "BlockId": { "data": { "$kind": "Opaque" @@ -1277,7 +1287,8 @@ "RegSync": "Visible", "RegAsync": "Visible", "Wire": "Visible", - "Instance": "Visible" + "Instance": "Visible", + "FormalInput": "Visible" } }, "TargetChild": { @@ -1349,6 +1360,21 @@ "generics": "", "fold_where": "T: Fold", "visit_where": "T: Visit" + }, + "FormalInput": { + "data": { + "$kind": "Struct", + "$constructor": "FormalInput::new", + "kind": "Visible", + "name_id": "Visible", + "ty": "Visible", + "source_location": "Visible" + } + }, + "FormalInputKind": { + "data": { + "$kind": "Opaque" + } } } } \ No newline at end of file