diff --git a/crates/fayalite/src/expr.rs b/crates/fayalite/src/expr.rs index 607ff1e..5ba6dbf 100644 --- a/crates/fayalite/src/expr.rs +++ b/crates/fayalite/src/expr.rs @@ -111,6 +111,7 @@ expr_enum! { BundleLiteral(ops::BundleLiteral), ArrayLiteral(ops::ArrayLiteral), EnumLiteral(ops::EnumLiteral), + Uninit(ops::Uninit), NotU(ops::NotU), NotS(ops::NotS), NotB(ops::NotB), @@ -697,3 +698,13 @@ pub fn check_match_expr( _check_fn: impl FnOnce(T::MatchVariant, Infallible), ) { } + +pub trait MakeUninitExpr: Type { + fn uninit(self) -> Expr; +} + +impl MakeUninitExpr for T { + fn uninit(self) -> Expr { + ops::Uninit::new(self).to_expr() + } +} diff --git a/crates/fayalite/src/expr/ops.rs b/crates/fayalite/src/expr/ops.rs index e5d3d9b..144a56f 100644 --- a/crates/fayalite/src/expr/ops.rs +++ b/crates/fayalite/src/expr/ops.rs @@ -2529,3 +2529,41 @@ impl ToExpr for CastBitsTo { } } } + +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +pub struct Uninit { + ty: T, +} + +impl Uninit { + #[track_caller] + pub fn new(ty: T) -> Self { + Self { ty } + } + pub fn ty(self) -> T { + self.ty + } +} + +impl ToLiteralBits for Uninit { + fn to_literal_bits(&self) -> Result, NotALiteralExpr> { + Err(NotALiteralExpr) + } +} + +impl_get_target_none!([T: Type] Uninit); + +impl ToExpr for Uninit { + type Type = T; + + fn to_expr(&self) -> Expr { + Expr { + __enum: ExprEnum::Uninit(Uninit { + ty: self.ty.canonical(), + }) + .intern(), + __ty: self.ty, + __flow: Flow::Source, + } + } +} diff --git a/crates/fayalite/src/firrtl.rs b/crates/fayalite/src/firrtl.rs index 5a562c0..dbbdd7d 100644 --- a/crates/fayalite/src/firrtl.rs +++ b/crates/fayalite/src/firrtl.rs @@ -912,6 +912,20 @@ impl<'a> Exporter<'a> { } ident.to_string() } + fn uninit_expr( + &mut self, + expr: ops::Uninit, + definitions: &RcDefinitions, + const_ty: bool, + ) -> String { + let ident = self.module.ns.make_new("_uninit_expr"); + let ty = expr.ty(); + let ty_ident = self.type_state.ty(ty); + let const_ = if const_ty { "const " } else { "" }; + definitions.add_definition_line(format_args!("wire {ident}: {const_}{ty_ident}")); + definitions.add_definition_line(format_args!("invalidate {ident}")); + ident.to_string() + } fn enum_literal_expr( &mut self, expr: ops::EnumLiteral, @@ -1367,6 +1381,7 @@ impl<'a> Exporter<'a> { ExprEnum::EnumLiteral(enum_literal) => { self.enum_literal_expr(enum_literal, definitions, const_ty) } + ExprEnum::Uninit(uninit) => self.uninit_expr(uninit, definitions, const_ty), ExprEnum::NotU(expr) => self.expr_unary("not", expr.arg(), definitions, const_ty), ExprEnum::NotS(expr) => self.expr_unary("not", expr.arg(), definitions, const_ty), ExprEnum::NotB(expr) => self.expr_unary("not", expr.arg(), definitions, const_ty), diff --git a/crates/fayalite/src/module/transform/simplify_enums.rs b/crates/fayalite/src/module/transform/simplify_enums.rs index 91bbba5..d6a9c02 100644 --- a/crates/fayalite/src/module/transform/simplify_enums.rs +++ b/crates/fayalite/src/module/transform/simplify_enums.rs @@ -287,6 +287,7 @@ impl Folder for State { | ExprEnum::BoolLiteral(_) | ExprEnum::BundleLiteral(_) | ExprEnum::ArrayLiteral(_) + | ExprEnum::Uninit(_) | ExprEnum::NotU(_) | ExprEnum::NotS(_) | ExprEnum::NotB(_) diff --git a/crates/fayalite/src/prelude.rs b/crates/fayalite/src/prelude.rs index 6a74e67..b376093 100644 --- a/crates/fayalite/src/prelude.rs +++ b/crates/fayalite/src/prelude.rs @@ -6,7 +6,7 @@ pub use crate::{ cli::Cli, clock::{Clock, ClockDomain, ToClock}, enum_::{HdlNone, HdlOption, HdlSome}, - expr::{CastBitsTo, CastTo, CastToBits, Expr, ReduceBits, ToExpr}, + expr::{CastBitsTo, CastTo, CastToBits, Expr, MakeUninitExpr, ReduceBits, ToExpr}, hdl, hdl_module, int::{Bool, DynSize, IntCmp, KnownSize, SInt, SIntType, Size, UInt, UIntType}, memory::{Mem, MemBuilder, ReadUnderWrite}, diff --git a/crates/fayalite/tests/module.rs b/crates/fayalite/tests/module.rs index 23cd61f..c135f51 100644 --- a/crates/fayalite/tests/module.rs +++ b/crates/fayalite/tests/module.rs @@ -3139,3 +3139,46 @@ circuit check_annotations: %[[ "#, }; } + +#[hdl_module(outline_generated)] +pub fn check_uninit(ty: T) { + #[hdl] + let o: T = m.output(ty); + connect(o, ty.uninit()); +} + +#[test] +fn test_uninit() { + let _n = SourceLocation::normalize_files_for_tests(); + let m = check_uninit((UInt[3], SInt[5], Clock)); + dbg!(m); + #[rustfmt::skip] // work around https://github.com/rust-lang/rustfmt/issues/6161 + assert_export_firrtl! { + m => + "/test/check_uninit.fir": r"FIRRTL version 3.2.0 +circuit check_uninit: + type Ty0 = {`0`: UInt<3>, `1`: SInt<5>, `2`: Clock} + module check_uninit: @[module-XXXXXXXXXX.rs 1:1] + output o: Ty0 @[module-XXXXXXXXXX.rs 2:1] + wire _uninit_expr: Ty0 + invalidate _uninit_expr + connect o, _uninit_expr @[module-XXXXXXXXXX.rs 3:1] +", + }; + let m = check_uninit(Array[HdlOption[()]][3]); + dbg!(m); + #[rustfmt::skip] // work around https://github.com/rust-lang/rustfmt/issues/6161 + assert_export_firrtl! { + m => + "/test/check_uninit_1.fir": r"FIRRTL version 3.2.0 +circuit check_uninit_1: + type Ty0 = {} + type Ty1 = {|HdlNone, HdlSome: Ty0|} + module check_uninit_1: @[module-XXXXXXXXXX.rs 1:1] + output o: Ty1[3] @[module-XXXXXXXXXX.rs 2:1] + wire _uninit_expr: Ty1[3] + invalidate _uninit_expr + connect o, _uninit_expr @[module-XXXXXXXXXX.rs 3:1] +", + }; +} diff --git a/crates/fayalite/visit_types.json b/crates/fayalite/visit_types.json index ad9ed25..cbaae05 100644 --- a/crates/fayalite/visit_types.json +++ b/crates/fayalite/visit_types.json @@ -283,6 +283,16 @@ "fold_where": "T: Fold", "visit_where": "T: Visit" }, + "ops::Uninit": { + "data": { + "$kind": "Struct", + "$constructor": "ops::Uninit::new", + "ty()": "Visible" + }, + "generics": "", + "fold_where": "T: Fold", + "visit_where": "T: Visit" + }, "ops::NotU": { "data": { "$kind": "Struct",