From cdd84953d076cd9a83db50e9d11a63b6181b0976 Mon Sep 17 00:00:00 2001 From: Jacob Lifshay Date: Thu, 13 Feb 2025 18:35:30 -0800 Subject: [PATCH 01/38] support unknown trait bounds in type parameters --- .../src/hdl_type_common.rs | 130 +++++++++++++++--- crates/fayalite/tests/module.rs | 6 +- 2 files changed, 119 insertions(+), 17 deletions(-) diff --git a/crates/fayalite-proc-macros-impl/src/hdl_type_common.rs b/crates/fayalite-proc-macros-impl/src/hdl_type_common.rs index 6193dc3..3b2e1ec 100644 --- a/crates/fayalite-proc-macros-impl/src/hdl_type_common.rs +++ b/crates/fayalite-proc-macros-impl/src/hdl_type_common.rs @@ -2069,11 +2069,16 @@ macro_rules! impl_bounds { $( $Variant:ident, )* + $( + #[unknown] + $Unknown:ident, + )? } ) => { #[derive(Clone, Debug)] $vis enum $enum_type { $($Variant(known_items::$Variant),)* + $($Unknown(syn::TypeParamBound),)? } $(impl From for $enum_type { @@ -2086,28 +2091,54 @@ macro_rules! impl_bounds { fn to_tokens(&self, tokens: &mut TokenStream) { match self { $(Self::$Variant(v) => v.to_tokens(tokens),)* + $(Self::$Unknown(v) => v.to_tokens(tokens),)? } } } impl $enum_type { $vis fn parse_path(path: Path) -> Result { + #![allow(unreachable_code)] $(let path = match known_items::$Variant::parse_path(path) { Ok(v) => return Ok(Self::$Variant(v)), Err(path) => path, };)* + $(return Ok(Self::$Unknown(syn::TraitBound { + paren_token: None, + modifier: syn::TraitBoundModifier::None, + lifetimes: None, + path, + }.into()));)? Err(path) } + $vis fn parse_type_param_bound(mut type_param_bound: syn::TypeParamBound) -> Result { + #![allow(unreachable_code)] + if let syn::TypeParamBound::Trait(mut trait_bound) = type_param_bound { + if let syn::TraitBound { + paren_token: _, + modifier: syn::TraitBoundModifier::None, + lifetimes: None, + path: _, + } = trait_bound { + match Self::parse_path(trait_bound.path) { + Ok(retval) => return Ok(retval), + Err(path) => trait_bound.path = path, + } + } + type_param_bound = trait_bound.into(); + } + $(return Ok(Self::$Unknown(type_param_bound));)? + Err(type_param_bound) + } } impl Parse for $enum_type { fn parse(input: ParseStream) -> syn::Result { - Self::parse_path(Path::parse_mod_style(input)?).map_err(|path| { - syn::Error::new_spanned( - path, + Self::parse_type_param_bound(input.parse()?) + .map_err(|type_param_bound| syn::Error::new_spanned( + type_param_bound, format_args!("expected one of: {}", [$(stringify!($Variant)),*].join(", ")), - ) - }) + )) } } @@ -2115,6 +2146,7 @@ macro_rules! impl_bounds { #[allow(non_snake_case)] $vis struct $struct_type { $($vis $Variant: Option,)* + $($vis $Unknown: Vec,)? } impl ToTokens for $struct_type { @@ -2126,42 +2158,63 @@ macro_rules! impl_bounds { separator = Some(::default()); v.to_tokens(tokens); })* + $(for v in &self.$Unknown { + separator.to_tokens(tokens); + separator = Some(::default()); + v.to_tokens(tokens); + })* } } const _: () = { #[derive(Clone, Debug)] - $vis struct Iter($vis $struct_type); + #[allow(non_snake_case)] + $vis struct Iter { + $($Variant: Option,)* + $($Unknown: std::vec::IntoIter,)? + } impl IntoIterator for $struct_type { type Item = $enum_type; type IntoIter = Iter; fn into_iter(self) -> Self::IntoIter { - Iter(self) + Iter { + $($Variant: self.$Variant,)* + $($Unknown: self.$Unknown.into_iter(),)? + } } } impl Iterator for Iter { type Item = $enum_type; - fn next(&mut self) -> Option { $( - if let Some(value) = self.0.$Variant.take() { + if let Some(value) = self.$Variant.take() { return Some($enum_type::$Variant(value)); } )* + $( + if let Some(value) = self.$Unknown.next() { + return Some($enum_type::$Unknown(value)); + } + )? None } #[allow(unused_mut, unused_variables)] fn fold B>(mut self, mut init: B, mut f: F) -> B { $( - if let Some(value) = self.0.$Variant.take() { + if let Some(value) = self.$Variant.take() { init = f(init, $enum_type::$Variant(value)); } )* + $( + if let Some(value) = self.$Unknown.next() { + init = f(init, $enum_type::$Unknown(value)); + } + )? init } } @@ -2173,6 +2226,9 @@ macro_rules! impl_bounds { $($enum_type::$Variant(v) => { self.$Variant = Some(v); })* + $($enum_type::$Unknown(v) => { + self.$Unknown.push(v); + })? }); } } @@ -2191,6 +2247,7 @@ macro_rules! impl_bounds { $(if let Some(v) = v.$Variant { self.$Variant = Some(v); })* + $(self.$Unknown.extend(v.$Unknown);)* }); } } @@ -2244,6 +2301,8 @@ impl_bounds! { Size, StaticType, Type, + #[unknown] + Unknown, } } @@ -2257,6 +2316,8 @@ impl_bounds! { ResetType, StaticType, Type, + #[unknown] + Unknown, } } @@ -2270,6 +2331,7 @@ impl From for ParsedBound { ParsedTypeBound::ResetType(v) => ParsedBound::ResetType(v), ParsedTypeBound::StaticType(v) => ParsedBound::StaticType(v), ParsedTypeBound::Type(v) => ParsedBound::Type(v), + ParsedTypeBound::Unknown(v) => ParsedBound::Unknown(v), } } } @@ -2284,6 +2346,7 @@ impl From for ParsedBounds { ResetType, StaticType, Type, + Unknown, } = value; Self { BoolOrIntType, @@ -2295,6 +2358,7 @@ impl From for ParsedBounds { Size: None, StaticType, Type, + Unknown, } } } @@ -2330,6 +2394,7 @@ impl ParsedTypeBound { ParsedTypeBound::Type(known_items::Type(span)), ]), Self::Type(v) => ParsedTypeBounds::from_iter([ParsedTypeBound::from(v)]), + Self::Unknown(v) => ParsedTypeBounds::from_iter([ParsedTypeBound::Unknown(v)]), } } } @@ -2364,6 +2429,7 @@ impl From for ParsedBounds { Size, StaticType: None, Type: None, + Unknown: vec![], } } } @@ -2391,6 +2457,7 @@ impl ParsedBounds { fn categorize(self, errors: &mut Errors, span: Span) -> ParsedBoundsCategory { let mut type_bounds = None; let mut size_type_bounds = None; + let mut unknown_bounds = vec![]; self.into_iter().for_each(|bound| match bound.categorize() { ParsedBoundCategory::Type(bound) => { type_bounds @@ -2402,15 +2469,37 @@ impl ParsedBounds { .get_or_insert_with(ParsedSizeTypeBounds::default) .extend([bound]); } + ParsedBoundCategory::Unknown(bound) => unknown_bounds.push(bound), }); - match (type_bounds, size_type_bounds) { - (None, None) => ParsedBoundsCategory::Type(ParsedTypeBounds { + match (type_bounds, size_type_bounds, unknown_bounds.is_empty()) { + (None, None, true) => ParsedBoundsCategory::Type(ParsedTypeBounds { Type: Some(known_items::Type(span)), ..Default::default() }), - (None, Some(bounds)) => ParsedBoundsCategory::SizeType(bounds), - (Some(bounds), None) => ParsedBoundsCategory::Type(bounds), - (Some(type_bounds), Some(size_type_bounds)) => { + (None, None, false) => { + errors.error( + unknown_bounds.remove(0), + "unknown bounds: must use at least one known bound (such as `Type`) with any unknown bounds", + ); + ParsedBoundsCategory::Type(ParsedTypeBounds { + Unknown: unknown_bounds, + ..Default::default() + }) + } + (None, Some(bounds), true) => ParsedBoundsCategory::SizeType(bounds), + (None, Some(bounds), false) => { + // TODO: implement + errors.error( + unknown_bounds.remove(0), + "unknown bounds with `Size` bounds are not implemented", + ); + ParsedBoundsCategory::SizeType(bounds) + } + (Some(bounds), None, _) => ParsedBoundsCategory::Type(ParsedTypeBounds { + Unknown: unknown_bounds, + ..bounds + }), + (Some(type_bounds), Some(size_type_bounds), _) => { errors.error( size_type_bounds .Size @@ -2427,6 +2516,7 @@ impl ParsedBounds { pub(crate) enum ParsedBoundCategory { Type(ParsedTypeBound), SizeType(ParsedSizeTypeBound), + Unknown(syn::TypeParamBound), } impl ParsedBound { @@ -2441,12 +2531,14 @@ impl ParsedBound { Self::Size(v) => ParsedBoundCategory::SizeType(ParsedSizeTypeBound::Size(v)), Self::StaticType(v) => ParsedBoundCategory::Type(ParsedTypeBound::StaticType(v)), Self::Type(v) => ParsedBoundCategory::Type(ParsedTypeBound::Type(v)), + Self::Unknown(v) => ParsedBoundCategory::Unknown(v), } } fn implied_bounds(self) -> ParsedBounds { match self.categorize() { ParsedBoundCategory::Type(v) => v.implied_bounds().into(), ParsedBoundCategory::SizeType(v) => v.implied_bounds().into(), + ParsedBoundCategory::Unknown(v) => ParsedBounds::from_iter([ParsedBound::Unknown(v)]), } } } @@ -3325,7 +3417,7 @@ impl ParsedGenerics { | ParsedTypeBound::EnumType(_) | ParsedTypeBound::IntType(_) | ParsedTypeBound::ResetType(_) => { - errors.error(bound, "bound on mask type not implemented"); + errors.error(bound, "bounds on mask types are not implemented"); } ParsedTypeBound::StaticType(bound) => { if bounds.StaticType.is_none() { @@ -3337,6 +3429,12 @@ impl ParsedGenerics { } } ParsedTypeBound::Type(_) => {} + ParsedTypeBound::Unknown(_) => { + errors.error( + bound, + "unknown bounds on mask types are not implemented", + ); + } } } bounds.add_implied_bounds(); diff --git a/crates/fayalite/tests/module.rs b/crates/fayalite/tests/module.rs index 49f5689..2f93fa5 100644 --- a/crates/fayalite/tests/module.rs +++ b/crates/fayalite/tests/module.rs @@ -191,10 +191,14 @@ circuit check_array_repeat: }; } +pub trait UnknownTrait {} + +impl UnknownTrait for T {} + #[hdl_module(outline_generated)] pub fn check_skipped_generics(v: U) where - T: StaticType, + T: StaticType + UnknownTrait, ConstUsize: KnownSize, U: std::fmt::Display, { From 43797db36eac26681ebcf62d13465cb5557af081 Mon Sep 17 00:00:00 2001 From: Jacob Lifshay Date: Sun, 16 Feb 2025 20:46:54 -0800 Subject: [PATCH 02/38] sort custom keywords --- crates/fayalite-proc-macros-impl/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/fayalite-proc-macros-impl/src/lib.rs b/crates/fayalite-proc-macros-impl/src/lib.rs index 6ba177b..cbd5f4a 100644 --- a/crates/fayalite-proc-macros-impl/src/lib.rs +++ b/crates/fayalite-proc-macros-impl/src/lib.rs @@ -77,8 +77,8 @@ mod kw { custom_keyword!(flip); custom_keyword!(hdl); custom_keyword!(hdl_module); - custom_keyword!(input); custom_keyword!(incomplete_wire); + custom_keyword!(input); custom_keyword!(instance); custom_keyword!(m); custom_keyword!(memory); From 3458c21f442652713f2f531f02d747d43550562c Mon Sep 17 00:00:00 2001 From: Jacob Lifshay Date: Sun, 16 Feb 2025 20:48:16 -0800 Subject: [PATCH 03/38] add #[hdl(cmp_eq)] to implement HdlPartialEq automatically --- .../src/hdl_bundle.rs | 65 +++++++++ .../fayalite-proc-macros-impl/src/hdl_enum.rs | 5 + .../src/hdl_type_alias.rs | 5 + .../src/hdl_type_common.rs | 1 + crates/fayalite-proc-macros-impl/src/lib.rs | 1 + crates/fayalite/src/array.rs | 40 +++++- crates/fayalite/src/bundle.rs | 67 +++++++-- crates/fayalite/src/enum_.rs | 59 +++++++- crates/fayalite/tests/module.rs | 128 +++++++++++++++++- 9 files changed, 351 insertions(+), 20 deletions(-) diff --git a/crates/fayalite-proc-macros-impl/src/hdl_bundle.rs b/crates/fayalite-proc-macros-impl/src/hdl_bundle.rs index 79326e2..b0fe498 100644 --- a/crates/fayalite-proc-macros-impl/src/hdl_bundle.rs +++ b/crates/fayalite-proc-macros-impl/src/hdl_bundle.rs @@ -83,6 +83,7 @@ impl ParsedBundle { custom_bounds, no_static: _, no_runtime_generics: _, + cmp_eq: _, } = options.body; let mut fields = match fields { syn::Fields::Named(fields) => fields, @@ -437,6 +438,7 @@ impl ToTokens for ParsedBundle { custom_bounds: _, no_static, no_runtime_generics, + cmp_eq, } = &options.body; let target = get_target(target, ident); let mut item_attrs = attrs.clone(); @@ -765,6 +767,69 @@ impl ToTokens for ParsedBundle { } } .to_tokens(tokens); + if let Some((cmp_eq,)) = cmp_eq { + let mut where_clause = + Generics::from(generics) + .where_clause + .unwrap_or_else(|| syn::WhereClause { + where_token: Token![where](span), + predicates: Punctuated::new(), + }); + let mut fields_cmp_eq = vec![]; + let mut fields_cmp_ne = vec![]; + for field in fields.named() { + let field_ident = field.ident(); + let field_ty = field.ty(); + where_clause + .predicates + .push(parse_quote_spanned! {cmp_eq.span=> + #field_ty: ::fayalite::expr::ops::ExprPartialEq<#field_ty> + }); + fields_cmp_eq.push(quote_spanned! {span=> + ::fayalite::expr::ops::ExprPartialEq::cmp_eq(__lhs.#field_ident, __rhs.#field_ident) + }); + fields_cmp_ne.push(quote_spanned! {span=> + ::fayalite::expr::ops::ExprPartialEq::cmp_ne(__lhs.#field_ident, __rhs.#field_ident) + }); + } + let cmp_eq_body; + let cmp_ne_body; + if fields_len == 0 { + cmp_eq_body = quote_spanned! {span=> + ::fayalite::expr::ToExpr::to_expr(&true) + }; + cmp_ne_body = quote_spanned! {span=> + ::fayalite::expr::ToExpr::to_expr(&false) + }; + } else { + cmp_eq_body = quote_spanned! {span=> + #(#fields_cmp_eq)&* + }; + cmp_ne_body = quote_spanned! {span=> + #(#fields_cmp_ne)|* + }; + }; + quote_spanned! {span=> + #[automatically_derived] + impl #impl_generics ::fayalite::expr::ops::ExprPartialEq for #target #type_generics + #where_clause + { + fn cmp_eq( + __lhs: ::fayalite::expr::Expr, + __rhs: ::fayalite::expr::Expr, + ) -> ::fayalite::expr::Expr<::fayalite::int::Bool> { + #cmp_eq_body + } + fn cmp_ne( + __lhs: ::fayalite::expr::Expr, + __rhs: ::fayalite::expr::Expr, + ) -> ::fayalite::expr::Expr<::fayalite::int::Bool> { + #cmp_ne_body + } + } + } + .to_tokens(tokens); + } if let (None, MaybeParsed::Parsed(generics)) = (no_static, &self.generics) { let static_generics = generics.clone().for_static_type(); let (static_impl_generics, static_type_generics, static_where_clause) = diff --git a/crates/fayalite-proc-macros-impl/src/hdl_enum.rs b/crates/fayalite-proc-macros-impl/src/hdl_enum.rs index 1d16177..9174566 100644 --- a/crates/fayalite-proc-macros-impl/src/hdl_enum.rs +++ b/crates/fayalite-proc-macros-impl/src/hdl_enum.rs @@ -155,7 +155,11 @@ impl ParsedEnum { custom_bounds, no_static: _, no_runtime_generics: _, + cmp_eq, } = options.body; + if let Some((cmp_eq,)) = cmp_eq { + errors.error(cmp_eq, "#[hdl(cmp_eq)] is not yet implemented for enums"); + } attrs.retain(|attr| { if attr.path().is_ident("repr") { errors.error(attr, "#[repr] is not supported on #[hdl] enums"); @@ -211,6 +215,7 @@ impl ToTokens for ParsedEnum { custom_bounds: _, no_static, no_runtime_generics, + cmp_eq: _, // TODO: implement cmp_eq for enums } = &options.body; let target = get_target(target, ident); let mut struct_attrs = attrs.clone(); diff --git a/crates/fayalite-proc-macros-impl/src/hdl_type_alias.rs b/crates/fayalite-proc-macros-impl/src/hdl_type_alias.rs index e5d5f7b..97501e7 100644 --- a/crates/fayalite-proc-macros-impl/src/hdl_type_alias.rs +++ b/crates/fayalite-proc-macros-impl/src/hdl_type_alias.rs @@ -49,10 +49,14 @@ impl ParsedTypeAlias { custom_bounds, no_static, no_runtime_generics: _, + cmp_eq, } = options.body; if let Some((no_static,)) = no_static { errors.error(no_static, "no_static is not valid on type aliases"); } + if let Some((cmp_eq,)) = cmp_eq { + errors.error(cmp_eq, "cmp_eq is not valid on type aliases"); + } let generics = if custom_bounds.is_some() { MaybeParsed::Unrecognized(generics) } else if let Some(generics) = errors.ok(ParsedGenerics::parse(&mut generics)) { @@ -95,6 +99,7 @@ impl ToTokens for ParsedTypeAlias { custom_bounds: _, no_static: _, no_runtime_generics, + cmp_eq: _, } = &options.body; let target = get_target(target, ident); let mut type_attrs = attrs.clone(); diff --git a/crates/fayalite-proc-macros-impl/src/hdl_type_common.rs b/crates/fayalite-proc-macros-impl/src/hdl_type_common.rs index 3b2e1ec..2da0915 100644 --- a/crates/fayalite-proc-macros-impl/src/hdl_type_common.rs +++ b/crates/fayalite-proc-macros-impl/src/hdl_type_common.rs @@ -26,6 +26,7 @@ crate::options! { CustomBounds(custom_bounds), NoStatic(no_static), NoRuntimeGenerics(no_runtime_generics), + CmpEq(cmp_eq), } } diff --git a/crates/fayalite-proc-macros-impl/src/lib.rs b/crates/fayalite-proc-macros-impl/src/lib.rs index cbd5f4a..5fe3ae8 100644 --- a/crates/fayalite-proc-macros-impl/src/lib.rs +++ b/crates/fayalite-proc-macros-impl/src/lib.rs @@ -72,6 +72,7 @@ mod kw { custom_keyword!(cfg); custom_keyword!(cfg_attr); custom_keyword!(clock_domain); + custom_keyword!(cmp_eq); custom_keyword!(connect_inexact); custom_keyword!(custom_bounds); custom_keyword!(flip); diff --git a/crates/fayalite/src/array.rs b/crates/fayalite/src/array.rs index f617f91..647b2e2 100644 --- a/crates/fayalite/src/array.rs +++ b/crates/fayalite/src/array.rs @@ -2,8 +2,11 @@ // See Notices.txt for copyright information use crate::{ - expr::{ops::ArrayIndex, Expr, ToExpr}, - int::{DynSize, KnownSize, Size, SizeType, DYN_SIZE}, + expr::{ + ops::{ArrayIndex, ArrayLiteral, ExprPartialEq}, + CastToBits, Expr, HdlPartialEq, ReduceBits, ToExpr, + }, + int::{Bool, DynSize, KnownSize, Size, SizeType, DYN_SIZE}, intern::{Intern, Interned, LazyInterned}, module::transform::visit::{Fold, Folder, Visit, Visitor}, source_location::SourceLocation, @@ -218,3 +221,36 @@ impl Index for ArrayWithoutLen { Interned::into_inner(Intern::intern_sized(ArrayType::new(self.element, len))) } } + +impl ExprPartialEq> for ArrayType +where + Lhs: ExprPartialEq, +{ + fn cmp_eq(lhs: Expr, rhs: Expr>) -> Expr { + let lhs_ty = Expr::ty(lhs); + let rhs_ty = Expr::ty(rhs); + assert_eq!(lhs_ty.len(), rhs_ty.len()); + ArrayLiteral::::new( + Bool, + (0..lhs_ty.len()) + .map(|i| Expr::canonical(lhs[i].cmp_eq(rhs[i]))) + .collect(), + ) + .cast_to_bits() + .all_one_bits() + } + + fn cmp_ne(lhs: Expr, rhs: Expr>) -> Expr { + let lhs_ty = Expr::ty(lhs); + let rhs_ty = Expr::ty(rhs); + assert_eq!(lhs_ty.len(), rhs_ty.len()); + ArrayLiteral::::new( + Bool, + (0..lhs_ty.len()) + .map(|i| Expr::canonical(lhs[i].cmp_ne(rhs[i]))) + .collect(), + ) + .cast_to_bits() + .any_one_bits() + } +} diff --git a/crates/fayalite/src/bundle.rs b/crates/fayalite/src/bundle.rs index 995510e..9807b92 100644 --- a/crates/fayalite/src/bundle.rs +++ b/crates/fayalite/src/bundle.rs @@ -2,7 +2,11 @@ // See Notices.txt for copyright information use crate::{ - expr::{ops::BundleLiteral, Expr, ToExpr}, + expr::{ + ops::{ArrayLiteral, BundleLiteral, ExprPartialEq}, + CastToBits, Expr, ReduceBits, ToExpr, + }, + int::{Bool, DynSize}, intern::{Intern, Interned}, sim::{SimValue, ToSimValue}, source_location::SourceLocation, @@ -325,7 +329,19 @@ macro_rules! impl_tuple_builder_fields { } macro_rules! impl_tuples { - ([$({#[num = $num:literal, field = $field:ident, ty = $ty_var:ident: $Ty:ident] $var:ident: $T:ident})*] []) => { + ( + [$({ + #[ + num = $num:tt, + field = $field:ident, + ty = $ty_var:ident: $Ty:ident, + lhs = $lhs_var:ident: $Lhs:ident, + rhs = $rhs_var:ident: $Rhs:ident + ] + $var:ident: $T:ident + })*] + [] + ) => { impl_tuple_builder_fields! { {} [$({ @@ -498,6 +514,29 @@ macro_rules! impl_tuples { Self::into_sim_value(*self, ty) } } + impl<$($Lhs: Type + ExprPartialEq<$Rhs>, $Rhs: Type,)*> ExprPartialEq<($($Rhs,)*)> for ($($Lhs,)*) { + fn cmp_eq(lhs: Expr, rhs: Expr<($($Rhs,)*)>) -> Expr { + let ($($lhs_var,)*) = *lhs; + let ($($rhs_var,)*) = *rhs; + ArrayLiteral::::new( + Bool, + FromIterator::from_iter([$(Expr::canonical(ExprPartialEq::cmp_eq($lhs_var, $rhs_var)),)*]), + ) + .cast_to_bits() + .all_one_bits() + } + + fn cmp_ne(lhs: Expr, rhs: Expr<($($Rhs,)*)>) -> Expr { + let ($($lhs_var,)*) = *lhs; + let ($($rhs_var,)*) = *rhs; + ArrayLiteral::::new( + Bool, + FromIterator::from_iter([$(Expr::canonical(ExprPartialEq::cmp_ne($lhs_var, $rhs_var)),)*]), + ) + .cast_to_bits() + .any_one_bits() + } + } }; ([$($lhs:tt)*] [$rhs_first:tt $($rhs:tt)*]) => { impl_tuples!([$($lhs)*] []); @@ -507,18 +546,18 @@ macro_rules! impl_tuples { impl_tuples! { [] [ - {#[num = 0, field = field_0, ty = ty0: Ty0] v0: T0} - {#[num = 1, field = field_1, ty = ty1: Ty1] v1: T1} - {#[num = 2, field = field_2, ty = ty2: Ty2] v2: T2} - {#[num = 3, field = field_3, ty = ty3: Ty3] v3: T3} - {#[num = 4, field = field_4, ty = ty4: Ty4] v4: T4} - {#[num = 5, field = field_5, ty = ty5: Ty5] v5: T5} - {#[num = 6, field = field_6, ty = ty6: Ty6] v6: T6} - {#[num = 7, field = field_7, ty = ty7: Ty7] v7: T7} - {#[num = 8, field = field_8, ty = ty8: Ty8] v8: T8} - {#[num = 9, field = field_9, ty = ty9: Ty9] v9: T9} - {#[num = 10, field = field_10, ty = ty10: Ty10] v10: T10} - {#[num = 11, field = field_11, ty = ty11: Ty11] v11: T11} + {#[num = 0, field = field_0, ty = ty0: Ty0, lhs = lhs0: Lhs0, rhs = rhs0: Rhs0] v0: T0} + {#[num = 1, field = field_1, ty = ty1: Ty1, lhs = lhs1: Lhs1, rhs = rhs1: Rhs1] v1: T1} + {#[num = 2, field = field_2, ty = ty2: Ty2, lhs = lhs2: Lhs2, rhs = rhs2: Rhs2] v2: T2} + {#[num = 3, field = field_3, ty = ty3: Ty3, lhs = lhs3: Lhs3, rhs = rhs3: Rhs3] v3: T3} + {#[num = 4, field = field_4, ty = ty4: Ty4, lhs = lhs4: Lhs4, rhs = rhs4: Rhs4] v4: T4} + {#[num = 5, field = field_5, ty = ty5: Ty5, lhs = lhs5: Lhs5, rhs = rhs5: Rhs5] v5: T5} + {#[num = 6, field = field_6, ty = ty6: Ty6, lhs = lhs6: Lhs6, rhs = rhs6: Rhs6] v6: T6} + {#[num = 7, field = field_7, ty = ty7: Ty7, lhs = lhs7: Lhs7, rhs = rhs7: Rhs7] v7: T7} + {#[num = 8, field = field_8, ty = ty8: Ty8, lhs = lhs8: Lhs8, rhs = rhs8: Rhs8] v8: T8} + {#[num = 9, field = field_9, ty = ty9: Ty9, lhs = lhs9: Lhs9, rhs = rhs9: Rhs9] v9: T9} + {#[num = 10, field = field_10, ty = ty10: Ty10, lhs = lhs10: Lhs10, rhs = rhs10: Rhs10] v10: T10} + {#[num = 11, field = field_11, ty = ty11: Ty11, lhs = lhs11: Lhs11, rhs = rhs11: Rhs11] v11: T11} ] } diff --git a/crates/fayalite/src/enum_.rs b/crates/fayalite/src/enum_.rs index 2ed0b8e..70c58c0 100644 --- a/crates/fayalite/src/enum_.rs +++ b/crates/fayalite/src/enum_.rs @@ -2,7 +2,10 @@ // See Notices.txt for copyright information use crate::{ - expr::{ops::VariantAccess, Expr, ToExpr}, + expr::{ + ops::{ExprPartialEq, VariantAccess}, + Expr, ToExpr, + }, hdl, int::Bool, intern::{Intern, Interned}, @@ -360,6 +363,60 @@ pub enum HdlOption { HdlSome(T), } +impl, Rhs: Type> ExprPartialEq> for HdlOption { + #[hdl] + fn cmp_eq(lhs: Expr, rhs: Expr>) -> Expr { + #[hdl] + let cmp_eq = wire(); + #[hdl] + match lhs { + HdlSome(lhs) => + { + #[hdl] + match rhs { + HdlSome(rhs) => connect(cmp_eq, ExprPartialEq::cmp_eq(lhs, rhs)), + HdlNone => connect(cmp_eq, false), + } + } + HdlNone => + { + #[hdl] + match rhs { + HdlSome(_) => connect(cmp_eq, false), + HdlNone => connect(cmp_eq, true), + } + } + } + cmp_eq + } + + #[hdl] + fn cmp_ne(lhs: Expr, rhs: Expr>) -> Expr { + #[hdl] + let cmp_ne = wire(); + #[hdl] + match lhs { + HdlSome(lhs) => + { + #[hdl] + match rhs { + HdlSome(rhs) => connect(cmp_ne, ExprPartialEq::cmp_ne(lhs, rhs)), + HdlNone => connect(cmp_ne, true), + } + } + HdlNone => + { + #[hdl] + match rhs { + HdlSome(_) => connect(cmp_ne, true), + HdlNone => connect(cmp_ne, false), + } + } + } + cmp_ne + } +} + #[allow(non_snake_case)] pub fn HdlNone() -> Expr> { HdlOption[T::TYPE].HdlNone() diff --git a/crates/fayalite/tests/module.rs b/crates/fayalite/tests/module.rs index 2f93fa5..49b226a 100644 --- a/crates/fayalite/tests/module.rs +++ b/crates/fayalite/tests/module.rs @@ -380,18 +380,18 @@ circuit check_written_inside_both_if_else: }; } -#[hdl(outline_generated)] +#[hdl(outline_generated, cmp_eq)] pub struct TestStruct { pub a: T, pub b: UInt<8>, } -#[hdl(outline_generated)] +#[hdl(outline_generated, cmp_eq)] pub struct TestStruct2 { pub v: UInt<8>, } -#[hdl(outline_generated)] +#[hdl(outline_generated, cmp_eq)] pub struct TestStruct3 {} #[hdl_module(outline_generated)] @@ -4425,3 +4425,125 @@ circuit check_let_patterns: ", }; } + +#[hdl_module(outline_generated)] +pub fn check_struct_cmp_eq() { + #[hdl] + let tuple_lhs: (UInt<1>, SInt<1>, Bool) = m.input(); + #[hdl] + let tuple_rhs: (UInt<1>, SInt<1>, Bool) = m.input(); + #[hdl] + let tuple_cmp_eq: Bool = m.output(); + connect(tuple_cmp_eq, tuple_lhs.cmp_eq(tuple_rhs)); + #[hdl] + let tuple_cmp_ne: Bool = m.output(); + connect(tuple_cmp_ne, tuple_lhs.cmp_ne(tuple_rhs)); + + #[hdl] + let test_struct_lhs: TestStruct> = m.input(); + #[hdl] + let test_struct_rhs: TestStruct> = m.input(); + #[hdl] + let test_struct_cmp_eq: Bool = m.output(); + connect(test_struct_cmp_eq, test_struct_lhs.cmp_eq(test_struct_rhs)); + #[hdl] + let test_struct_cmp_ne: Bool = m.output(); + connect(test_struct_cmp_ne, test_struct_lhs.cmp_ne(test_struct_rhs)); + + #[hdl] + let test_struct_2_lhs: TestStruct2 = m.input(); + #[hdl] + let test_struct_2_rhs: TestStruct2 = m.input(); + #[hdl] + let test_struct_2_cmp_eq: Bool = m.output(); + connect( + test_struct_2_cmp_eq, + test_struct_2_lhs.cmp_eq(test_struct_2_rhs), + ); + #[hdl] + let test_struct_2_cmp_ne: Bool = m.output(); + connect( + test_struct_2_cmp_ne, + test_struct_2_lhs.cmp_ne(test_struct_2_rhs), + ); + + #[hdl] + let test_struct_3_lhs: TestStruct3 = m.input(); + #[hdl] + let test_struct_3_rhs: TestStruct3 = m.input(); + #[hdl] + let test_struct_3_cmp_eq: Bool = m.output(); + connect( + test_struct_3_cmp_eq, + test_struct_3_lhs.cmp_eq(test_struct_3_rhs), + ); + #[hdl] + let test_struct_3_cmp_ne: Bool = m.output(); + connect( + test_struct_3_cmp_ne, + test_struct_3_lhs.cmp_ne(test_struct_3_rhs), + ); +} + +#[test] +fn test_struct_cmp_eq() { + let _n = SourceLocation::normalize_files_for_tests(); + let m = check_struct_cmp_eq(); + dbg!(m); + #[rustfmt::skip] // work around https://github.com/rust-lang/rustfmt/issues/6161 + assert_export_firrtl! { + m => + "/test/check_struct_cmp_eq.fir": r"FIRRTL version 3.2.0 +circuit check_struct_cmp_eq: + type Ty0 = {`0`: UInt<1>, `1`: SInt<1>, `2`: UInt<1>} + type Ty1 = {a: SInt<8>, b: UInt<8>} + type Ty2 = {v: UInt<8>} + type Ty3 = {} + module check_struct_cmp_eq: @[module-XXXXXXXXXX.rs 1:1] + input tuple_lhs: Ty0 @[module-XXXXXXXXXX.rs 2:1] + input tuple_rhs: Ty0 @[module-XXXXXXXXXX.rs 3:1] + output tuple_cmp_eq: UInt<1> @[module-XXXXXXXXXX.rs 4:1] + output tuple_cmp_ne: UInt<1> @[module-XXXXXXXXXX.rs 6:1] + input test_struct_lhs: Ty1 @[module-XXXXXXXXXX.rs 8:1] + input test_struct_rhs: Ty1 @[module-XXXXXXXXXX.rs 9:1] + output test_struct_cmp_eq: UInt<1> @[module-XXXXXXXXXX.rs 10:1] + output test_struct_cmp_ne: UInt<1> @[module-XXXXXXXXXX.rs 12:1] + input test_struct_2_lhs: Ty2 @[module-XXXXXXXXXX.rs 14:1] + input test_struct_2_rhs: Ty2 @[module-XXXXXXXXXX.rs 15:1] + output test_struct_2_cmp_eq: UInt<1> @[module-XXXXXXXXXX.rs 16:1] + output test_struct_2_cmp_ne: UInt<1> @[module-XXXXXXXXXX.rs 18:1] + input test_struct_3_lhs: Ty3 @[module-XXXXXXXXXX.rs 20:1] + input test_struct_3_rhs: Ty3 @[module-XXXXXXXXXX.rs 21:1] + output test_struct_3_cmp_eq: UInt<1> @[module-XXXXXXXXXX.rs 22:1] + output test_struct_3_cmp_ne: UInt<1> @[module-XXXXXXXXXX.rs 24:1] + wire _array_literal_expr: UInt<1>[3] + connect _array_literal_expr[0], eq(tuple_lhs.`0`, tuple_rhs.`0`) + connect _array_literal_expr[1], eq(tuple_lhs.`1`, tuple_rhs.`1`) + connect _array_literal_expr[2], eq(tuple_lhs.`2`, tuple_rhs.`2`) + wire _cast_array_to_bits_expr: UInt<1>[3] + connect _cast_array_to_bits_expr[0], _array_literal_expr[0] + connect _cast_array_to_bits_expr[1], _array_literal_expr[1] + connect _cast_array_to_bits_expr[2], _array_literal_expr[2] + wire _cast_to_bits_expr: UInt<3> + connect _cast_to_bits_expr, cat(_cast_array_to_bits_expr[2], cat(_cast_array_to_bits_expr[1], _cast_array_to_bits_expr[0])) + connect tuple_cmp_eq, andr(_cast_to_bits_expr) @[module-XXXXXXXXXX.rs 5:1] + wire _array_literal_expr_1: UInt<1>[3] + connect _array_literal_expr_1[0], neq(tuple_lhs.`0`, tuple_rhs.`0`) + connect _array_literal_expr_1[1], neq(tuple_lhs.`1`, tuple_rhs.`1`) + connect _array_literal_expr_1[2], neq(tuple_lhs.`2`, tuple_rhs.`2`) + wire _cast_array_to_bits_expr_1: UInt<1>[3] + connect _cast_array_to_bits_expr_1[0], _array_literal_expr_1[0] + connect _cast_array_to_bits_expr_1[1], _array_literal_expr_1[1] + connect _cast_array_to_bits_expr_1[2], _array_literal_expr_1[2] + wire _cast_to_bits_expr_1: UInt<3> + connect _cast_to_bits_expr_1, cat(_cast_array_to_bits_expr_1[2], cat(_cast_array_to_bits_expr_1[1], _cast_array_to_bits_expr_1[0])) + connect tuple_cmp_ne, orr(_cast_to_bits_expr_1) @[module-XXXXXXXXXX.rs 7:1] + connect test_struct_cmp_eq, and(eq(test_struct_lhs.a, test_struct_rhs.a), eq(test_struct_lhs.b, test_struct_rhs.b)) @[module-XXXXXXXXXX.rs 11:1] + connect test_struct_cmp_ne, or(neq(test_struct_lhs.a, test_struct_rhs.a), neq(test_struct_lhs.b, test_struct_rhs.b)) @[module-XXXXXXXXXX.rs 13:1] + connect test_struct_2_cmp_eq, eq(test_struct_2_lhs.v, test_struct_2_rhs.v) @[module-XXXXXXXXXX.rs 17:1] + connect test_struct_2_cmp_ne, neq(test_struct_2_lhs.v, test_struct_2_rhs.v) @[module-XXXXXXXXXX.rs 19:1] + connect test_struct_3_cmp_eq, UInt<1>(0h1) @[module-XXXXXXXXXX.rs 23:1] + connect test_struct_3_cmp_ne, UInt<1>(0h0) @[module-XXXXXXXXXX.rs 25:1] +", + }; +} From 60734cc9d170a87496c69a532a029a9ea23e48b8 Mon Sep 17 00:00:00 2001 From: Jacob Lifshay Date: Sun, 2 Mar 2025 17:43:29 -0800 Subject: [PATCH 04/38] switch CI to use mirrors --- .forgejo/workflows/deps.yml | 12 ++++++------ .forgejo/workflows/test.yml | 6 +++--- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/.forgejo/workflows/deps.yml b/.forgejo/workflows/deps.yml index ffaca53..b29723c 100644 --- a/.forgejo/workflows/deps.yml +++ b/.forgejo/workflows/deps.yml @@ -12,10 +12,10 @@ jobs: outputs: cache-primary-key: ${{ steps.restore-deps.outputs.cache-primary-key }} steps: - - uses: https://code.forgejo.org/actions/checkout@v3 + - uses: https://git.libre-chip.org/mirrors/checkout@v3 with: fetch-depth: 0 - - uses: https://code.forgejo.org/actions/cache/restore@v3 + - uses: https://git.libre-chip.org/mirrors/cache/restore@v3 id: restore-deps with: path: deps @@ -58,19 +58,19 @@ jobs: - name: Get SymbiYosys if: steps.restore-deps.outputs.cache-hit != 'true' run: | - git clone --depth=1 --branch=yosys-0.45 https://github.com/YosysHQ/sby.git deps/sby + git clone --depth=1 --branch=yosys-0.45 https://git.libre-chip.org/mirrors/sby deps/sby - name: Build Z3 if: steps.restore-deps.outputs.cache-hit != 'true' run: | - git clone --depth=1 --recursive --branch=z3-4.13.3 https://github.com/Z3Prover/z3.git deps/z3 + git clone --depth=1 --recursive --branch=z3-4.13.3 https://git.libre-chip.org/mirrors/z3 deps/z3 (cd deps/z3; PYTHON=python3 ./configure --prefix=/usr/local) make -C deps/z3/build -j"$(nproc)" - name: Build Yosys if: steps.restore-deps.outputs.cache-hit != 'true' run: | - git clone --depth=1 --recursive --branch=0.45 https://github.com/YosysHQ/yosys.git deps/yosys + git clone --depth=1 --recursive --branch=0.45 https://git.libre-chip.org/mirrors/yosys deps/yosys make -C deps/yosys -j"$(nproc)" - - uses: https://code.forgejo.org/actions/cache/save@v3 + - uses: https://git.libre-chip.org/mirrors/cache/save@v3 if: steps.restore-deps.outputs.cache-hit != 'true' with: path: deps diff --git a/.forgejo/workflows/test.yml b/.forgejo/workflows/test.yml index e83c668..49fb3e4 100644 --- a/.forgejo/workflows/test.yml +++ b/.forgejo/workflows/test.yml @@ -9,7 +9,7 @@ jobs: runs-on: debian-12 needs: deps steps: - - uses: https://code.forgejo.org/actions/checkout@v3 + - uses: https://git.libre-chip.org/mirrors/checkout@v3 with: fetch-depth: 0 - run: | @@ -41,7 +41,7 @@ jobs: curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain 1.82.0 source "$HOME/.cargo/env" echo "$PATH" >> "$GITHUB_PATH" - - uses: https://code.forgejo.org/actions/cache/restore@v3 + - uses: https://git.libre-chip.org/mirrors/cache/restore@v3 with: path: deps key: ${{ needs.deps.outputs.cache-primary-key }} @@ -52,7 +52,7 @@ jobs: make -C deps/yosys install export PATH="$(realpath deps/firtool/bin):$PATH" echo "$PATH" >> "$GITHUB_PATH" - - uses: https://github.com/Swatinem/rust-cache@v2 + - uses: https://git.libre-chip.org/mirrors/rust-cache@v2 with: save-if: ${{ github.ref == 'refs/heads/master' }} - run: cargo test From 50c86e18dc2fda0622b471ddf55a4d28f1471eeb Mon Sep 17 00:00:00 2001 From: Jacob Lifshay Date: Sun, 2 Mar 2025 16:11:05 -0800 Subject: [PATCH 05/38] add Expr>: IntoIterator and Expr>: FromIterator --- crates/fayalite/src/array.rs | 139 ++++++++++++++++++++++++++------ crates/fayalite/src/expr/ops.rs | 44 ++++++++++ 2 files changed, 159 insertions(+), 24 deletions(-) diff --git a/crates/fayalite/src/array.rs b/crates/fayalite/src/array.rs index 647b2e2..0d9b63f 100644 --- a/crates/fayalite/src/array.rs +++ b/crates/fayalite/src/array.rs @@ -3,7 +3,7 @@ use crate::{ expr::{ - ops::{ArrayIndex, ArrayLiteral, ExprPartialEq}, + ops::{ArrayLiteral, ExprFromIterator, ExprIntoIterator, ExprPartialEq}, CastToBits, Expr, HdlPartialEq, ReduceBits, ToExpr, }, int::{Bool, DynSize, KnownSize, Size, SizeType, DYN_SIZE}, @@ -15,7 +15,7 @@ use crate::{ }, util::ConstUsize, }; -use std::ops::Index; +use std::{iter::FusedIterator, ops::Index}; #[derive(Copy, Clone, PartialEq, Eq, Hash)] pub struct ArrayType { @@ -151,10 +151,8 @@ impl Type for ArrayType { this: Expr, source_location: SourceLocation, ) -> Self::MatchVariantsIter { - let base = Expr::as_dyn_array(this); - let base_ty = Expr::ty(base); let _ = source_location; - let retval = Vec::from_iter((0..base_ty.len()).map(|i| ArrayIndex::new(base, i).to_expr())); + let retval = Vec::from_iter(this); std::iter::once(MatchVariantWithoutScope( Len::ArrayMatch::::try_from(retval) .ok() @@ -187,9 +185,7 @@ impl Type for ArrayType { impl TypeWithDeref for ArrayType { fn expr_deref(this: &Expr) -> &Self::MatchVariant { - let base = Expr::as_dyn_array(*this); - let base_ty = Expr::ty(base); - let retval = Vec::from_iter((0..base_ty.len()).map(|i| ArrayIndex::new(base, i).to_expr())); + let retval = Vec::from_iter(*this); Interned::into_inner(Intern::intern_sized( Len::ArrayMatch::::try_from(retval) .ok() @@ -230,27 +226,122 @@ where let lhs_ty = Expr::ty(lhs); let rhs_ty = Expr::ty(rhs); assert_eq!(lhs_ty.len(), rhs_ty.len()); - ArrayLiteral::::new( - Bool, - (0..lhs_ty.len()) - .map(|i| Expr::canonical(lhs[i].cmp_eq(rhs[i]))) - .collect(), - ) - .cast_to_bits() - .all_one_bits() + lhs.into_iter() + .zip(rhs) + .map(|(l, r)| l.cmp_eq(r)) + .collect::>>() + .cast_to_bits() + .all_one_bits() } fn cmp_ne(lhs: Expr, rhs: Expr>) -> Expr { let lhs_ty = Expr::ty(lhs); let rhs_ty = Expr::ty(rhs); assert_eq!(lhs_ty.len(), rhs_ty.len()); - ArrayLiteral::::new( - Bool, - (0..lhs_ty.len()) - .map(|i| Expr::canonical(lhs[i].cmp_ne(rhs[i]))) - .collect(), - ) - .cast_to_bits() - .any_one_bits() + lhs.into_iter() + .zip(rhs) + .map(|(l, r)| l.cmp_ne(r)) + .collect::>>() + .cast_to_bits() + .any_one_bits() + } +} + +impl ExprIntoIterator for ArrayType { + type Item = T; + type ExprIntoIter = ExprArrayIter; + + fn expr_into_iter(e: Expr) -> Self::ExprIntoIter { + ExprArrayIter { + base: e, + indexes: 0..Expr::ty(e).len(), + } + } +} + +#[derive(Clone, Debug)] +pub struct ExprArrayIter { + base: Expr>, + indexes: std::ops::Range, +} + +impl ExprArrayIter { + pub fn base(&self) -> Expr> { + self.base + } + pub fn indexes(&self) -> std::ops::Range { + self.indexes.clone() + } +} + +impl Iterator for ExprArrayIter { + type Item = Expr; + + fn next(&mut self) -> Option { + self.indexes.next().map(|i| self.base[i]) + } + + fn size_hint(&self) -> (usize, Option) { + self.indexes.size_hint() + } + + fn count(self) -> usize { + self.indexes.count() + } + + fn last(mut self) -> Option { + self.next_back() + } + + fn nth(&mut self, n: usize) -> Option { + self.indexes.nth(n).map(|i| self.base[i]) + } + + fn fold(self, init: B, mut f: F) -> B + where + F: FnMut(B, Self::Item) -> B, + { + self.indexes.fold(init, |b, i| f(b, self.base[i])) + } +} + +impl DoubleEndedIterator for ExprArrayIter { + fn next_back(&mut self) -> Option { + self.indexes.next_back().map(|i| self.base[i]) + } + + fn nth_back(&mut self, n: usize) -> Option { + self.indexes.nth_back(n).map(|i| self.base[i]) + } + + fn rfold(self, init: B, mut f: F) -> B + where + F: FnMut(B, Self::Item) -> B, + { + self.indexes.rfold(init, |b, i| f(b, self.base[i])) + } +} + +impl ExactSizeIterator for ExprArrayIter { + fn len(&self) -> usize { + self.indexes.len() + } +} + +impl FusedIterator for ExprArrayIter {} + +impl ExprFromIterator> for Array { + fn expr_from_iter>>(iter: T) -> Expr { + ArrayLiteral::new( + A::TYPE, + iter.into_iter().map(|v| Expr::canonical(v)).collect(), + ) + .to_expr() + } +} + +impl<'a, A: StaticType> ExprFromIterator<&'a Expr> for Array { + fn expr_from_iter>>(iter: T) -> Expr { + iter.into_iter().copied().collect() } } diff --git a/crates/fayalite/src/expr/ops.rs b/crates/fayalite/src/expr/ops.rs index 15c195e..c502fd5 100644 --- a/crates/fayalite/src/expr/ops.rs +++ b/crates/fayalite/src/expr/ops.rs @@ -2708,3 +2708,47 @@ impl ToExpr for Uninit { } } } + +pub trait ExprIntoIterator: Type { + type Item: Type; + type ExprIntoIter: Iterator>; + + fn expr_into_iter(e: Expr) -> Self::ExprIntoIter; +} + +impl IntoIterator for Expr { + type Item = Expr; + type IntoIter = T::ExprIntoIter; + + fn into_iter(self) -> Self::IntoIter { + T::expr_into_iter(self) + } +} + +impl IntoIterator for &'_ Expr { + type Item = Expr; + type IntoIter = T::ExprIntoIter; + + fn into_iter(self) -> Self::IntoIter { + T::expr_into_iter(*self) + } +} + +impl IntoIterator for &'_ mut Expr { + type Item = Expr; + type IntoIter = T::ExprIntoIter; + + fn into_iter(self) -> Self::IntoIter { + T::expr_into_iter(*self) + } +} + +pub trait ExprFromIterator: Type { + fn expr_from_iter>(iter: T) -> Expr; +} + +impl, A> FromIterator for Expr { + fn from_iter>(iter: T) -> Self { + This::expr_from_iter(iter) + } +} From bd75fdfefd642f6dd2210cfb003fa63f9dce114e Mon Sep 17 00:00:00 2001 From: Jacob Lifshay Date: Sun, 2 Mar 2025 23:04:17 -0800 Subject: [PATCH 06/38] add efficient prefix-sums and reductions --- crates/fayalite/src/util.rs | 1 + crates/fayalite/src/util/prefix_sum.rs | 839 +++++++++++++++++++++++++ 2 files changed, 840 insertions(+) create mode 100644 crates/fayalite/src/util/prefix_sum.rs diff --git a/crates/fayalite/src/util.rs b/crates/fayalite/src/util.rs index fadc7af..66fc921 100644 --- a/crates/fayalite/src/util.rs +++ b/crates/fayalite/src/util.rs @@ -29,4 +29,5 @@ pub use misc::{ }; pub mod job_server; +pub mod prefix_sum; pub mod ready_valid; diff --git a/crates/fayalite/src/util/prefix_sum.rs b/crates/fayalite/src/util/prefix_sum.rs new file mode 100644 index 0000000..758d89c --- /dev/null +++ b/crates/fayalite/src/util/prefix_sum.rs @@ -0,0 +1,839 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// See Notices.txt for copyright information + +// code derived from: +// https://web.archive.org/web/20250303054010/https://git.libre-soc.org/?p=nmutil.git;a=blob;f=src/nmutil/prefix_sum.py;hb=effeb28e5848392adddcdad1f6e7a098f2a44c9c + +use crate::intern::{Intern, Interned, Memoize}; +use std::{borrow::Cow, num::NonZeroUsize}; + +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] +pub struct PrefixSumOp { + pub lhs_index: usize, + pub rhs_and_dest_index: NonZeroUsize, + pub row: u32, +} + +#[derive(Clone, PartialEq, Eq, Hash, Debug)] +#[non_exhaustive] +pub struct DiagramConfig { + pub space: Cow<'static, str>, + pub vertical_bar: Cow<'static, str>, + pub plus: Cow<'static, str>, + pub slant: Cow<'static, str>, + pub connect: Cow<'static, str>, + pub no_connect: Cow<'static, str>, + pub padding: usize, +} + +impl DiagramConfig { + pub const fn new() -> Self { + Self { + space: Cow::Borrowed(" "), + vertical_bar: Cow::Borrowed("|"), + plus: Cow::Borrowed("\u{2295}"), // ⊕ + slant: Cow::Borrowed(r"\"), + connect: Cow::Borrowed("\u{25CF}"), // ● + no_connect: Cow::Borrowed("X"), + padding: 1, + } + } + pub fn draw(self, ops: impl IntoIterator, item_count: usize) -> String { + #[derive(Copy, Clone, Debug)] + struct DiagramCell { + slant: bool, + plus: bool, + tee: bool, + } + let mut ops_by_row: Vec> = Vec::new(); + let mut last_row = 0; + ops.into_iter().for_each(|op| { + assert!( + op.lhs_index < op.rhs_and_dest_index.get(), + "invalid PrefixSumOp! lhs_index must be less \ + than rhs_and_dest_index: {op:?}", + ); + assert!( + op.row >= last_row, + "invalid PrefixSumOp! row must \ + not decrease (row last was: {last_row}): {op:?}", + ); + let ops = if op.row > last_row || ops_by_row.is_empty() { + ops_by_row.push(vec![]); + ops_by_row.last_mut().expect("just pushed") + } else { + ops_by_row + .last_mut() + .expect("just checked if ops_by_row is empty") + }; + if let Some(last) = ops.last() { + assert!( + op.rhs_and_dest_index < last.rhs_and_dest_index, + "invalid PrefixSumOp! rhs_and_dest_index must strictly \ + decrease in a row:\nthis op: {op:?}\nlast op: {last:?}", + ); + } + ops.push(op); + last_row = op.row; + }); + let blank_row = || { + vec![ + DiagramCell { + slant: false, + plus: false, + tee: false + }; + item_count + ] + }; + let mut cells = vec![blank_row()]; + for ops in ops_by_row { + let max_distance = ops + .iter() + .map( + |&PrefixSumOp { + lhs_index, + rhs_and_dest_index, + .. + }| { rhs_and_dest_index.get() - lhs_index }, + ) + .max() + .expect("ops is known to be non-empty"); + cells.extend((0..max_distance).map(|_| blank_row())); + for op in ops { + let mut y = cells.len() - 1; + assert!( + op.rhs_and_dest_index.get() < item_count, + "invalid PrefixSumOp! rhs_and_dest_index must be \ + less than item_count ({item_count}): {op:?}", + ); + let mut x = op.rhs_and_dest_index.get(); + cells[y][x].plus = true; + x -= 1; + y -= 1; + while op.lhs_index < x { + cells[y][x].slant = true; + x -= 1; + y -= 1; + } + cells[y][x].tee = true; + } + } + let mut retval = String::new(); + let mut row_text = vec![String::new(); 2 * self.padding + 1]; + for cells_row in cells { + for cell in cells_row { + // top padding + for y in 0..self.padding { + // top left padding + for x in 0..self.padding { + row_text[y] += if x == y && (cell.plus || cell.slant) { + &self.slant + } else { + &self.space + }; + } + // top vertical bar + row_text[y] += &self.vertical_bar; + // top right padding + for _ in 0..self.padding { + row_text[y] += &self.space; + } + } + // center left padding + for _ in 0..self.padding { + row_text[self.padding] += &self.space; + } + // center + row_text[self.padding] += if cell.plus { + &self.plus + } else if cell.tee { + &self.connect + } else if cell.slant { + &self.no_connect + } else { + &self.vertical_bar + }; + // center right padding + for _ in 0..self.padding { + row_text[self.padding] += &self.space; + } + let bottom_padding_start = self.padding + 1; + let bottom_padding_last = self.padding * 2; + // bottom padding + for y in bottom_padding_start..=bottom_padding_last { + // bottom left padding + for _ in 0..self.padding { + row_text[y] += &self.space; + } + // bottom vertical bar + row_text[y] += &self.vertical_bar; + // bottom right padding + for x in bottom_padding_start..=bottom_padding_last { + row_text[y] += if x == y && (cell.tee || cell.slant) { + &self.slant + } else { + &self.space + }; + } + } + } + for line in &mut row_text { + retval += line.trim_end(); + retval += "\n"; + line.clear(); + } + } + retval + } +} + +impl Default for DiagramConfig { + fn default() -> Self { + Self::new() + } +} + +impl PrefixSumOp { + pub fn diagram(ops: impl IntoIterator, item_count: usize) -> String { + Self::diagram_with_config(ops, item_count, DiagramConfig::new()) + } + pub fn diagram_with_config( + ops: impl IntoIterator, + item_count: usize, + config: DiagramConfig, + ) -> String { + config.draw(ops, item_count) + } +} + +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +pub enum PrefixSumAlgorithm { + /// Uses the algorithm from: + /// https://en.wikipedia.org/wiki/Prefix_sum#Algorithm_1:_Shorter_span,_more_parallel + LowLatency, + /// Uses the algorithm from: + /// https://en.wikipedia.org/wiki/Prefix_sum#Algorithm_2:_Work-efficient + WorkEfficient, +} + +impl PrefixSumAlgorithm { + fn ops_impl(self, item_count: usize) -> Vec { + let mut retval = Vec::new(); + let mut distance = 1; + let mut row = 0; + while distance < item_count { + let double_distance = distance + .checked_mul(2) + .expect("prefix-sum item_count is too big"); + let (start, step) = match self { + Self::LowLatency => (distance, 1), + Self::WorkEfficient => (double_distance - 1, double_distance), + }; + for rhs_and_dest_index in (start..item_count).step_by(step).rev() { + let Some(rhs_and_dest_index) = NonZeroUsize::new(rhs_and_dest_index) else { + unreachable!(); + }; + let lhs_index = rhs_and_dest_index.get() - distance; + retval.push(PrefixSumOp { + lhs_index, + rhs_and_dest_index, + row, + }); + } + distance = double_distance; + row += 1; + } + match self { + Self::LowLatency => {} + Self::WorkEfficient => { + distance /= 2; + while distance >= 1 { + let start = distance + .checked_mul(3) + .expect("prefix-sum item_count is too big") + - 1; + for rhs_and_dest_index in (start..item_count).step_by(distance * 2).rev() { + let Some(rhs_and_dest_index) = NonZeroUsize::new(rhs_and_dest_index) else { + unreachable!(); + }; + let lhs_index = rhs_and_dest_index.get() - distance; + retval.push(PrefixSumOp { + lhs_index, + rhs_and_dest_index, + row, + }); + } + row += 1; + distance /= 2; + } + } + } + retval + } + pub fn ops(self, item_count: usize) -> Interned<[PrefixSumOp]> { + #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] + struct MyMemoize(PrefixSumAlgorithm); + impl Memoize for MyMemoize { + type Input = usize; + type InputOwned = usize; + type Output = Interned<[PrefixSumOp]>; + + fn inner(self, item_count: &Self::Input) -> Self::Output { + Intern::intern_owned(self.0.ops_impl(*item_count)) + } + } + MyMemoize(self).get_owned(item_count) + } + pub fn run(self, items: impl IntoIterator, f: impl FnMut(&T, &T) -> T) -> Vec { + let mut items = Vec::from_iter(items); + self.run_on_slice(&mut items, f); + items + } + pub fn run_on_slice(self, items: &mut [T], mut f: impl FnMut(&T, &T) -> T) -> &mut [T] { + self.ops(items.len()).into_iter().for_each( + |PrefixSumOp { + lhs_index, + rhs_and_dest_index, + row: _, + }| { + items[rhs_and_dest_index.get()] = + f(&items[lhs_index], &items[rhs_and_dest_index.get()]); + }, + ); + items + } + pub fn filtered_ops( + self, + item_live_out_flags: impl IntoIterator, + ) -> Vec { + let mut item_live_out_flags = Vec::from_iter(item_live_out_flags); + let prefix_sum_ops = self.ops(item_live_out_flags.len()); + let mut ops_live_flags = vec![false; prefix_sum_ops.len()]; + for ( + op_index, + &PrefixSumOp { + lhs_index, + rhs_and_dest_index, + row: _, + }, + ) in prefix_sum_ops.iter().enumerate().rev() + { + let live = item_live_out_flags[rhs_and_dest_index.get()]; + item_live_out_flags[lhs_index] |= live; + ops_live_flags[op_index] = live; + } + prefix_sum_ops + .into_iter() + .zip(ops_live_flags) + .filter_map(|(op, live)| live.then_some(op)) + .collect() + } + pub fn reduce_ops(self, item_count: usize) -> Interned<[PrefixSumOp]> { + #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] + struct MyMemoize(PrefixSumAlgorithm); + impl Memoize for MyMemoize { + type Input = usize; + type InputOwned = usize; + type Output = Interned<[PrefixSumOp]>; + + fn inner(self, item_count: &Self::Input) -> Self::Output { + let mut item_live_out_flags = vec![false; *item_count]; + let Some(last_item_live_out_flag) = item_live_out_flags.last_mut() else { + return Interned::default(); + }; + *last_item_live_out_flag = true; + Intern::intern_owned(self.0.filtered_ops(item_live_out_flags)) + } + } + MyMemoize(self).get_owned(item_count) + } +} + +pub fn reduce_ops(item_count: usize) -> Interned<[PrefixSumOp]> { + PrefixSumAlgorithm::LowLatency.reduce_ops(item_count) +} + +pub fn reduce(items: impl IntoIterator, mut f: impl FnMut(T, T) -> T) -> Option { + let mut items: Vec<_> = items.into_iter().map(Some).collect(); + for op in reduce_ops(items.len()) { + let (Some(lhs), Some(rhs)) = ( + items[op.lhs_index].take(), + items[op.rhs_and_dest_index.get()].take(), + ) else { + unreachable!(); + }; + items[op.rhs_and_dest_index.get()] = Some(f(lhs, rhs)); + } + items.last_mut().and_then(Option::take) +} + +#[cfg(test)] +mod tests { + use super::*; + + fn input_strings() -> [String; 9] { + std::array::from_fn(|i| String::from_utf8(vec![b'a' + i as u8]).unwrap()) + } + + #[test] + fn test_prefix_sum_strings() { + let input = input_strings(); + let expected: Vec = input + .iter() + .scan(String::new(), |l, r| { + *l += r; + Some(l.clone()) + }) + .collect(); + println!("expected: {expected:?}"); + assert_eq!( + *PrefixSumAlgorithm::WorkEfficient + .run_on_slice(&mut input.clone(), |l, r| l.to_string() + r), + *expected + ); + assert_eq!( + *PrefixSumAlgorithm::LowLatency + .run_on_slice(&mut input.clone(), |l, r| l.to_string() + r), + *expected + ); + } + + #[test] + fn test_reduce_string() { + let input = input_strings(); + let expected = input.clone().into_iter().reduce(|l, r| l + &r); + assert_eq!(reduce(input, |l, r| l + &r), expected); + } + + fn op(lhs_index: usize, rhs_and_dest_index: usize, row: u32) -> PrefixSumOp { + PrefixSumOp { + lhs_index, + rhs_and_dest_index: NonZeroUsize::new(rhs_and_dest_index).expect("should be non-zero"), + row, + } + } + + #[test] + fn test_reduce_ops_9() { + let expected = vec![ + op(7, 8, 0), + op(5, 6, 0), + op(3, 4, 0), + op(1, 2, 0), + op(6, 8, 1), + op(2, 4, 1), + op(4, 8, 2), + op(0, 8, 3), + ]; + println!("expected: {expected:#?}"); + let ops = reduce_ops(9); + println!("ops: {ops:#?}"); + assert_eq!(*ops, *expected); + } + + #[test] + fn test_reduce_ops_8() { + let expected = vec![ + op(6, 7, 0), + op(4, 5, 0), + op(2, 3, 0), + op(0, 1, 0), + op(5, 7, 1), + op(1, 3, 1), + op(3, 7, 2), + ]; + println!("expected: {expected:#?}"); + let ops = reduce_ops(8); + println!("ops: {ops:#?}"); + assert_eq!(*ops, *expected); + } + + #[test] + fn test_count_ones() { + for width in 0..=10u32 { + for v in 0..1u32 << width { + let expected = v.count_ones(); + assert_eq!( + reduce((0..width).map(|i| (v >> i) & 1), |l, r| l + r).unwrap_or(0), + expected, + "v={v:#X}" + ); + } + } + } + + #[track_caller] + fn test_diagram(ops: impl IntoIterator, item_count: usize, expected: &str) { + let text = PrefixSumOp::diagram_with_config( + ops, + item_count, + DiagramConfig { + plus: Cow::Borrowed("@"), + ..Default::default() + }, + ); + println!("text:\n{text}\n"); + assert_eq!(text, expected); + } + + #[test] + fn test_work_efficient_diagram_16() { + let item_count = 16; + test_diagram( + PrefixSumAlgorithm::WorkEfficient.ops(item_count), + item_count, + &r" + | | | | | | | | | | | | | | | | + ● | ● | ● | ● | ● | ● | ● | ● | + |\ | |\ | |\ | |\ | |\ | |\ | |\ | |\ | + | \| | \| | \| | \| | \| | \| | \| | \| + | @ | @ | @ | @ | @ | @ | @ | @ + | |\ | | | |\ | | | |\ | | | |\ | | + | | \| | | | \| | | | \| | | | \| | + | | X | | | X | | | X | | | X | + | | |\ | | | |\ | | | |\ | | | |\ | + | | | \| | | | \| | | | \| | | | \| + | | | @ | | | @ | | | @ | | | @ + | | | |\ | | | | | | | |\ | | | | + | | | | \| | | | | | | | \| | | | + | | | | X | | | | | | | X | | | + | | | | |\ | | | | | | | |\ | | | + | | | | | \| | | | | | | | \| | | + | | | | | X | | | | | | | X | | + | | | | | |\ | | | | | | | |\ | | + | | | | | | \| | | | | | | | \| | + | | | | | | X | | | | | | | X | + | | | | | | |\ | | | | | | | |\ | + | | | | | | | \| | | | | | | | \| + | | | | | | | @ | | | | | | | @ + | | | | | | | |\ | | | | | | | | + | | | | | | | | \| | | | | | | | + | | | | | | | | X | | | | | | | + | | | | | | | | |\ | | | | | | | + | | | | | | | | | \| | | | | | | + | | | | | | | | | X | | | | | | + | | | | | | | | | |\ | | | | | | + | | | | | | | | | | \| | | | | | + | | | | | | | | | | X | | | | | + | | | | | | | | | | |\ | | | | | + | | | | | | | | | | | \| | | | | + | | | | | | | | | | | X | | | | + | | | | | | | | | | | |\ | | | | + | | | | | | | | | | | | \| | | | + | | | | | | | | | | | | X | | | + | | | | | | | | | | | | |\ | | | + | | | | | | | | | | | | | \| | | + | | | | | | | | | | | | | X | | + | | | | | | | | | | | | | |\ | | + | | | | | | | | | | | | | | \| | + | | | | | | | | | | | | | | X | + | | | | | | | | | | | | | | |\ | + | | | | | | | | | | | | | | | \| + | | | | | | | ● | | | | | | | @ + | | | | | | | |\ | | | | | | | | + | | | | | | | | \| | | | | | | | + | | | | | | | | X | | | | | | | + | | | | | | | | |\ | | | | | | | + | | | | | | | | | \| | | | | | | + | | | | | | | | | X | | | | | | + | | | | | | | | | |\ | | | | | | + | | | | | | | | | | \| | | | | | + | | | | | | | | | | X | | | | | + | | | | | | | | | | |\ | | | | | + | | | | | | | | | | | \| | | | | + | | | ● | | | ● | | | @ | | | | + | | | |\ | | | |\ | | | |\ | | | | + | | | | \| | | | \| | | | \| | | | + | | | | X | | | X | | | X | | | + | | | | |\ | | | |\ | | | |\ | | | + | | | | | \| | | | \| | | | \| | | + | ● | ● | @ | ● | @ | ● | @ | | + | |\ | |\ | |\ | |\ | |\ | |\ | |\ | | + | | \| | \| | \| | \| | \| | \| | \| | + | | @ | @ | @ | @ | @ | @ | @ | + | | | | | | | | | | | | | | | | +"[1..], // trim newline at start + ); + } + + #[test] + fn test_low_latency_diagram_16() { + let item_count = 16; + test_diagram( + PrefixSumAlgorithm::LowLatency.ops(item_count), + item_count, + &r" + | | | | | | | | | | | | | | | | + ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● | + |\ |\ |\ |\ |\ |\ |\ |\ |\ |\ |\ |\ |\ |\ |\ | + | \| \| \| \| \| \| \| \| \| \| \| \| \| \| \| + ● @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ + |\ |\ |\ |\ |\ |\ |\ |\ |\ |\ |\ |\ |\ |\ | | + | \| \| \| \| \| \| \| \| \| \| \| \| \| \| | + | X X X X X X X X X X X X X X | + | |\ |\ |\ |\ |\ |\ |\ |\ |\ |\ |\ |\ |\ |\ | + | | \| \| \| \| \| \| \| \| \| \| \| \| \| \| + ● ● @ @ @ @ @ @ @ @ @ @ @ @ @ @ + |\ |\ |\ |\ |\ |\ |\ |\ |\ |\ |\ |\ | | | | + | \| \| \| \| \| \| \| \| \| \| \| \| | | | + | X X X X X X X X X X X X | | | + | |\ |\ |\ |\ |\ |\ |\ |\ |\ |\ |\ |\ | | | + | | \| \| \| \| \| \| \| \| \| \| \| \| | | + | | X X X X X X X X X X X X | | + | | |\ |\ |\ |\ |\ |\ |\ |\ |\ |\ |\ |\ | | + | | | \| \| \| \| \| \| \| \| \| \| \| \| | + | | | X X X X X X X X X X X X | + | | | |\ |\ |\ |\ |\ |\ |\ |\ |\ |\ |\ |\ | + | | | | \| \| \| \| \| \| \| \| \| \| \| \| + ● ● ● ● @ @ @ @ @ @ @ @ @ @ @ @ + |\ |\ |\ |\ |\ |\ |\ |\ | | | | | | | | + | \| \| \| \| \| \| \| \| | | | | | | | + | X X X X X X X X | | | | | | | + | |\ |\ |\ |\ |\ |\ |\ |\ | | | | | | | + | | \| \| \| \| \| \| \| \| | | | | | | + | | X X X X X X X X | | | | | | + | | |\ |\ |\ |\ |\ |\ |\ |\ | | | | | | + | | | \| \| \| \| \| \| \| \| | | | | | + | | | X X X X X X X X | | | | | + | | | |\ |\ |\ |\ |\ |\ |\ |\ | | | | | + | | | | \| \| \| \| \| \| \| \| | | | | + | | | | X X X X X X X X | | | | + | | | | |\ |\ |\ |\ |\ |\ |\ |\ | | | | + | | | | | \| \| \| \| \| \| \| \| | | | + | | | | | X X X X X X X X | | | + | | | | | |\ |\ |\ |\ |\ |\ |\ |\ | | | + | | | | | | \| \| \| \| \| \| \| \| | | + | | | | | | X X X X X X X X | | + | | | | | | |\ |\ |\ |\ |\ |\ |\ |\ | | + | | | | | | | \| \| \| \| \| \| \| \| | + | | | | | | | X X X X X X X X | + | | | | | | | |\ |\ |\ |\ |\ |\ |\ |\ | + | | | | | | | | \| \| \| \| \| \| \| \| + | | | | | | | | @ @ @ @ @ @ @ @ + | | | | | | | | | | | | | | | | +"[1..], // trim newline at start + ); + } + + #[test] + fn test_work_efficient_diagram_9() { + let item_count = 9; + test_diagram( + PrefixSumAlgorithm::WorkEfficient.ops(item_count), + item_count, + &r" + | | | | | | | | | + ● | ● | ● | ● | | + |\ | |\ | |\ | |\ | | + | \| | \| | \| | \| | + | @ | @ | @ | @ | + | |\ | | | |\ | | | + | | \| | | | \| | | + | | X | | | X | | + | | |\ | | | |\ | | + | | | \| | | | \| | + | | | @ | | | @ | + | | | |\ | | | | | + | | | | \| | | | | + | | | | X | | | | + | | | | |\ | | | | + | | | | | \| | | | + | | | | | X | | | + | | | | | |\ | | | + | | | | | | \| | | + | | | | | | X | | + | | | | | | |\ | | + | | | | | | | \| | + | | | ● | | | @ | + | | | |\ | | | | | + | | | | \| | | | | + | | | | X | | | | + | | | | |\ | | | | + | | | | | \| | | | + | ● | ● | @ | ● | + | |\ | |\ | |\ | |\ | + | | \| | \| | \| | \| + | | @ | @ | @ | @ + | | | | | | | | | +"[1..], // trim newline at start + ); + } + + #[test] + fn test_low_latency_diagram_9() { + let item_count = 9; + test_diagram( + PrefixSumAlgorithm::LowLatency.ops(item_count), + item_count, + &r" + | | | | | | | | | + ● ● ● ● ● ● ● ● | + |\ |\ |\ |\ |\ |\ |\ |\ | + | \| \| \| \| \| \| \| \| + ● @ @ @ @ @ @ @ @ + |\ |\ |\ |\ |\ |\ |\ | | + | \| \| \| \| \| \| \| | + | X X X X X X X | + | |\ |\ |\ |\ |\ |\ |\ | + | | \| \| \| \| \| \| \| + ● ● @ @ @ @ @ @ @ + |\ |\ |\ |\ |\ | | | | + | \| \| \| \| \| | | | + | X X X X X | | | + | |\ |\ |\ |\ |\ | | | + | | \| \| \| \| \| | | + | | X X X X X | | + | | |\ |\ |\ |\ |\ | | + | | | \| \| \| \| \| | + | | | X X X X X | + | | | |\ |\ |\ |\ |\ | + | | | | \| \| \| \| \| + ● | | | @ @ @ @ @ + |\ | | | | | | | | + | \| | | | | | | | + | X | | | | | | | + | |\ | | | | | | | + | | \| | | | | | | + | | X | | | | | | + | | |\ | | | | | | + | | | \| | | | | | + | | | X | | | | | + | | | |\ | | | | | + | | | | \| | | | | + | | | | X | | | | + | | | | |\ | | | | + | | | | | \| | | | + | | | | | X | | | + | | | | | |\ | | | + | | | | | | \| | | + | | | | | | X | | + | | | | | | |\ | | + | | | | | | | \| | + | | | | | | | X | + | | | | | | | |\ | + | | | | | | | | \| + | | | | | | | | @ + | | | | | | | | | +"[1..], // trim newline at start + ); + } + + #[test] + fn test_reduce_diagram_16() { + let item_count = 16; + test_diagram( + reduce_ops(item_count), + item_count, + &r" + | | | | | | | | | | | | | | | | + ● | ● | ● | ● | ● | ● | ● | ● | + |\ | |\ | |\ | |\ | |\ | |\ | |\ | |\ | + | \| | \| | \| | \| | \| | \| | \| | \| + | @ | @ | @ | @ | @ | @ | @ | @ + | |\ | | | |\ | | | |\ | | | |\ | | + | | \| | | | \| | | | \| | | | \| | + | | X | | | X | | | X | | | X | + | | |\ | | | |\ | | | |\ | | | |\ | + | | | \| | | | \| | | | \| | | | \| + | | | @ | | | @ | | | @ | | | @ + | | | |\ | | | | | | | |\ | | | | + | | | | \| | | | | | | | \| | | | + | | | | X | | | | | | | X | | | + | | | | |\ | | | | | | | |\ | | | + | | | | | \| | | | | | | | \| | | + | | | | | X | | | | | | | X | | + | | | | | |\ | | | | | | | |\ | | + | | | | | | \| | | | | | | | \| | + | | | | | | X | | | | | | | X | + | | | | | | |\ | | | | | | | |\ | + | | | | | | | \| | | | | | | | \| + | | | | | | | @ | | | | | | | @ + | | | | | | | |\ | | | | | | | | + | | | | | | | | \| | | | | | | | + | | | | | | | | X | | | | | | | + | | | | | | | | |\ | | | | | | | + | | | | | | | | | \| | | | | | | + | | | | | | | | | X | | | | | | + | | | | | | | | | |\ | | | | | | + | | | | | | | | | | \| | | | | | + | | | | | | | | | | X | | | | | + | | | | | | | | | | |\ | | | | | + | | | | | | | | | | | \| | | | | + | | | | | | | | | | | X | | | | + | | | | | | | | | | | |\ | | | | + | | | | | | | | | | | | \| | | | + | | | | | | | | | | | | X | | | + | | | | | | | | | | | | |\ | | | + | | | | | | | | | | | | | \| | | + | | | | | | | | | | | | | X | | + | | | | | | | | | | | | | |\ | | + | | | | | | | | | | | | | | \| | + | | | | | | | | | | | | | | X | + | | | | | | | | | | | | | | |\ | + | | | | | | | | | | | | | | | \| + | | | | | | | | | | | | | | | @ + | | | | | | | | | | | | | | | | +"[1..], // trim newline at start + ); + } + + #[test] + fn test_reduce_diagram_9() { + let item_count = 9; + test_diagram( + reduce_ops(item_count), + item_count, + &r" + | | | | | | | | | + | ● | ● | ● | ● | + | |\ | |\ | |\ | |\ | + | | \| | \| | \| | \| + | | @ | @ | @ | @ + | | |\ | | | |\ | | + | | | \| | | | \| | + | | | X | | | X | + | | | |\ | | | |\ | + | | | | \| | | | \| + | | | | @ | | | @ + | | | | |\ | | | | + | | | | | \| | | | + | | | | | X | | | + | | | | | |\ | | | + | | | | | | \| | | + | | | | | | X | | + | | | | | | |\ | | + | | | | | | | \| | + | | | | | | | X | + | | | | | | | |\ | + | | | | | | | | \| + ● | | | | | | | @ + |\ | | | | | | | | + | \| | | | | | | | + | X | | | | | | | + | |\ | | | | | | | + | | \| | | | | | | + | | X | | | | | | + | | |\ | | | | | | + | | | \| | | | | | + | | | X | | | | | + | | | |\ | | | | | + | | | | \| | | | | + | | | | X | | | | + | | | | |\ | | | | + | | | | | \| | | | + | | | | | X | | | + | | | | | |\ | | | + | | | | | | \| | | + | | | | | | X | | + | | | | | | |\ | | + | | | | | | | \| | + | | | | | | | X | + | | | | | | | |\ | + | | | | | | | | \| + | | | | | | | | @ + | | | | | | | | | +"[1..], // trim newline at start + ); + } +} From 2fa0ea61920590dcc331d9740a25720e025b9d66 Mon Sep 17 00:00:00 2001 From: Jacob Lifshay Date: Sun, 9 Mar 2025 20:59:21 -0700 Subject: [PATCH 07/38] make FillInDefaultedGenerics work with `Size`s and not just `Type`s --- crates/fayalite/src/ty.rs | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/crates/fayalite/src/ty.rs b/crates/fayalite/src/ty.rs index 69080c9..55c7e8f 100644 --- a/crates/fayalite/src/ty.rs +++ b/crates/fayalite/src/ty.rs @@ -11,6 +11,7 @@ use crate::{ intern::{Intern, Interned}, reset::{AsyncReset, Reset, SyncReset}, source_location::SourceLocation, + util::ConstUsize, }; use std::{fmt, hash::Hash, iter::FusedIterator, ops::Index}; @@ -166,7 +167,7 @@ impl MatchVariantAndInactiveScope for MatchVariantWith } pub trait FillInDefaultedGenerics { - type Type: Type; + type Type; fn fill_in_defaulted_generics(self) -> Self::Type; } @@ -178,6 +179,22 @@ impl FillInDefaultedGenerics for T { } } +impl FillInDefaultedGenerics for usize { + type Type = usize; + + fn fill_in_defaulted_generics(self) -> Self::Type { + self + } +} + +impl FillInDefaultedGenerics for ConstUsize { + type Type = ConstUsize; + + fn fill_in_defaulted_generics(self) -> Self::Type { + self + } +} + mod sealed { pub trait TypeOrDefaultSealed {} pub trait BaseTypeSealed {} From c0c5b550bc1ca2ddbb2917084211cf420091df75 Mon Sep 17 00:00:00 2001 From: Jacob Lifshay Date: Sun, 9 Mar 2025 21:03:47 -0700 Subject: [PATCH 08/38] add PhantomConst --- crates/fayalite/src/expr.rs | 26 ++ crates/fayalite/src/expr/ops.rs | 25 +- crates/fayalite/src/firrtl.rs | 15 +- crates/fayalite/src/lib.rs | 1 + crates/fayalite/src/memory.rs | 1 + crates/fayalite/src/module.rs | 3 + .../src/module/transform/deduce_resets.rs | 13 +- .../src/module/transform/simplify_enums.rs | 13 +- .../src/module/transform/simplify_memories.rs | 14 +- crates/fayalite/src/module/transform/visit.rs | 1 + crates/fayalite/src/phantom_const.rs | 273 ++++++++++++++++++ crates/fayalite/src/prelude.rs | 1 + crates/fayalite/src/sim.rs | 36 ++- crates/fayalite/src/ty.rs | 12 + crates/fayalite/visit_types.json | 9 +- 15 files changed, 428 insertions(+), 15 deletions(-) create mode 100644 crates/fayalite/src/phantom_const.rs diff --git a/crates/fayalite/src/expr.rs b/crates/fayalite/src/expr.rs index f0008f4..016ec8e 100644 --- a/crates/fayalite/src/expr.rs +++ b/crates/fayalite/src/expr.rs @@ -16,6 +16,7 @@ use crate::{ transform::visit::{Fold, Folder, Visit, Visitor}, Instance, ModuleIO, }, + phantom_const::PhantomConst, reg::Reg, reset::{AsyncReset, Reset, ResetType, ResetTypeDispatch, SyncReset}, ty::{CanonicalType, StaticType, Type, TypeWithDeref}, @@ -109,6 +110,7 @@ expr_enum! { UIntLiteral(Interned), SIntLiteral(Interned), BoolLiteral(bool), + PhantomConst(PhantomConst), BundleLiteral(ops::BundleLiteral), ArrayLiteral(ops::ArrayLiteral), EnumLiteral(ops::EnumLiteral), @@ -755,3 +757,27 @@ pub fn repeat( ) .to_expr() } + +impl ToExpr for PhantomConst { + type Type = Self; + + fn to_expr(&self) -> Expr { + Expr { + __enum: ExprEnum::PhantomConst(self.canonical_phantom_const()).intern_sized(), + __ty: *self, + __flow: Flow::Source, + } + } +} + +impl GetTarget for PhantomConst { + fn target(&self) -> Option> { + None + } +} + +impl ToLiteralBits for PhantomConst { + fn to_literal_bits(&self) -> Result, NotALiteralExpr> { + Ok(Interned::default()) + } +} diff --git a/crates/fayalite/src/expr/ops.rs b/crates/fayalite/src/expr/ops.rs index c502fd5..e794a68 100644 --- a/crates/fayalite/src/expr/ops.rs +++ b/crates/fayalite/src/expr/ops.rs @@ -11,14 +11,15 @@ use crate::{ GetTarget, Target, TargetPathArrayElement, TargetPathBundleField, TargetPathDynArrayElement, TargetPathElement, }, - CastTo, Expr, ExprEnum, Flow, HdlPartialEq, HdlPartialOrd, NotALiteralExpr, ReduceBits, - ToExpr, ToLiteralBits, + CastBitsTo as _, CastTo, CastToBits as _, Expr, ExprEnum, Flow, HdlPartialEq, + HdlPartialOrd, NotALiteralExpr, ReduceBits, ToExpr, ToLiteralBits, }, int::{ Bool, BoolOrIntType, DynSize, IntType, KnownSize, SInt, SIntType, SIntValue, Size, UInt, UIntType, UIntValue, }, intern::{Intern, Interned}, + phantom_const::{PhantomConst, PhantomConstValue}, reset::{ AsyncReset, Reset, ResetType, ResetTypeDispatch, SyncReset, ToAsyncReset, ToReset, ToSyncReset, @@ -1892,6 +1893,26 @@ impl ExprCastTo for Clock { } } +impl ExprCastTo<()> for PhantomConst { + fn cast_to(src: Expr, to_type: ()) -> Expr<()> { + src.cast_to_bits().cast_bits_to(to_type) + } +} + +impl ExprCastTo> for () { + fn cast_to(src: Expr, to_type: PhantomConst) -> Expr> { + src.cast_to_bits().cast_bits_to(to_type) + } +} + +impl ExprCastTo> + for PhantomConst +{ + fn cast_to(src: Expr, to_type: PhantomConst) -> Expr> { + src.cast_to_bits().cast_bits_to(to_type) + } +} + #[derive(Copy, Clone, PartialEq, Eq, Hash)] pub struct FieldAccess { base: Expr, diff --git a/crates/fayalite/src/firrtl.rs b/crates/fayalite/src/firrtl.rs index ea76cf8..dd5fc2e 100644 --- a/crates/fayalite/src/firrtl.rs +++ b/crates/fayalite/src/firrtl.rs @@ -15,7 +15,7 @@ use crate::{ target::{ Target, TargetBase, TargetPathArrayElement, TargetPathBundleField, TargetPathElement, }, - Expr, ExprEnum, + CastBitsTo, Expr, ExprEnum, }, formal::FormalKind, int::{Bool, DynSize, IntType, SIntValue, UInt, UIntValue}, @@ -447,6 +447,7 @@ impl TypeState { CanonicalType::AsyncReset(AsyncReset {}) => "AsyncReset".into(), CanonicalType::SyncReset(SyncReset {}) => "UInt<1>".into(), CanonicalType::Reset(Reset {}) => "Reset".into(), + CanonicalType::PhantomConst(_) => "{}".into(), } } } @@ -1152,6 +1153,7 @@ impl<'a> Exporter<'a> { | CanonicalType::Clock(_) | CanonicalType::AsyncReset(_) | CanonicalType::Reset(_) => format!("asUInt({value_str})"), + CanonicalType::PhantomConst(_) => "UInt<0>(0)".into(), } } fn expr_cast_bits_to_bundle( @@ -1357,6 +1359,12 @@ impl<'a> Exporter<'a> { CanonicalType::AsyncReset(_) => format!("asAsyncReset({value_str})"), CanonicalType::SyncReset(_) => value_str, CanonicalType::Reset(_) => unreachable!("Reset is not bit castable to"), + CanonicalType::PhantomConst(_) => { + let retval = self.module.ns.make_new("_cast_bits_to_phantom_const_expr"); + definitions.add_definition_line(format_args!("{extra_indent}wire {retval}: {{}}")); + definitions.add_definition_line(format_args!("{extra_indent}invalidate {retval}")); + return retval.to_string(); + } } } fn expr_unary( @@ -1395,6 +1403,11 @@ impl<'a> Exporter<'a> { ExprEnum::UIntLiteral(literal) => self.uint_literal(&literal), ExprEnum::SIntLiteral(literal) => self.sint_literal(&literal), ExprEnum::BoolLiteral(literal) => self.bool_literal(literal), + ExprEnum::PhantomConst(ty) => self.expr( + UInt[0].zero().cast_bits_to(ty.canonical()), + definitions, + const_ty, + ), ExprEnum::ArrayLiteral(array_literal) => { self.array_literal_expr(array_literal, definitions, const_ty) } diff --git a/crates/fayalite/src/lib.rs b/crates/fayalite/src/lib.rs index 88fe169..512572d 100644 --- a/crates/fayalite/src/lib.rs +++ b/crates/fayalite/src/lib.rs @@ -96,6 +96,7 @@ pub mod int; pub mod intern; pub mod memory; pub mod module; +pub mod phantom_const; pub mod prelude; pub mod reg; pub mod reset; diff --git a/crates/fayalite/src/memory.rs b/crates/fayalite/src/memory.rs index 2f0ec47..1101157 100644 --- a/crates/fayalite/src/memory.rs +++ b/crates/fayalite/src/memory.rs @@ -1082,6 +1082,7 @@ pub fn splat_mask(ty: T, value: Expr) -> Expr> { ) .to_expr(), )), + CanonicalType::PhantomConst(_) => Expr::from_canonical(Expr::canonical(().to_expr())), } } diff --git a/crates/fayalite/src/module.rs b/crates/fayalite/src/module.rs index 5a18ac9..446746a 100644 --- a/crates/fayalite/src/module.rs +++ b/crates/fayalite/src/module.rs @@ -1490,6 +1490,9 @@ impl TargetState { }) .collect(), }, + CanonicalType::PhantomConst(_) => TargetStateInner::Decomposed { + subtargets: HashMap::new(), + }, CanonicalType::Array(ty) => TargetStateInner::Decomposed { subtargets: (0..ty.len()) .map(|index| { diff --git a/crates/fayalite/src/module/transform/deduce_resets.rs b/crates/fayalite/src/module/transform/deduce_resets.rs index fe518a5..a70dc33 100644 --- a/crates/fayalite/src/module/transform/deduce_resets.rs +++ b/crates/fayalite/src/module/transform/deduce_resets.rs @@ -155,6 +155,7 @@ impl ResetsLayout { CanonicalType::SyncReset(_) => ResetsLayout::SyncReset, CanonicalType::Reset(_) => ResetsLayout::Reset, CanonicalType::Clock(_) => ResetsLayout::NoResets, + CanonicalType::PhantomConst(_) => ResetsLayout::NoResets, } } } @@ -407,7 +408,8 @@ impl Resets { | CanonicalType::Bool(_) | CanonicalType::AsyncReset(_) | CanonicalType::SyncReset(_) - | CanonicalType::Clock(_) => Ok(self.ty), + | CanonicalType::Clock(_) + | CanonicalType::PhantomConst(_) => Ok(self.ty), CanonicalType::Array(ty) => Ok(CanonicalType::Array(Array::new_dyn( self.array_elements().substituted_type( reset_graph, @@ -998,7 +1000,8 @@ fn cast_bit_op( CanonicalType::Array(_) | CanonicalType::Enum(_) | CanonicalType::Bundle(_) - | CanonicalType::Reset(_) => unreachable!(), + | CanonicalType::Reset(_) + | CanonicalType::PhantomConst(_) => unreachable!(), $(CanonicalType::$Variant(ty) => Expr::expr_enum($arg.cast_to(ty)),)* } }; @@ -1010,6 +1013,7 @@ fn cast_bit_op( | CanonicalType::Enum(_) | CanonicalType::Bundle(_) | CanonicalType::Reset(_) => unreachable!(), + CanonicalType::PhantomConst(_) => Expr::expr_enum(arg), $(CanonicalType::$Variant(_) => { let arg = Expr::<$Variant>::from_canonical(arg); match_expr_ty!(arg, UInt, SInt, Bool, AsyncReset, SyncReset, Clock) @@ -1040,6 +1044,7 @@ impl RunPass

for ExprEnum { ExprEnum::UIntLiteral(expr) => Ok(expr.run_pass(pass_args)?.map(ExprEnum::from)), ExprEnum::SIntLiteral(expr) => Ok(expr.run_pass(pass_args)?.map(ExprEnum::from)), ExprEnum::BoolLiteral(expr) => Ok(expr.run_pass(pass_args)?.map(ExprEnum::from)), + ExprEnum::PhantomConst(expr) => Ok(expr.run_pass(pass_args)?.map(ExprEnum::from)), ExprEnum::BundleLiteral(expr) => Ok(expr.run_pass(pass_args)?.map(ExprEnum::from)), ExprEnum::ArrayLiteral(expr) => Ok(expr.run_pass(pass_args)?.map(ExprEnum::from)), ExprEnum::EnumLiteral(expr) => Ok(expr.run_pass(pass_args)?.map(ExprEnum::from)), @@ -1670,7 +1675,8 @@ impl RunPassDispatch for AnyReg { | CanonicalType::Enum(_) | CanonicalType::Bundle(_) | CanonicalType::Reset(_) - | CanonicalType::Clock(_) => unreachable!(), + | CanonicalType::Clock(_) + | CanonicalType::PhantomConst(_) => unreachable!(), } }) } @@ -1769,6 +1775,7 @@ impl_run_pass_copy!([] SVAttributeAnnotation); impl_run_pass_copy!([] UInt); impl_run_pass_copy!([] usize); impl_run_pass_copy!([] FormalKind); +impl_run_pass_copy!([] PhantomConst); macro_rules! impl_run_pass_for_struct { ( diff --git a/crates/fayalite/src/module/transform/simplify_enums.rs b/crates/fayalite/src/module/transform/simplify_enums.rs index 4eb0d0c..e8b6168 100644 --- a/crates/fayalite/src/module/transform/simplify_enums.rs +++ b/crates/fayalite/src/module/transform/simplify_enums.rs @@ -69,7 +69,8 @@ fn contains_any_enum_types(ty: CanonicalType) -> bool { | CanonicalType::AsyncReset(_) | CanonicalType::SyncReset(_) | CanonicalType::Reset(_) - | CanonicalType::Clock(_) => false, + | CanonicalType::Clock(_) + | CanonicalType::PhantomConst(_) => false, } } } @@ -512,7 +513,8 @@ impl State { | CanonicalType::AsyncReset(_) | CanonicalType::SyncReset(_) | CanonicalType::Reset(_) - | CanonicalType::Clock(_) => unreachable!(), + | CanonicalType::Clock(_) + | CanonicalType::PhantomConst(_) => unreachable!(), } } } @@ -577,7 +579,8 @@ fn connect_port( | (CanonicalType::Clock(_), _) | (CanonicalType::AsyncReset(_), _) | (CanonicalType::SyncReset(_), _) - | (CanonicalType::Reset(_), _) => unreachable!( + | (CanonicalType::Reset(_), _) + | (CanonicalType::PhantomConst(_), _) => unreachable!( "trying to connect memory ports:\n{:?}\n{:?}", Expr::ty(lhs), Expr::ty(rhs), @@ -665,6 +668,7 @@ impl Folder for State { ExprEnum::UIntLiteral(_) | ExprEnum::SIntLiteral(_) | ExprEnum::BoolLiteral(_) + | ExprEnum::PhantomConst(_) | ExprEnum::BundleLiteral(_) | ExprEnum::ArrayLiteral(_) | ExprEnum::Uninit(_) @@ -923,7 +927,8 @@ impl Folder for State { | CanonicalType::Clock(_) | CanonicalType::AsyncReset(_) | CanonicalType::SyncReset(_) - | CanonicalType::Reset(_) => canonical_type.default_fold(self), + | CanonicalType::Reset(_) + | CanonicalType::PhantomConst(_) => canonical_type.default_fold(self), } } diff --git a/crates/fayalite/src/module/transform/simplify_memories.rs b/crates/fayalite/src/module/transform/simplify_memories.rs index e8f9cbf..101385e 100644 --- a/crates/fayalite/src/module/transform/simplify_memories.rs +++ b/crates/fayalite/src/module/transform/simplify_memories.rs @@ -62,6 +62,7 @@ enum MemSplit { Bundle { fields: Rc<[MemSplit]>, }, + PhantomConst, Single { output_mem: Option, element_type: SingleType, @@ -76,6 +77,7 @@ impl MemSplit { fn mark_changed_element_type(self) -> Self { match self { MemSplit::Bundle { fields: _ } => self, + MemSplit::PhantomConst => self, MemSplit::Single { output_mem, element_type, @@ -97,6 +99,7 @@ impl MemSplit { .map(|field| Self::new(field.ty).mark_changed_element_type()) .collect(), }, + CanonicalType::PhantomConst(_) => MemSplit::PhantomConst, CanonicalType::Array(ty) => { let element = MemSplit::new(ty.element()); if let Self::Single { @@ -339,6 +342,7 @@ impl SplitMemState<'_, '_> { self.split_state_stack.pop(); } } + MemSplit::PhantomConst => {} MemSplit::Single { output_mem, element_type: single_type, @@ -538,7 +542,12 @@ impl ModuleState { }; loop { match input_element_type { - CanonicalType::Bundle(_) => unreachable!("bundle types are always split"), + CanonicalType::Bundle(_) => { + unreachable!("bundle types are always split") + } + CanonicalType::PhantomConst(_) => { + unreachable!("PhantomConst are always removed") + } CanonicalType::Enum(_) if input_array_types .first() @@ -743,7 +752,8 @@ impl ModuleState { .. } | MemSplit::Bundle { .. } - | MemSplit::Array { .. } => { + | MemSplit::Array { .. } + | MemSplit::PhantomConst => { let mut replacement_ports = Vec::with_capacity(input_mem.ports().len()); let mut wire_port_rdata = Vec::with_capacity(input_mem.ports().len()); let mut wire_port_wdata = Vec::with_capacity(input_mem.ports().len()); diff --git a/crates/fayalite/src/module/transform/visit.rs b/crates/fayalite/src/module/transform/visit.rs index 97de4fc..662a578 100644 --- a/crates/fayalite/src/module/transform/visit.rs +++ b/crates/fayalite/src/module/transform/visit.rs @@ -28,6 +28,7 @@ use crate::{ NormalModuleBody, ScopedNameId, Stmt, StmtConnect, StmtDeclaration, StmtFormal, StmtIf, StmtInstance, StmtMatch, StmtReg, StmtWire, }, + phantom_const::PhantomConst, reg::Reg, reset::{AsyncReset, Reset, ResetType, SyncReset}, source_location::SourceLocation, diff --git a/crates/fayalite/src/phantom_const.rs b/crates/fayalite/src/phantom_const.rs new file mode 100644 index 0000000..81f5d6f --- /dev/null +++ b/crates/fayalite/src/phantom_const.rs @@ -0,0 +1,273 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// See Notices.txt for copyright information + +use serde::{de::DeserializeOwned, Deserialize, Serialize}; + +use crate::{ + intern::{Intern, Interned, InternedCompare, LazyInterned, Memoize}, + source_location::SourceLocation, + ty::{impl_match_variant_as_self, CanonicalType, StaticType, Type, TypeProperties}, +}; +use std::{ + any::Any, + fmt, + hash::{Hash, Hasher}, + marker::PhantomData, +}; + +#[derive(Clone)] +pub struct PhantomConstCanonicalValue { + parsed: serde_json::Value, + serialized: Interned, +} + +impl PhantomConstCanonicalValue { + pub fn from_json_value(parsed: serde_json::Value) -> Self { + let serialized = Intern::intern_owned( + serde_json::to_string(&parsed) + .expect("conversion from json value to text shouldn't fail"), + ); + Self { parsed, serialized } + } + pub fn as_json_value(&self) -> &serde_json::Value { + &self.parsed + } + pub fn as_str(&self) -> Interned { + self.serialized + } +} + +impl fmt::Debug for PhantomConstCanonicalValue { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str(&self.serialized) + } +} + +impl fmt::Display for PhantomConstCanonicalValue { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str(&self.serialized) + } +} + +impl PartialEq for PhantomConstCanonicalValue { + fn eq(&self, other: &Self) -> bool { + self.serialized == other.serialized + } +} + +impl Eq for PhantomConstCanonicalValue {} + +impl Hash for PhantomConstCanonicalValue { + fn hash(&self, state: &mut H) { + self.serialized.hash(state); + } +} + +impl Serialize for PhantomConstCanonicalValue { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + self.parsed.serialize(serializer) + } +} + +impl<'de> Deserialize<'de> for PhantomConstCanonicalValue { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + Ok(Self::from_json_value(serde_json::Value::deserialize( + deserializer, + )?)) + } +} + +pub trait PhantomConstValue: Intern + InternedCompare + Serialize + fmt::Debug { + fn deserialize<'de, D>(deserializer: D) -> Result, D::Error> + where + D: serde::Deserializer<'de>; +} + +impl PhantomConstValue for T +where + T: ?Sized + Intern + InternedCompare + Serialize + fmt::Debug, + Interned: DeserializeOwned, +{ + fn deserialize<'de, D>(deserializer: D) -> Result, D::Error> + where + D: serde::Deserializer<'de>, + { + as Deserialize<'de>>::deserialize(deserializer) + } +} + +/// Wrapper type that allows any Rust value to be smuggled as a HDL [`Type`]. +/// This only works for values that can be [serialized][Serialize] to and [deserialized][Deserialize] from [JSON][serde_json]. +pub struct PhantomConst { + value: LazyInterned, +} + +impl fmt::Debug for PhantomConst { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_tuple("PhantomConst").field(&self.get()).finish() + } +} + +impl Clone for PhantomConst { + fn clone(&self) -> Self { + *self + } +} + +impl Copy for PhantomConst {} + +impl PartialEq for PhantomConst { + fn eq(&self, other: &Self) -> bool { + self.get() == other.get() + } +} + +impl Eq for PhantomConst {} + +impl Hash for PhantomConst { + fn hash(&self, state: &mut H) { + self.get().hash(state); + } +} + +struct PhantomConstCanonicalMemoize(PhantomData); + +impl Copy + for PhantomConstCanonicalMemoize +{ +} + +impl Clone + for PhantomConstCanonicalMemoize +{ + fn clone(&self) -> Self { + *self + } +} + +impl Eq + for PhantomConstCanonicalMemoize +{ +} + +impl PartialEq + for PhantomConstCanonicalMemoize +{ + fn eq(&self, _other: &Self) -> bool { + true + } +} + +impl Hash + for PhantomConstCanonicalMemoize +{ + fn hash(&self, _state: &mut H) {} +} + +impl Memoize for PhantomConstCanonicalMemoize { + type Input = Interned; + type InputOwned = Interned; + type Output = Interned; + + fn inner(self, input: &Self::Input) -> Self::Output { + Intern::intern_sized(PhantomConstCanonicalValue::from_json_value( + serde_json::to_value(input) + .expect("serialization failed when constructing a canonical PhantomConst"), + )) + } +} + +impl Memoize for PhantomConstCanonicalMemoize { + type Input = Interned; + type InputOwned = Interned; + type Output = Interned; + + fn inner(self, input: &Self::Input) -> Self::Output { + PhantomConstValue::deserialize(input.as_json_value()).expect("deserialization failed ") + } +} + +impl PhantomConst +where + Interned: Default, +{ + pub const fn default() -> Self { + PhantomConst { + value: LazyInterned::new_lazy(&Interned::::default), + } + } +} + +impl PhantomConst { + pub fn new(value: Interned) -> Self { + Self { + value: LazyInterned::Interned(value), + } + } + pub fn get(self) -> Interned { + self.value.interned() + } + pub fn type_properties(self) -> TypeProperties { + <()>::TYPE_PROPERTIES + } + pub fn can_connect(self, other: Self) -> bool { + self == other + } + pub fn canonical_phantom_const(self) -> PhantomConst { + if let Some(&retval) = ::downcast_ref::(&self) { + return retval; + } + ::new( + PhantomConstCanonicalMemoize::(PhantomData).get_owned(self.get()), + ) + } + pub fn from_canonical_phantom_const(canonical_type: PhantomConst) -> Self { + if let Some(&retval) = ::downcast_ref::(&canonical_type) { + return retval; + } + Self::new( + PhantomConstCanonicalMemoize::(PhantomData).get_owned(canonical_type.get()), + ) + } +} + +impl Type for PhantomConst { + type BaseType = PhantomConst; + type MaskType = (); + impl_match_variant_as_self!(); + + fn mask_type(&self) -> Self::MaskType { + () + } + + fn canonical(&self) -> CanonicalType { + CanonicalType::PhantomConst(self.canonical_phantom_const()) + } + + fn from_canonical(canonical_type: CanonicalType) -> Self { + let CanonicalType::PhantomConst(phantom_const) = canonical_type else { + panic!("expected PhantomConst"); + }; + Self::from_canonical_phantom_const(phantom_const) + } + + fn source_location() -> SourceLocation { + SourceLocation::builtin() + } +} + +impl StaticType for PhantomConst +where + Interned: Default, +{ + const TYPE: Self = Self::default(); + const MASK_TYPE: Self::MaskType = (); + const TYPE_PROPERTIES: TypeProperties = <()>::TYPE_PROPERTIES; + const MASK_TYPE_PROPERTIES: TypeProperties = <()>::TYPE_PROPERTIES; +} diff --git a/crates/fayalite/src/prelude.rs b/crates/fayalite/src/prelude.rs index 9e7a85e..39fa143 100644 --- a/crates/fayalite/src/prelude.rs +++ b/crates/fayalite/src/prelude.rs @@ -26,6 +26,7 @@ pub use crate::{ annotate, connect, connect_any, incomplete_wire, instance, memory, memory_array, memory_with_init, reg_builder, wire, Instance, Module, ModuleBuilder, }, + phantom_const::PhantomConst, reg::Reg, reset::{AsyncReset, Reset, SyncReset, ToAsyncReset, ToReset, ToSyncReset}, source_location::SourceLocation, diff --git a/crates/fayalite/src/sim.rs b/crates/fayalite/src/sim.rs index f630f5a..96f6dd9 100644 --- a/crates/fayalite/src/sim.rs +++ b/crates/fayalite/src/sim.rs @@ -167,6 +167,14 @@ impl CompiledTypeLayout { body: CompiledTypeLayoutBody::Array { element }, } } + CanonicalType::PhantomConst(_) => { + let unit_layout = CompiledTypeLayout::get(()); + CompiledTypeLayout { + ty: *input, + layout: unit_layout.layout, + body: unit_layout.body, + } + } CanonicalType::Bundle(bundle) => { let mut layout = TypeLayout::empty(); let fields = bundle @@ -1792,7 +1800,7 @@ impl Compiler { } .into() } - CanonicalType::Bundle(_) => unreachable!(), + CanonicalType::Bundle(_) | CanonicalType::PhantomConst(_) => unreachable!(), CanonicalType::AsyncReset(_) => TraceAsyncReset { location: self.make_trace_scalar_helper( instantiated_module, @@ -2009,6 +2017,13 @@ impl Compiler { | CanonicalType::Clock(_) => { self.make_trace_scalar(instantiated_module, target, name, source_location) } + CanonicalType::PhantomConst(_) => TraceBundle { + name, + fields: Interned::default(), + ty: Bundle::new(Interned::default()), + flow: target.flow(), + } + .into(), } } fn make_trace_decl( @@ -2469,6 +2484,9 @@ impl Compiler { Expr::field(Expr::::from_canonical(expr.arg()), &field.name) }), ), + CanonicalType::PhantomConst(_) => { + self.compile_cast_aggregate_to_bits(instantiated_module, []) + } } } fn compile_cast_bits_to( @@ -2518,6 +2536,10 @@ impl Compiler { CanonicalType::SyncReset(ty) => Expr::canonical(expr.arg().cast_to(ty)), CanonicalType::Reset(_) => unreachable!(), CanonicalType::Clock(ty) => Expr::canonical(expr.arg().cast_to(ty)), + CanonicalType::PhantomConst(ty) => { + let _ = self.compile_expr(instantiated_module, Expr::canonical(expr.arg())); + Expr::canonical(ty.to_expr()) + } }; let retval = self.compile_expr(instantiated_module, Expr::canonical(retval)); self.compiled_expr_to_value(retval, instantiated_module.leaf_module().source_location()) @@ -2567,6 +2589,7 @@ impl Compiler { CanonicalType::SyncReset(_) => false, CanonicalType::Reset(_) => false, CanonicalType::Clock(_) => false, + CanonicalType::PhantomConst(_) => unreachable!(), }; let dest_signed = match Expr::ty(expr) { CanonicalType::UInt(_) => false, @@ -2579,6 +2602,7 @@ impl Compiler { CanonicalType::SyncReset(_) => false, CanonicalType::Reset(_) => false, CanonicalType::Clock(_) => false, + CanonicalType::PhantomConst(_) => unreachable!(), }; self.simple_nary_big_expr(instantiated_module, Expr::ty(expr), [arg], |dest, [src]| { match (src_signed, dest_signed) { @@ -2634,6 +2658,9 @@ impl Compiler { }] }) .into(), + ExprEnum::PhantomConst(_) => self + .compile_aggregate_literal(instantiated_module, Expr::ty(expr), Interned::default()) + .into(), ExprEnum::BundleLiteral(literal) => self .compile_aggregate_literal( instantiated_module, @@ -3537,6 +3564,7 @@ impl Compiler { CanonicalType::SyncReset(_) => unreachable!(), CanonicalType::Reset(_) => unreachable!(), CanonicalType::Clock(_) => unreachable!(), + CanonicalType::PhantomConst(_) => unreachable!("PhantomConst mismatch"), } } let Some(target) = lhs.target() else { @@ -3901,6 +3929,7 @@ impl Compiler { CanonicalType::SyncReset(_) => false, CanonicalType::Reset(_) => false, CanonicalType::Clock(_) => false, + CanonicalType::PhantomConst(_) => unreachable!(), }; let width = data_layout.ty.bit_width(); if let Some(MemoryPortReadInsns { @@ -5909,6 +5938,7 @@ impl SimValue { CanonicalType::Clock(ty) => { Expr::canonical(Bool::bits_to_expr(Cow::Borrowed(bits)).cast_to(ty)) } + CanonicalType::PhantomConst(ty) => Expr::canonical(ty.to_expr()), } } } @@ -6312,7 +6342,8 @@ impl ToSimValue for bool { | CanonicalType::SInt(_) | CanonicalType::Array(_) | CanonicalType::Enum(_) - | CanonicalType::Bundle(_) => { + | CanonicalType::Bundle(_) + | CanonicalType::PhantomConst(_) => { panic!("can't create SimValue from bool: expected value of type: {ty:?}"); } CanonicalType::Bool(_) @@ -6977,6 +7008,7 @@ impl SimulationImpl { CanonicalType::SyncReset(_) => false, CanonicalType::Reset(_) => false, CanonicalType::Clock(_) => false, + CanonicalType::PhantomConst(_) => unreachable!(), }; match compiled_value.range.len() { TypeLen::A_SMALL_SLOT => read_write_small_scalar( diff --git a/crates/fayalite/src/ty.rs b/crates/fayalite/src/ty.rs index 55c7e8f..2786782 100644 --- a/crates/fayalite/src/ty.rs +++ b/crates/fayalite/src/ty.rs @@ -9,6 +9,7 @@ use crate::{ expr::Expr, int::{Bool, SInt, UInt}, intern::{Intern, Interned}, + phantom_const::PhantomConst, reset::{AsyncReset, Reset, SyncReset}, source_location::SourceLocation, util::ConstUsize, @@ -36,6 +37,7 @@ pub enum CanonicalType { SyncReset(SyncReset), Reset(Reset), Clock(Clock), + PhantomConst(PhantomConst), } impl fmt::Debug for CanonicalType { @@ -51,6 +53,7 @@ impl fmt::Debug for CanonicalType { Self::SyncReset(v) => v.fmt(f), Self::Reset(v) => v.fmt(f), Self::Clock(v) => v.fmt(f), + Self::PhantomConst(v) => v.fmt(f), } } } @@ -68,6 +71,7 @@ impl CanonicalType { CanonicalType::SyncReset(v) => v.type_properties(), CanonicalType::Reset(v) => v.type_properties(), CanonicalType::Clock(v) => v.type_properties(), + CanonicalType::PhantomConst(v) => v.type_properties(), } } pub fn is_passive(self) -> bool { @@ -144,6 +148,12 @@ impl CanonicalType { }; lhs.can_connect(rhs) } + CanonicalType::PhantomConst(lhs) => { + let CanonicalType::PhantomConst(rhs) = rhs else { + return false; + }; + lhs.can_connect(rhs) + } } } } @@ -222,6 +232,7 @@ impl_base_type!(AsyncReset); impl_base_type!(SyncReset); impl_base_type!(Reset); impl_base_type!(Clock); +impl_base_type!(PhantomConst); impl sealed::BaseTypeSealed for CanonicalType {} @@ -316,6 +327,7 @@ impl Type for CanonicalType { CanonicalType::SyncReset(v) => v.mask_type().canonical(), CanonicalType::Reset(v) => v.mask_type().canonical(), CanonicalType::Clock(v) => v.mask_type().canonical(), + CanonicalType::PhantomConst(v) => v.mask_type().canonical(), } } fn canonical(&self) -> CanonicalType { diff --git a/crates/fayalite/visit_types.json b/crates/fayalite/visit_types.json index 3eff1f5..b284372 100644 --- a/crates/fayalite/visit_types.json +++ b/crates/fayalite/visit_types.json @@ -49,7 +49,8 @@ "AsyncReset": "Visible", "SyncReset": "Visible", "Reset": "Visible", - "Clock": "Visible" + "Clock": "Visible", + "PhantomConst": "Visible" } }, "Bundle": { @@ -1262,6 +1263,12 @@ "ArrayElement": "Visible", "DynArrayElement": "Visible" } + }, + "PhantomConst": { + "data": { + "$kind": "Opaque" + }, + "generics": "" } } } \ No newline at end of file From 450e1004b6eef6fcdce74a94e3bded3e0268610d Mon Sep 17 00:00:00 2001 From: Jacob Lifshay Date: Sun, 9 Mar 2025 23:14:14 -0700 Subject: [PATCH 09/38] fix using fayalite as a dependency --- crates/fayalite/src/phantom_const.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/crates/fayalite/src/phantom_const.rs b/crates/fayalite/src/phantom_const.rs index 81f5d6f..b8f3f09 100644 --- a/crates/fayalite/src/phantom_const.rs +++ b/crates/fayalite/src/phantom_const.rs @@ -84,7 +84,7 @@ impl<'de> Deserialize<'de> for PhantomConstCanonicalValue { } pub trait PhantomConstValue: Intern + InternedCompare + Serialize + fmt::Debug { - fn deserialize<'de, D>(deserializer: D) -> Result, D::Error> + fn deserialize_value<'de, D>(deserializer: D) -> Result, D::Error> where D: serde::Deserializer<'de>; } @@ -94,7 +94,7 @@ where T: ?Sized + Intern + InternedCompare + Serialize + fmt::Debug, Interned: DeserializeOwned, { - fn deserialize<'de, D>(deserializer: D) -> Result, D::Error> + fn deserialize_value<'de, D>(deserializer: D) -> Result, D::Error> where D: serde::Deserializer<'de>, { @@ -189,7 +189,8 @@ impl Memoize for PhantomConstCanonicalMemoize; fn inner(self, input: &Self::Input) -> Self::Output { - PhantomConstValue::deserialize(input.as_json_value()).expect("deserialization failed ") + PhantomConstValue::deserialize_value(input.as_json_value()) + .expect("deserialization failed ") } } From d453755bb2cd0b6f2340f3e49058d29a2ee279e8 Mon Sep 17 00:00:00 2001 From: Jacob Lifshay Date: Mon, 10 Mar 2025 19:40:03 -0700 Subject: [PATCH 10/38] add ExprPartialEq/ExprPartialOrd impls for PhantomConst --- crates/fayalite/src/phantom_const.rs | 39 ++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/crates/fayalite/src/phantom_const.rs b/crates/fayalite/src/phantom_const.rs index b8f3f09..dd6cff6 100644 --- a/crates/fayalite/src/phantom_const.rs +++ b/crates/fayalite/src/phantom_const.rs @@ -4,6 +4,11 @@ use serde::{de::DeserializeOwned, Deserialize, Serialize}; use crate::{ + expr::{ + ops::{ExprPartialEq, ExprPartialOrd}, + Expr, ToExpr, + }, + int::Bool, intern::{Intern, Interned, InternedCompare, LazyInterned, Memoize}, source_location::SourceLocation, ty::{impl_match_variant_as_self, CanonicalType, StaticType, Type, TypeProperties}, @@ -272,3 +277,37 @@ where const TYPE_PROPERTIES: TypeProperties = <()>::TYPE_PROPERTIES; const MASK_TYPE_PROPERTIES: TypeProperties = <()>::TYPE_PROPERTIES; } + +impl ExprPartialEq for PhantomConst { + fn cmp_eq(lhs: Expr, rhs: Expr) -> Expr { + assert_eq!(Expr::ty(lhs), Expr::ty(rhs)); + true.to_expr() + } + + fn cmp_ne(lhs: Expr, rhs: Expr) -> Expr { + assert_eq!(Expr::ty(lhs), Expr::ty(rhs)); + false.to_expr() + } +} + +impl ExprPartialOrd for PhantomConst { + fn cmp_lt(lhs: Expr, rhs: Expr) -> Expr { + assert_eq!(Expr::ty(lhs), Expr::ty(rhs)); + false.to_expr() + } + + fn cmp_le(lhs: Expr, rhs: Expr) -> Expr { + assert_eq!(Expr::ty(lhs), Expr::ty(rhs)); + true.to_expr() + } + + fn cmp_gt(lhs: Expr, rhs: Expr) -> Expr { + assert_eq!(Expr::ty(lhs), Expr::ty(rhs)); + false.to_expr() + } + + fn cmp_ge(lhs: Expr, rhs: Expr) -> Expr { + assert_eq!(Expr::ty(lhs), Expr::ty(rhs)); + true.to_expr() + } +} From 920d8d875f80b970a996b4d45f06b145a07fba84 Mon Sep 17 00:00:00 2001 From: Jacob Lifshay Date: Wed, 19 Mar 2025 17:10:51 -0700 Subject: [PATCH 11/38] add some missing #[track_caller] --- crates/fayalite/src/module.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/crates/fayalite/src/module.rs b/crates/fayalite/src/module.rs index 446746a..d26dc7b 100644 --- a/crates/fayalite/src/module.rs +++ b/crates/fayalite/src/module.rs @@ -2174,6 +2174,7 @@ impl ModuleBuilder { .builder_extern_body() .verilog_name = name.intern(); } + #[track_caller] pub fn parameter(&self, name: impl AsRef, value: ExternModuleParameterValue) { let name = name.as_ref(); self.impl_ @@ -2186,6 +2187,7 @@ impl ModuleBuilder { value, }); } + #[track_caller] pub fn parameter_int(&self, name: impl AsRef, value: impl Into) { let name = name.as_ref(); let value = value.into(); @@ -2199,6 +2201,7 @@ impl ModuleBuilder { value: ExternModuleParameterValue::Integer(value), }); } + #[track_caller] pub fn parameter_str(&self, name: impl AsRef, value: impl AsRef) { let name = name.as_ref(); let value = value.as_ref(); @@ -2212,6 +2215,7 @@ impl ModuleBuilder { value: ExternModuleParameterValue::String(value.intern()), }); } + #[track_caller] pub fn parameter_raw_verilog(&self, name: impl AsRef, raw_verilog: impl AsRef) { let name = name.as_ref(); let raw_verilog = raw_verilog.as_ref(); From d1bd176b288ab84e58e6f44bce432d13fa105452 Mon Sep 17 00:00:00 2001 From: Jacob Lifshay Date: Wed, 19 Mar 2025 17:11:41 -0700 Subject: [PATCH 12/38] implement simulation of extern modules --- crates/fayalite/src/firrtl.rs | 1 + crates/fayalite/src/module.rs | 25 +- crates/fayalite/src/module/transform/visit.rs | 1 + crates/fayalite/src/sim.rs | 1453 ++++++++--- crates/fayalite/tests/sim.rs | 65 +- .../fayalite/tests/sim/expected/array_rw.txt | 1819 +++----------- .../expected/conditional_assignment_last.txt | 57 +- .../tests/sim/expected/connect_const.txt | 57 +- .../sim/expected/connect_const_reset.txt | 103 +- .../tests/sim/expected/counter_async.txt | 249 +- .../tests/sim/expected/counter_sync.txt | 249 +- .../tests/sim/expected/duplicate_names.txt | 12 +- crates/fayalite/tests/sim/expected/enums.txt | 497 +--- .../tests/sim/expected/extern_module.txt | 224 ++ .../tests/sim/expected/extern_module.vcd | 51 + .../fayalite/tests/sim/expected/memories.txt | 1436 ++--------- .../fayalite/tests/sim/expected/memories2.txt | 571 +---- .../fayalite/tests/sim/expected/memories3.txt | 2135 ++--------------- crates/fayalite/tests/sim/expected/mod1.txt | 349 +-- .../tests/sim/expected/shift_register.txt | 297 +-- crates/fayalite/visit_types.json | 9 +- 21 files changed, 2702 insertions(+), 6958 deletions(-) create mode 100644 crates/fayalite/tests/sim/expected/extern_module.txt create mode 100644 crates/fayalite/tests/sim/expected/extern_module.vcd diff --git a/crates/fayalite/src/firrtl.rs b/crates/fayalite/src/firrtl.rs index dd5fc2e..d082187 100644 --- a/crates/fayalite/src/firrtl.rs +++ b/crates/fayalite/src/firrtl.rs @@ -2258,6 +2258,7 @@ impl<'a> Exporter<'a> { ModuleBody::Extern(ExternModuleBody { verilog_name, parameters, + simulation: _, }) => { let verilog_name = Ident(verilog_name); writeln!(body, "{indent}defname = {verilog_name}").unwrap(); diff --git a/crates/fayalite/src/module.rs b/crates/fayalite/src/module.rs index d26dc7b..87f86cc 100644 --- a/crates/fayalite/src/module.rs +++ b/crates/fayalite/src/module.rs @@ -21,6 +21,7 @@ use crate::{ memory::{Mem, MemBuilder, MemBuilderTarget, PortName}, reg::Reg, reset::{AsyncReset, Reset, ResetType, ResetTypeDispatch, SyncReset}, + sim::{ExternModuleSimGenerator, ExternModuleSimulation}, source_location::SourceLocation, ty::{CanonicalType, Type}, util::ScopedRef, @@ -1081,6 +1082,7 @@ pub struct ExternModuleBody< > { pub verilog_name: Interned, pub parameters: P, + pub simulation: Option>, } impl From>> for ExternModuleBody { @@ -1088,11 +1090,13 @@ impl From>> for ExternModuleBody { let ExternModuleBody { verilog_name, parameters, + simulation, } = value; let parameters = Intern::intern_owned(parameters); Self { verilog_name, parameters, + simulation, } } } @@ -1283,10 +1287,12 @@ impl fmt::Debug for DebugModuleBody { ModuleBody::Extern(ExternModuleBody { verilog_name, parameters, + simulation, }) => { debug_struct .field("verilog_name", verilog_name) - .field("parameters", parameters); + .field("parameters", parameters) + .field("simulation", simulation); } } debug_struct.finish_non_exhaustive() @@ -1761,7 +1767,12 @@ impl AssertValidityState { ModuleBody::Extern(ExternModuleBody { verilog_name: _, parameters: _, - }) => {} + simulation, + }) => { + if let Some(simulation) = simulation { + simulation.check_io_ty(self.module.io_ty); + } + } ModuleBody::Normal(NormalModuleBody { body }) => { let body = self.make_block_index(body); assert_eq!(body, 0); @@ -2108,6 +2119,7 @@ impl ModuleBuilder { ModuleKind::Extern => ModuleBody::Extern(ExternModuleBody { verilog_name: name.0, parameters: vec![], + simulation: None, }), ModuleKind::Normal => ModuleBody::Normal(NormalModuleBody { body: BuilderModuleBody { @@ -2229,6 +2241,15 @@ impl ModuleBuilder { value: ExternModuleParameterValue::RawVerilog(raw_verilog.intern()), }); } + #[track_caller] + pub fn extern_module_simulation(&self, generator: G) { + let mut impl_ = self.impl_.borrow_mut(); + let simulation = &mut impl_.body.builder_extern_body().simulation; + if simulation.is_some() { + panic!("already added an extern module simulation"); + } + *simulation = Some(ExternModuleSimulation::new(generator)); + } } #[track_caller] diff --git a/crates/fayalite/src/module/transform/visit.rs b/crates/fayalite/src/module/transform/visit.rs index 662a578..526a62c 100644 --- a/crates/fayalite/src/module/transform/visit.rs +++ b/crates/fayalite/src/module/transform/visit.rs @@ -31,6 +31,7 @@ use crate::{ phantom_const::PhantomConst, reg::Reg, reset::{AsyncReset, Reset, ResetType, SyncReset}, + sim::ExternModuleSimulation, source_location::SourceLocation, ty::{CanonicalType, Type}, wire::Wire, diff --git a/crates/fayalite/src/sim.rs b/crates/fayalite/src/sim.rs index 96f6dd9..a5d7d13 100644 --- a/crates/fayalite/src/sim.rs +++ b/crates/fayalite/src/sim.rs @@ -15,12 +15,15 @@ use crate::{ ExprEnum, Flow, ToLiteralBits, }, int::{BoolOrIntType, IntType, SIntValue, UIntValue}, - intern::{Intern, Interned, Memoize}, + intern::{ + Intern, Interned, InternedCompare, Memoize, PtrEqWithTypeId, SupportsPtrEqWithTypeId, + }, memory::PortKind, module::{ - transform::deduce_resets::deduce_resets, AnnotatedModuleIO, Block, Id, InstantiatedModule, - ModuleBody, NameId, NormalModuleBody, ScopedNameId, Stmt, StmtConnect, StmtDeclaration, - StmtFormal, StmtIf, StmtInstance, StmtMatch, StmtReg, StmtWire, TargetInInstantiatedModule, + transform::deduce_resets::deduce_resets, AnnotatedModuleIO, Block, ExternModuleBody, Id, + InstantiatedModule, ModuleBody, NameId, NormalModuleBody, ScopedNameId, Stmt, StmtConnect, + StmtDeclaration, StmtFormal, StmtIf, StmtInstance, StmtMatch, StmtReg, StmtWire, + TargetInInstantiatedModule, }, prelude::*, reset::{ResetType, ResetTypeDispatch}, @@ -51,7 +54,19 @@ use petgraph::{ }, }; use std::{ - borrow::Cow, collections::BTreeSet, fmt, marker::PhantomData, mem, ops::IndexMut, sync::Arc, + any::Any, + borrow::Cow, + cell::RefCell, + collections::BTreeSet, + fmt, + future::{Future, IntoFuture}, + marker::PhantomData, + mem, + ops::IndexMut, + pin::Pin, + rc::Rc, + sync::Arc, + task::Poll, }; mod interpreter; @@ -1617,12 +1632,21 @@ impl fmt::Debug for DebugOpaque { } } +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +struct CompiledExternModule { + io_ty: Bundle, + module_io_targets: Interned<[Target]>, + module_io: Interned<[CompiledValue]>, + simulation: ExternModuleSimulation, +} + #[derive(Debug)] pub struct Compiler { insns: Insns, original_base_module: Interned>, base_module: Interned>, modules: HashMap, + extern_modules: Vec, compiled_values: HashMap>, compiled_exprs: HashMap, CompiledExpr>, compiled_exprs_to_values: HashMap, CompiledValue>, @@ -1651,6 +1675,7 @@ impl Compiler { original_base_module, base_module, modules: HashMap::new(), + extern_modules: Vec::new(), compiled_values: HashMap::new(), compiled_exprs: HashMap::new(), compiled_exprs_to_values: HashMap::new(), @@ -4676,8 +4701,28 @@ impl Compiler { ModuleBody::Normal(NormalModuleBody { body }) => { self.compile_block(module, body, Interned::default(), &mut trace_decls); } - ModuleBody::Extern(_extern_module_body) => { - todo!("simulating extern module: {:?}", module); + ModuleBody::Extern(ExternModuleBody { + verilog_name: _, + parameters: _, + simulation, + }) => { + let Some(simulation) = simulation else { + panic!( + "can't simulate extern module without extern_module_simulation: {}", + module.leaf_module().source_location() + ); + }; + self.extern_modules.push(CompiledExternModule { + io_ty: module.leaf_module().io_ty(), + module_io_targets: module + .leaf_module() + .module_io() + .iter() + .map(|v| Target::from(v.module_io)) + .collect(), + module_io, + simulation, + }); } } let hashbrown::hash_map::Entry::Vacant(entry) = self.modules.entry(*module) else { @@ -4958,6 +5003,7 @@ impl Compiler { Compiled { insns: Insns::from(self.insns).intern_sized(), base_module, + extern_modules: Intern::intern_owned(self.extern_modules), io: Instance::new_unchecked( ScopedNameId( NameId("".intern(), Id::new()), @@ -4990,6 +5036,7 @@ struct CompiledModule { pub struct Compiled { insns: Interned>, base_module: CompiledModule, + extern_modules: Interned<[CompiledExternModule]>, io: Instance, traces: SimTraces]>>, trace_memories: Interned<[(StatePartIndex, TraceMem)]>, @@ -5004,6 +5051,7 @@ impl Compiled { let Self { insns, base_module, + extern_modules, io, traces, trace_memories, @@ -5012,6 +5060,7 @@ impl Compiled { Compiled { insns, base_module, + extern_modules, io: Instance::from_canonical(io.canonical()), traces, trace_memories, @@ -5022,6 +5071,7 @@ impl Compiled { let Compiled { insns, base_module, + extern_modules, io, traces, trace_memories, @@ -5030,6 +5080,7 @@ impl Compiled { Self { insns, base_module, + extern_modules, io: Instance::from_canonical(io.canonical()), traces, trace_memories, @@ -6474,62 +6525,85 @@ macro_rules! impl_to_sim_value_for_int_value { impl_to_sim_value_for_int_value!(UIntValue, UInt, UIntType); impl_to_sim_value_for_int_value!(SIntValue, SInt, SIntType); -struct SimulationImpl { - state: interpreter::State, - io: Expr, - uninitialized_inputs: HashMap>, - io_targets: HashMap>, - made_initial_step: bool, - needs_settle: bool, - trace_decls: TraceModule, - traces: SimTraces]>>, - trace_memories: HashMap, TraceMem>, - trace_writers: Vec>, - instant: SimInstant, - clocks_triggered: Interned<[StatePartIndex]>, - breakpoints: Option, +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +enum MaybeNeedsSettle { + NeedsSettle(S), + NoSettleNeeded(N), } -impl fmt::Debug for SimulationImpl { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.debug_fmt(None, f) +impl MaybeNeedsSettle { + fn map(self, f: impl FnOnce(T) -> U) -> MaybeNeedsSettle { + match self { + MaybeNeedsSettle::NeedsSettle(v) => MaybeNeedsSettle::NeedsSettle(f(v)), + MaybeNeedsSettle::NoSettleNeeded(v) => MaybeNeedsSettle::NoSettleNeeded(f(v)), + } } } -impl SimulationImpl { - fn debug_fmt(&self, io: Option<&dyn fmt::Debug>, f: &mut fmt::Formatter<'_>) -> fmt::Result { +// workaround implementing FnOnce not being stable +trait MaybeNeedsSettleFn { + type Output; + + fn call(self, arg: A) -> Self::Output; +} + +impl O, A, O> MaybeNeedsSettleFn for T { + type Output = O; + + fn call(self, arg: A) -> Self::Output { + self(arg) + } +} + +impl MaybeNeedsSettle { + fn apply_no_settle(self, arg: T) -> MaybeNeedsSettle + where + N: MaybeNeedsSettleFn, + { + match self { + MaybeNeedsSettle::NeedsSettle(v) => MaybeNeedsSettle::NeedsSettle(v), + MaybeNeedsSettle::NoSettleNeeded(v) => MaybeNeedsSettle::NoSettleNeeded(v.call(arg)), + } + } +} + +struct SimulationModuleState { + base_targets: Vec, + uninitialized_ios: HashMap>, + io_targets: HashMap>, + did_initial_settle: bool, +} + +impl fmt::Debug for SimulationModuleState { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let Self { - state, - io: self_io, - uninitialized_inputs, + base_targets, + uninitialized_ios, io_targets, - made_initial_step, - needs_settle, - trace_decls, - traces, - trace_memories, - trace_writers, - instant, - clocks_triggered, - breakpoints: _, + did_initial_settle, } = self; - f.debug_struct("Simulation") - .field("state", state) - .field("io", io.unwrap_or(self_io)) - .field( - "uninitialized_inputs", - &SortedSetDebug(uninitialized_inputs), - ) - .field("io_targets", &SortedMapDebug(io_targets)) - .field("made_initial_step", made_initial_step) - .field("needs_settle", needs_settle) - .field("trace_decls", trace_decls) - .field("traces", traces) - .field("trace_memories", trace_memories) - .field("trace_writers", trace_writers) - .field("instant", instant) - .field("clocks_triggered", clocks_triggered) - .finish_non_exhaustive() + f.debug_struct("SimulationModuleState") + .field("base_targets", base_targets) + .field("uninitialized_ios", &SortedSetDebug(uninitialized_ios)) + .field("io_targets", &SortedSetDebug(io_targets)) + .field("did_initial_settle", did_initial_settle) + .finish() + } +} + +impl SimulationModuleState { + fn new(base_targets: impl IntoIterator)>) -> Self { + let mut retval = Self { + base_targets: Vec::new(), + uninitialized_ios: HashMap::new(), + io_targets: HashMap::new(), + did_initial_settle: false, + }; + for (base_target, value) in base_targets { + retval.base_targets.push(base_target); + retval.parse_io(base_target, value); + } + retval } /// returns `true` if `target` or any sub-targets are uninitialized inputs fn parse_io(&mut self, target: Target, value: CompiledValue) -> bool { @@ -6538,7 +6612,7 @@ impl SimulationImpl { CompiledTypeLayoutBody::Scalar => match target.flow() { Flow::Source => false, Flow::Sink => { - self.uninitialized_inputs.insert(target, vec![]); + self.uninitialized_ios.insert(target, vec![]); true } Flow::Duplex => unreachable!(), @@ -6557,7 +6631,7 @@ impl SimulationImpl { if sub_targets.is_empty() { false } else { - self.uninitialized_inputs.insert(target, sub_targets); + self.uninitialized_ios.insert(target, sub_targets); true } } @@ -6575,20 +6649,406 @@ impl SimulationImpl { if sub_targets.is_empty() { false } else { - self.uninitialized_inputs.insert(target, sub_targets); + self.uninitialized_ios.insert(target, sub_targets); true } } } } + fn mark_target_as_initialized(&mut self, mut target: Target) { + fn remove_target_and_children( + uninitialized_ios: &mut HashMap>, + target: Target, + ) { + let Some(children) = uninitialized_ios.remove(&target) else { + return; + }; + for child in children { + remove_target_and_children(uninitialized_ios, child); + } + } + remove_target_and_children(&mut self.uninitialized_ios, target); + while let Some(target_child) = target.child() { + let parent = target_child.parent(); + for child in self + .uninitialized_ios + .get(&*parent) + .map(|v| &**v) + .unwrap_or(&[]) + { + if self.uninitialized_ios.contains_key(child) { + return; + } + } + target = *parent; + self.uninitialized_ios.remove(&target); + } + } + #[track_caller] + fn get_io( + &self, + mut target: Target, + which_module: WhichModule, + ) -> CompiledValue { + if let Some(&retval) = self.io_targets.get(&target) { + return retval; + } + loop { + target = match target { + Target::Base(_) => break, + Target::Child(child) => { + match *child.path_element() { + TargetPathElement::BundleField(_) | TargetPathElement::ArrayElement(_) => {} + TargetPathElement::DynArrayElement(_) => panic!( + "simulator read/write expression must not have dynamic array indexes" + ), + } + *child.parent() + } + }; + } + match which_module { + WhichModule::Main => panic!( + "simulator read/write expression must be \ + an array element/field of `Simulation::io()`" + ), + WhichModule::Extern { .. } => panic!( + "simulator read/write expression must be \ + one of this module's inputs/outputs or an \ + array element/field of one of this module's inputs/outputs" + ), + } + } + #[track_caller] + fn read_helper( + &self, + io: Expr, + which_module: WhichModule, + ) -> MaybeNeedsSettle> { + let Some(target) = io.target() else { + match which_module { + WhichModule::Main => panic!( + "can't read from an expression that's not a field/element of `Simulation::io()`" + ), + WhichModule::Extern { .. } => panic!( + "can't read from an expression that's not based on one of this module's inputs/outputs" + ), + } + }; + let compiled_value = self.get_io(*target, which_module); + match target.flow() { + Flow::Source => { + if !self.uninitialized_ios.is_empty() { + match which_module { + WhichModule::Main => { + panic!("can't read from an output before initializing all inputs"); + } + WhichModule::Extern { .. } => { + panic!("can't read from an input before initializing all outputs"); + } + } + } + MaybeNeedsSettle::NeedsSettle(compiled_value) + } + Flow::Sink => { + if self.uninitialized_ios.contains_key(&*target) { + match which_module { + WhichModule::Main => panic!("can't read from an uninitialized input"), + WhichModule::Extern { .. } => { + panic!("can't read from an uninitialized output"); + } + } + } + MaybeNeedsSettle::NoSettleNeeded(compiled_value) + } + Flow::Duplex => unreachable!(), + } + } + #[track_caller] + fn write_helper( + &mut self, + io: Expr, + which_module: WhichModule, + ) -> CompiledValue { + let Some(target) = io.target() else { + match which_module { + WhichModule::Main => panic!( + "can't write to an expression that's not a field/element of `Simulation::io()`" + ), + WhichModule::Extern { .. } => panic!( + "can't write to an expression that's not based on one of this module's outputs" + ), + } + }; + let compiled_value = self.get_io(*target, which_module); + match target.flow() { + Flow::Source => match which_module { + WhichModule::Main => panic!("can't write to an output"), + WhichModule::Extern { .. } => panic!("can't write to an input"), + }, + Flow::Sink => {} + Flow::Duplex => unreachable!(), + } + if !self.did_initial_settle { + self.mark_target_as_initialized(*target); + } + compiled_value + } +} + +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +enum WaitTarget { + /// Settle is less than Instant + Settle, + Instant(SimInstant), +} + +impl PartialOrd for WaitTarget { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +impl Ord for WaitTarget { + fn cmp(&self, other: &Self) -> std::cmp::Ordering { + match (self, other) { + (WaitTarget::Settle, WaitTarget::Settle) => std::cmp::Ordering::Equal, + (WaitTarget::Settle, WaitTarget::Instant(_)) => std::cmp::Ordering::Less, + (WaitTarget::Instant(_), WaitTarget::Settle) => std::cmp::Ordering::Greater, + (WaitTarget::Instant(l), WaitTarget::Instant(r)) => l.cmp(r), + } + } +} + +struct SimulationExternModuleState { + module_state: SimulationModuleState, + io_ty: Bundle, + sim: ExternModuleSimulation, + running_generator: Option + 'static>>>, + wait_target: Option, +} + +impl fmt::Debug for SimulationExternModuleState { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let Self { + module_state, + io_ty, + sim, + running_generator, + wait_target, + } = self; + f.debug_struct("SimulationExternModuleState") + .field("module_state", module_state) + .field("io_ty", io_ty) + .field("sim", sim) + .field( + "running_generator", + &running_generator.as_ref().map(|_| DebugAsDisplay("...")), + ) + .field("wait_target", wait_target) + .finish() + } +} + +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +enum WhichModule { + Main, + Extern { module_index: usize }, +} + +struct ReadBitFn { + compiled_value: CompiledValue, +} + +impl MaybeNeedsSettleFn<&'_ mut interpreter::State> for ReadBitFn { + type Output = bool; + + fn call(self, state: &mut interpreter::State) -> Self::Output { + match self.compiled_value.range.len() { + TypeLen::A_SMALL_SLOT => { + state.small_slots[self.compiled_value.range.small_slots.start] != 0 + } + TypeLen::A_BIG_SLOT => !state.big_slots[self.compiled_value.range.big_slots.start] + .clone() + .is_zero(), + _ => unreachable!(), + } + } +} + +struct ReadBoolOrIntFn { + compiled_value: CompiledValue, + io: Expr, +} + +impl MaybeNeedsSettleFn<&'_ mut interpreter::State> for ReadBoolOrIntFn { + type Output = I::Value; + + fn call(self, state: &mut interpreter::State) -> Self::Output { + let Self { compiled_value, io } = self; + match compiled_value.range.len() { + TypeLen::A_SMALL_SLOT => Expr::ty(io) + .value_from_int_wrapping(state.small_slots[compiled_value.range.small_slots.start]), + TypeLen::A_BIG_SLOT => Expr::ty(io).value_from_int_wrapping( + state.big_slots[compiled_value.range.big_slots.start].clone(), + ), + _ => unreachable!(), + } + } +} + +struct ReadFn { + compiled_value: CompiledValue, + io: Expr, +} + +impl MaybeNeedsSettleFn<&'_ mut interpreter::State> for ReadFn { + type Output = SimValue; + + fn call(self, state: &mut interpreter::State) -> Self::Output { + let Self { compiled_value, io } = self; + let mut bits = BitVec::repeat(false, compiled_value.layout.ty.bit_width()); + SimulationImpl::read_write_sim_value_helper( + state, + compiled_value, + &mut bits, + |_signed, bits, value| ::copy_bits_from_bigint_wrapping(value, bits), + |_signed, bits, value| { + let bytes = value.to_le_bytes(); + let bitslice = BitSlice::::from_slice(&bytes); + bits.clone_from_bitslice(&bitslice[..bits.len()]); + }, + ); + SimValue { + ty: Expr::ty(io), + bits, + } + } +} + +struct GeneratorWaker; + +impl std::task::Wake for GeneratorWaker { + fn wake(self: Arc) { + panic!("can't await other kinds of futures in function passed to ExternalModuleSimulation"); + } +} + +#[derive(Default)] +struct ReadyToRunSet { + state_ready_to_run: bool, + extern_modules_ready_to_run: Vec, +} + +impl ReadyToRunSet { + fn clear(&mut self) { + let Self { + state_ready_to_run, + extern_modules_ready_to_run, + } = self; + *state_ready_to_run = false; + extern_modules_ready_to_run.clear(); + } +} + +struct SimulationImpl { + state: interpreter::State, + io: Expr, + main_module: SimulationModuleState, + extern_modules: Box<[SimulationExternModuleState]>, + state_ready_to_run: bool, + trace_decls: TraceModule, + traces: SimTraces]>>, + trace_memories: HashMap, TraceMem>, + trace_writers: Vec>, + instant: SimInstant, + clocks_triggered: Interned<[StatePartIndex]>, + breakpoints: Option, + generator_waker: std::task::Waker, +} + +impl fmt::Debug for SimulationImpl { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.debug_fmt(None, f) + } +} + +impl SimulationImpl { + fn debug_fmt(&self, io: Option<&dyn fmt::Debug>, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let Self { + state, + io: self_io, + main_module, + extern_modules, + state_ready_to_run, + trace_decls, + traces, + trace_memories, + trace_writers, + instant, + clocks_triggered, + breakpoints: _, + generator_waker: _, + } = self; + f.debug_struct("Simulation") + .field("state", state) + .field("io", io.unwrap_or(self_io)) + .field("main_module", main_module) + .field("extern_modules", extern_modules) + .field("state_ready_to_run", state_ready_to_run) + .field("trace_decls", trace_decls) + .field("traces", traces) + .field("trace_memories", trace_memories) + .field("trace_writers", trace_writers) + .field("instant", instant) + .field("clocks_triggered", clocks_triggered) + .finish_non_exhaustive() + } fn new(compiled: Compiled) -> Self { - let mut retval = Self { + let io_target = Target::from(compiled.io); + let extern_modules = Box::from_iter(compiled.extern_modules.iter().map( + |&CompiledExternModule { + io_ty, + module_io_targets, + module_io, + simulation, + }| { + SimulationExternModuleState { + module_state: SimulationModuleState::new( + module_io_targets + .iter() + .copied() + .zip(module_io.iter().copied()), + ), + io_ty, + sim: simulation, + running_generator: None, + wait_target: Some(WaitTarget::Settle), + } + }, + )); + Self { state: State::new(compiled.insns), io: compiled.io.to_expr(), - uninitialized_inputs: HashMap::new(), - io_targets: HashMap::new(), - made_initial_step: false, - needs_settle: true, + main_module: SimulationModuleState::new( + compiled + .io + .ty() + .fields() + .into_iter() + .zip(compiled.base_module.module_io) + .map(|(BundleField { name, .. }, value)| { + ( + io_target.join( + TargetPathElement::from(TargetPathBundleField { name }) + .intern_sized(), + ), + value, + ) + }), + ), + extern_modules, + state_ready_to_run: true, trace_decls: compiled.base_module.trace_decls, traces: SimTraces(Box::from_iter(compiled.traces.0.iter().map( |&SimTrace { @@ -6606,22 +7066,8 @@ impl SimulationImpl { instant: SimInstant::START, clocks_triggered: compiled.clocks_triggered, breakpoints: None, - }; - let io_target = Target::from(compiled.io); - for (BundleField { name, .. }, value) in compiled - .io - .ty() - .fields() - .into_iter() - .zip(compiled.base_module.module_io) - { - retval.parse_io( - io_target - .join(TargetPathElement::from(TargetPathBundleField { name }).intern_sized()), - value, - ); + generator_waker: Arc::new(GeneratorWaker).into(), } - retval } fn write_traces( &mut self, @@ -6759,9 +7205,92 @@ impl SimulationImpl { } } #[track_caller] - fn advance_time(&mut self, duration: SimDuration) { - self.settle(); - self.instant += duration; + fn advance_time(this_ref: &Rc>, duration: SimDuration) { + let instant = this_ref.borrow().instant + duration; + Self::run_until(this_ref, WaitTarget::Instant(instant)); + } + #[must_use] + fn yield_advance_time_or_settle( + this: Rc>, + module_index: usize, + duration: Option, + ) -> impl Future + 'static { + struct MyGenerator { + sim: Rc>, + yielded_at_all: bool, + module_index: usize, + target: WaitTarget, + } + impl Future for MyGenerator { + type Output = (); + + fn poll( + mut self: Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + ) -> Poll { + let this = &mut *self; + let yielded_at_all = mem::replace(&mut this.yielded_at_all, true); + let mut sim = this.sim.borrow_mut(); + let sim = &mut *sim; + assert!(cx.waker().will_wake(&sim.generator_waker), "can't use ExternModuleSimulationState's methods outside of ExternModuleSimulation"); + if let WaitTarget::Instant(target) = this.target { + if target < sim.instant { + this.target = WaitTarget::Settle; + } else if yielded_at_all && target == sim.instant { + this.target = WaitTarget::Settle; + } + } + if let WaitTarget::Settle = this.target { + if yielded_at_all { + return Poll::Ready(()); + } + } + let wait_target = sim.extern_modules[this.module_index] + .wait_target + .get_or_insert(this.target); + *wait_target = (*wait_target).min(this.target); + Poll::Pending + } + } + let target = duration.map_or(WaitTarget::Settle, |duration| { + WaitTarget::Instant(this.borrow().instant + duration) + }); + MyGenerator { + sim: this, + yielded_at_all: false, + module_index, + target, + } + } + /// returns the next `WaitTarget` and the set of things ready to run then. + fn get_ready_to_run_set(&self, ready_to_run_set: &mut ReadyToRunSet) -> Option { + ready_to_run_set.clear(); + let mut wait_target = None; + if self.state_ready_to_run { + ready_to_run_set.state_ready_to_run = true; + wait_target = Some(WaitTarget::Settle); + } + for (module_index, extern_module) in self.extern_modules.iter().enumerate() { + let Some(extern_module_wait_target) = extern_module.wait_target else { + continue; + }; + if let Some(wait_target) = &mut wait_target { + match extern_module_wait_target.cmp(wait_target) { + std::cmp::Ordering::Less => ready_to_run_set.clear(), + std::cmp::Ordering::Equal => {} + std::cmp::Ordering::Greater => continue, + } + } else { + wait_target = Some(extern_module_wait_target); + } + ready_to_run_set + .extern_modules_ready_to_run + .push(module_index); + } + wait_target + } + fn set_instant_no_sim(&mut self, instant: SimInstant) { + self.instant = instant; self.for_each_trace_writer_storing_error(|this, mut trace_writer_state| { match &mut trace_writer_state { TraceWriterState::Decls(_) | TraceWriterState::Init(_) => unreachable!(), @@ -6774,53 +7303,126 @@ impl SimulationImpl { }); } #[track_caller] - fn settle(&mut self) { + fn run_until(this_ref: &Rc>, run_target: WaitTarget) { + let mut this = this_ref.borrow_mut(); + let mut ready_to_run_set = ReadyToRunSet::default(); + let generator_waker = this.generator_waker.clone(); assert!( - self.uninitialized_inputs.is_empty(), + this.main_module.uninitialized_ios.is_empty(), "didn't initialize all inputs", ); - for _ in 0..100000 { - if !self.needs_settle { - return; - } - self.state.setup_call(0); - if self.breakpoints.is_some() { - loop { - match self - .state - .run(self.breakpoints.as_mut().expect("just checked")) - { - RunResult::Break(break_action) => { - println!( - "hit breakpoint at:\n{:?}", - self.state.debug_insn_at(self.state.pc), - ); - match break_action { - BreakAction::DumpStateAndContinue => { - println!("{self:#?}"); - } - BreakAction::Continue => {} - } - } - RunResult::Return(()) => break, - } + match run_target { + WaitTarget::Settle => {} + WaitTarget::Instant(run_target) => assert!(run_target >= this.instant), + } + let mut settle_cycle = 0; + let mut run_extern_modules = true; + loop { + assert!(settle_cycle < 100000, "settle(): took too many steps"); + settle_cycle += 1; + let next_wait_target = match this.get_ready_to_run_set(&mut ready_to_run_set) { + Some(next_wait_target) if next_wait_target <= run_target => next_wait_target, + _ => break, + }; + match next_wait_target { + WaitTarget::Settle => {} + WaitTarget::Instant(instant) => { + settle_cycle = 0; + this.set_instant_no_sim(instant); } - } else { - let RunResult::Return(()) = self.state.run(()); } - if self.made_initial_step { - self.read_traces::(); - } else { - self.read_traces::(); + if run_extern_modules { + for module_index in ready_to_run_set.extern_modules_ready_to_run.drain(..) { + let extern_module = &mut this.extern_modules[module_index]; + extern_module.wait_target = None; + let mut generator = if !extern_module.module_state.did_initial_settle { + let sim = extern_module.sim; + let io_ty = extern_module.io_ty; + drop(this); + Box::into_pin(sim.run(ExternModuleSimulationState { + sim_impl: this_ref.clone(), + module_index, + io_ty, + })) + } else if let Some(generator) = extern_module.running_generator.take() { + drop(this); + generator + } else { + continue; + }; + let generator = match generator + .as_mut() + .poll(&mut std::task::Context::from_waker(&generator_waker)) + { + Poll::Ready(()) => None, + Poll::Pending => Some(generator), + }; + this = this_ref.borrow_mut(); + this.extern_modules[module_index] + .module_state + .did_initial_settle = true; + if !this.extern_modules[module_index] + .module_state + .uninitialized_ios + .is_empty() + { + panic!( + "extern module didn't initialize all outputs before \ + waiting, settling, or reading any inputs: {}", + this.extern_modules[module_index].sim.source_location + ); + } + this.extern_modules[module_index].running_generator = generator; + } } - self.state.memory_write_log.sort_unstable(); - self.state.memory_write_log.dedup(); - self.made_initial_step = true; - self.needs_settle = self - .clocks_triggered - .iter() - .any(|i| self.state.small_slots[*i] != 0); - self.for_each_trace_writer_storing_error(|this, trace_writer_state| { + if ready_to_run_set.state_ready_to_run { + this.state_ready_to_run = false; + run_extern_modules = true; + this.state.setup_call(0); + if this.breakpoints.is_some() { + loop { + let this = &mut *this; + match this + .state + .run(this.breakpoints.as_mut().expect("just checked")) + { + RunResult::Break(break_action) => { + println!( + "hit breakpoint at:\n{:?}", + this.state.debug_insn_at(this.state.pc), + ); + match break_action { + BreakAction::DumpStateAndContinue => { + println!("{this:#?}"); + } + BreakAction::Continue => {} + } + } + RunResult::Return(()) => break, + } + } + } else { + let RunResult::Return(()) = this.state.run(()); + } + if this + .clocks_triggered + .iter() + .any(|i| this.state.small_slots[*i] != 0) + { + this.state_ready_to_run = true; + // wait for clocks to settle before running extern modules again + run_extern_modules = false; + } + } + if this.main_module.did_initial_settle { + this.read_traces::(); + } else { + this.read_traces::(); + } + this.state.memory_write_log.sort_unstable(); + this.state.memory_write_log.dedup(); + this.main_module.did_initial_settle = true; + this.for_each_trace_writer_storing_error(|this, trace_writer_state| { Ok(match trace_writer_state { TraceWriterState::Decls(trace_writer_decls) => TraceWriterState::Running( this.init_trace_writer(trace_writer_decls.write_decls( @@ -6838,114 +7440,48 @@ impl SimulationImpl { TraceWriterState::Errored(e) => TraceWriterState::Errored(e), }) }); - self.state.memory_write_log.clear(); + this.state.memory_write_log.clear(); } - panic!("settle(): took too many steps"); - } - #[track_caller] - fn get_io(&self, target: Target) -> CompiledValue { - if let Some(&retval) = self.io_targets.get(&target) { - return retval; - } - if Some(&target) == self.io.target().as_deref() - || Some(target.base()) != self.io.target().map(|v| v.base()) - { - panic!("simulator read/write expression must be an array element/field of `Simulation::io()`"); - }; - panic!("simulator read/write expression must not have dynamic array indexes"); - } - fn mark_target_as_initialized(&mut self, mut target: Target) { - fn remove_target_and_children( - uninitialized_inputs: &mut HashMap>, - target: Target, - ) { - let Some(children) = uninitialized_inputs.remove(&target) else { - return; - }; - for child in children { - remove_target_and_children(uninitialized_inputs, child); - } - } - remove_target_and_children(&mut self.uninitialized_inputs, target); - while let Some(target_child) = target.child() { - let parent = target_child.parent(); - for child in self - .uninitialized_inputs - .get(&*parent) - .map(|v| &**v) - .unwrap_or(&[]) - { - if self.uninitialized_inputs.contains_key(child) { - return; - } - } - target = *parent; - self.uninitialized_inputs.remove(&target); + match run_target { + WaitTarget::Settle => {} + WaitTarget::Instant(instant) => this.set_instant_no_sim(instant), } } #[track_caller] - fn read_helper(&mut self, io: Expr) -> CompiledValue { - let Some(target) = io.target() else { - panic!("can't read from expression that's not a field/element of `Simulation::io()`"); - }; - let compiled_value = self.get_io(*target); - if self.made_initial_step { - self.settle(); - } else { - match target.flow() { - Flow::Source => { - if !self.uninitialized_inputs.is_empty() { - panic!( - "can't read from an output before the simulation has made any steps" - ); - } - self.settle(); - } - Flow::Sink => { - if self.uninitialized_inputs.contains_key(&*target) { - panic!("can't read from an uninitialized input"); - } - } - Flow::Duplex => unreachable!(), - } - } - compiled_value + fn settle(this_ref: &Rc>) { + Self::run_until(this_ref, WaitTarget::Settle); } - #[track_caller] - fn write_helper(&mut self, io: Expr) -> CompiledValue { - let Some(target) = io.target() else { - panic!("can't write to an expression that's not a field/element of `Simulation::io()`"); - }; - let compiled_value = self.get_io(*target); - match target.flow() { - Flow::Source => { - panic!("can't write to an output"); - } - Flow::Sink => {} - Flow::Duplex => unreachable!(), + fn get_module(&self, which_module: WhichModule) -> &SimulationModuleState { + match which_module { + WhichModule::Main => &self.main_module, + WhichModule::Extern { module_index } => &self.extern_modules[module_index].module_state, } - if !self.made_initial_step { - self.mark_target_as_initialized(*target); - } - self.needs_settle = true; - compiled_value } - #[track_caller] - fn read_bit(&mut self, io: Expr) -> bool { - let compiled_value = self.read_helper(Expr::canonical(io)); - match compiled_value.range.len() { - TypeLen::A_SMALL_SLOT => { - self.state.small_slots[compiled_value.range.small_slots.start] != 0 + fn get_module_mut(&mut self, which_module: WhichModule) -> &mut SimulationModuleState { + match which_module { + WhichModule::Main => &mut self.main_module, + WhichModule::Extern { module_index } => { + &mut self.extern_modules[module_index].module_state } - TypeLen::A_BIG_SLOT => !self.state.big_slots[compiled_value.range.big_slots.start] - .clone() - .is_zero(), - _ => unreachable!(), } } #[track_caller] - fn write_bit(&mut self, io: Expr, value: bool) { - let compiled_value = self.write_helper(io); + fn read_bit( + &mut self, + io: Expr, + which_module: WhichModule, + ) -> MaybeNeedsSettle { + self.get_module(which_module) + .read_helper(Expr::canonical(io), which_module) + .map(|compiled_value| ReadBitFn { compiled_value }) + .apply_no_settle(&mut self.state) + } + #[track_caller] + fn write_bit(&mut self, io: Expr, value: bool, which_module: WhichModule) { + let compiled_value = self + .get_module_mut(which_module) + .write_helper(io, which_module); + self.state_ready_to_run = true; match compiled_value.range.len() { TypeLen::A_SMALL_SLOT => { self.state.small_slots[compiled_value.range.small_slots.start] = value as _; @@ -6957,21 +7493,27 @@ impl SimulationImpl { } } #[track_caller] - fn read_bool_or_int(&mut self, io: Expr) -> I::Value { - let compiled_value = self.read_helper(Expr::canonical(io)); - match compiled_value.range.len() { - TypeLen::A_SMALL_SLOT => Expr::ty(io).value_from_int_wrapping( - self.state.small_slots[compiled_value.range.small_slots.start], - ), - TypeLen::A_BIG_SLOT => Expr::ty(io).value_from_int_wrapping( - self.state.big_slots[compiled_value.range.big_slots.start].clone(), - ), - _ => unreachable!(), - } + fn read_bool_or_int( + &mut self, + io: Expr, + which_module: WhichModule, + ) -> MaybeNeedsSettle, I::Value> { + self.get_module(which_module) + .read_helper(Expr::canonical(io), which_module) + .map(|compiled_value| ReadBoolOrIntFn { compiled_value, io }) + .apply_no_settle(&mut self.state) } #[track_caller] - fn write_bool_or_int(&mut self, io: Expr, value: I::Value) { - let compiled_value = self.write_helper(Expr::canonical(io)); + fn write_bool_or_int( + &mut self, + io: Expr, + value: I::Value, + which_module: WhichModule, + ) { + let compiled_value = self + .get_module_mut(which_module) + .write_helper(Expr::canonical(io), which_module); + self.state_ready_to_run = true; let value: BigInt = value.into(); match compiled_value.range.len() { TypeLen::A_SMALL_SLOT => { @@ -6989,7 +7531,7 @@ impl SimulationImpl { } #[track_caller] fn read_write_sim_value_helper( - &mut self, + state: &mut interpreter::State, compiled_value: CompiledValue, bits: &mut BitSlice, read_write_big_scalar: impl Fn(bool, &mut BitSlice, &mut BigInt) + Copy, @@ -7014,12 +7556,12 @@ impl SimulationImpl { TypeLen::A_SMALL_SLOT => read_write_small_scalar( signed, bits, - &mut self.state.small_slots[compiled_value.range.small_slots.start], + &mut state.small_slots[compiled_value.range.small_slots.start], ), TypeLen::A_BIG_SLOT => read_write_big_scalar( signed, bits, - &mut self.state.big_slots[compiled_value.range.big_slots.start], + &mut state.big_slots[compiled_value.range.big_slots.start], ), _ => unreachable!(), } @@ -7028,7 +7570,8 @@ impl SimulationImpl { let ty = ::from_canonical(compiled_value.layout.ty); let element_bit_width = ty.element().bit_width(); for element_index in 0..ty.len() { - self.read_write_sim_value_helper( + Self::read_write_sim_value_helper( + state, CompiledValue { layout: *element, range: compiled_value @@ -7052,7 +7595,8 @@ impl SimulationImpl { }, ) in ty.fields().iter().zip(ty.field_offsets()).zip(fields) { - self.read_write_sim_value_helper( + Self::read_write_sim_value_helper( + state, CompiledValue { layout: field_layout, range: compiled_value.range.slice(TypeIndexRange::new( @@ -7070,29 +7614,30 @@ impl SimulationImpl { } } #[track_caller] - fn read(&mut self, io: Expr) -> SimValue { - let compiled_value = self.read_helper(io); - let mut bits = BitVec::repeat(false, compiled_value.layout.ty.bit_width()); - self.read_write_sim_value_helper( - compiled_value, - &mut bits, - |_signed, bits, value| ::copy_bits_from_bigint_wrapping(value, bits), - |_signed, bits, value| { - let bytes = value.to_le_bytes(); - let bitslice = BitSlice::::from_slice(&bytes); - bits.clone_from_bitslice(&bitslice[..bits.len()]); - }, - ); - SimValue { - ty: Expr::ty(io), - bits, - } + fn read( + &mut self, + io: Expr, + which_module: WhichModule, + ) -> MaybeNeedsSettle> { + self.get_module(which_module) + .read_helper(io, which_module) + .map(|compiled_value| ReadFn { compiled_value, io }) + .apply_no_settle(&mut self.state) } #[track_caller] - fn write(&mut self, io: Expr, value: SimValue) { - let compiled_value = self.write_helper(io); + fn write( + &mut self, + io: Expr, + value: SimValue, + which_module: WhichModule, + ) { + let compiled_value = self + .get_module_mut(which_module) + .write_helper(io, which_module); + self.state_ready_to_run = true; assert_eq!(Expr::ty(io), value.ty()); - self.read_write_sim_value_helper( + Self::read_write_sim_value_helper( + &mut self.state, compiled_value, &mut value.into_bits(), |signed, bits, value| { @@ -7112,6 +7657,35 @@ impl SimulationImpl { }, ); } + #[track_caller] + fn settle_if_needed(this_ref: &Rc>, v: MaybeNeedsSettle) -> O + where + for<'a> F: MaybeNeedsSettleFn<&'a mut interpreter::State, Output = O>, + { + match v { + MaybeNeedsSettle::NeedsSettle(v) => { + Self::settle(this_ref); + v.call(&mut this_ref.borrow_mut().state) + } + MaybeNeedsSettle::NoSettleNeeded(v) => v, + } + } + async fn yield_settle_if_needed( + this_ref: &Rc>, + module_index: usize, + v: MaybeNeedsSettle, + ) -> O + where + for<'a> F: MaybeNeedsSettleFn<&'a mut interpreter::State, Output = O>, + { + match v { + MaybeNeedsSettle::NeedsSettle(v) => { + Self::yield_advance_time_or_settle(this_ref.clone(), module_index, None).await; + v.call(&mut this_ref.borrow_mut().state) + } + MaybeNeedsSettle::NoSettleNeeded(v) => v, + } + } fn close_all_trace_writers(&mut self) -> std::io::Result<()> { let trace_writers = mem::take(&mut self.trace_writers); let mut retval = Ok(()); @@ -7181,17 +7755,17 @@ impl SimulationImpl { self.trace_writers = trace_writers; retval } - fn close(mut self) -> std::io::Result<()> { - if self.made_initial_step { - self.settle(); + fn close(this: Rc>) -> std::io::Result<()> { + if this.borrow().main_module.did_initial_settle { + Self::settle(&this); } - self.close_all_trace_writers() + this.borrow_mut().close_all_trace_writers() } - fn flush_traces(&mut self) -> std::io::Result<()> { - if self.made_initial_step { - self.settle(); + fn flush_traces(this_ref: &Rc>) -> std::io::Result<()> { + if this_ref.borrow().main_module.did_initial_settle { + Self::settle(this_ref); } - self.for_each_trace_writer_getting_error( + this_ref.borrow_mut().for_each_trace_writer_getting_error( |this, trace_writer: TraceWriterState| match trace_writer { TraceWriterState::Decls(v) => { let mut v = v.write_decls( @@ -7225,7 +7799,7 @@ impl Drop for SimulationImpl { } pub struct Simulation { - sim_impl: SimulationImpl, + sim_impl: Rc>, io: Expr, } @@ -7272,24 +7846,117 @@ impl fmt::Debug for SortedMapDebug<'_, K, V> { impl fmt::Debug for Simulation { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let Self { sim_impl, io } = self; - sim_impl.debug_fmt(Some(io), f) + sim_impl.borrow().debug_fmt(Some(io), f) } } +macro_rules! impl_simulation_methods { + ( + async_await = ($($async:tt, $await:tt)?), + track_caller = ($(#[$track_caller:tt])?), + which_module = |$self:ident| $which_module:expr, + ) => { + $(#[$track_caller])? + pub $($async)? fn read_bool_or_int(&mut $self, io: Expr) -> I::Value { + let retval = $self + .sim_impl + .borrow_mut() + .read_bool_or_int(io, $which_module); + $self.settle_if_needed(retval)$(.$await)? + } + $(#[$track_caller])? + pub $($async)? fn write_bool_or_int( + &mut $self, + io: Expr, + value: impl ToExpr, + ) { + let value = value.to_expr(); + assert_eq!(Expr::ty(io), Expr::ty(value), "type mismatch"); + let value = value + .to_literal_bits() + .expect("the value that is being written to an input must be a literal"); + $self.sim_impl.borrow_mut().write_bool_or_int( + io, + I::bits_to_value(Cow::Borrowed(&value)), + $which_module, + ); + } + $(#[$track_caller])? + pub $($async)? fn write_clock(&mut $self, io: Expr, value: bool) { + $self.sim_impl + .borrow_mut() + .write_bit(Expr::canonical(io), value, $which_module); + } + $(#[$track_caller])? + pub $($async)? fn read_clock(&mut $self, io: Expr) -> bool { + let retval = $self + .sim_impl + .borrow_mut() + .read_bit(Expr::canonical(io), $which_module); + $self.settle_if_needed(retval)$(.$await)? + } + $(#[$track_caller])? + pub $($async)? fn write_bool(&mut $self, io: Expr, value: bool) { + $self.sim_impl + .borrow_mut() + .write_bit(Expr::canonical(io), value, $which_module); + } + $(#[$track_caller])? + pub $($async)? fn read_bool(&mut $self, io: Expr) -> bool { + let retval = $self + .sim_impl + .borrow_mut() + .read_bit(Expr::canonical(io), $which_module); + $self.settle_if_needed(retval)$(.$await)? + } + $(#[$track_caller])? + pub $($async)? fn write_reset(&mut $self, io: Expr, value: bool) { + $self.sim_impl + .borrow_mut() + .write_bit(Expr::canonical(io), value, $which_module); + } + $(#[$track_caller])? + pub $($async)? fn read_reset(&mut $self, io: Expr) -> bool { + let retval = $self + .sim_impl + .borrow_mut() + .read_bit(Expr::canonical(io), $which_module); + $self.settle_if_needed(retval)$(.$await)? + } + $(#[$track_caller])? + pub $($async)? fn read(&mut $self, io: Expr) -> SimValue { + let retval = $self + .sim_impl + .borrow_mut() + .read(Expr::canonical(io), $which_module); + SimValue::from_canonical($self.settle_if_needed(retval)$(.$await)?) + } + $(#[$track_caller])? + pub $($async)? fn write>(&mut $self, io: Expr, value: V) { + $self.sim_impl.borrow_mut().write( + Expr::canonical(io), + value.into_sim_value(Expr::ty(io)).into_canonical(), + $which_module, + ); + } + }; +} + impl Simulation { pub fn new(module: Interned>) -> Self { Self::from_compiled(Compiled::new(module)) } pub fn add_trace_writer(&mut self, writer: W) { self.sim_impl + .borrow_mut() .trace_writers .push(TraceWriterState::Decls(DynTraceWriterDecls::new(writer))); } pub fn flush_traces(&mut self) -> std::io::Result<()> { - self.sim_impl.flush_traces() + SimulationImpl::flush_traces(&self.sim_impl) } pub fn close(self) -> std::io::Result<()> { - self.sim_impl.close() + SimulationImpl::close(self.sim_impl) } pub fn canonical(self) -> Simulation { let Self { sim_impl, io } = self; @@ -7312,77 +7979,215 @@ impl Simulation { let sim_impl = SimulationImpl::new(compiled.canonical()); Self { io: Expr::from_bundle(sim_impl.io), - sim_impl, + sim_impl: Rc::new(RefCell::new(sim_impl)), } } #[track_caller] pub fn settle(&mut self) { - self.sim_impl.settle(); + SimulationImpl::settle(&self.sim_impl); } #[track_caller] pub fn advance_time(&mut self, duration: SimDuration) { - self.sim_impl.advance_time(duration); + SimulationImpl::advance_time(&self.sim_impl, duration); } #[track_caller] - pub fn read_bool_or_int(&mut self, io: Expr) -> I::Value { - self.sim_impl.read_bool_or_int(io) - } - #[track_caller] - pub fn write_bool_or_int( - &mut self, - io: Expr, - value: impl ToExpr, - ) { - let value = value.to_expr(); - assert_eq!(Expr::ty(io), Expr::ty(value), "type mismatch"); - let value = value - .to_literal_bits() - .expect("the value that is being written to an input must be a literal"); - self.sim_impl - .write_bool_or_int(io, I::bits_to_value(Cow::Borrowed(&value))); - } - #[track_caller] - pub fn write_clock(&mut self, io: Expr, value: bool) { - self.sim_impl.write_bit(Expr::canonical(io), value); - } - #[track_caller] - pub fn read_clock(&mut self, io: Expr) -> bool { - self.sim_impl.read_bit(Expr::canonical(io)) - } - #[track_caller] - pub fn write_bool(&mut self, io: Expr, value: bool) { - self.sim_impl.write_bit(Expr::canonical(io), value); - } - #[track_caller] - pub fn read_bool(&mut self, io: Expr) -> bool { - self.sim_impl.read_bit(Expr::canonical(io)) - } - #[track_caller] - pub fn write_reset(&mut self, io: Expr, value: bool) { - self.sim_impl.write_bit(Expr::canonical(io), value); - } - #[track_caller] - pub fn read_reset(&mut self, io: Expr) -> bool { - self.sim_impl.read_bit(Expr::canonical(io)) - } - #[track_caller] - pub fn read(&mut self, io: Expr) -> SimValue { - SimValue::from_canonical(self.sim_impl.read(Expr::canonical(io))) - } - #[track_caller] - pub fn write>(&mut self, io: Expr, value: V) { - self.sim_impl.write( - Expr::canonical(io), - value.into_sim_value(Expr::ty(io)).into_canonical(), - ); + fn settle_if_needed(&mut self, v: MaybeNeedsSettle) -> O + where + for<'a> F: MaybeNeedsSettleFn<&'a mut interpreter::State, Output = O>, + { + SimulationImpl::settle_if_needed(&self.sim_impl, v) } + impl_simulation_methods!( + async_await = (), + track_caller = (#[track_caller]), + which_module = |self| WhichModule::Main, + ); #[doc(hidden)] /// This is explicitly unstable and may be changed/removed at any time pub fn set_breakpoints_unstable(&mut self, pcs: HashSet, trace: bool) { - self.sim_impl.breakpoints = Some(BreakpointsSet { + self.sim_impl.borrow_mut().breakpoints = Some(BreakpointsSet { last_was_break: false, set: pcs, trace, }); } } + +pub struct ExternModuleSimulationState { + sim_impl: Rc>, + module_index: usize, + io_ty: T, +} + +impl fmt::Debug for ExternModuleSimulationState { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let Self { + sim_impl: _, + module_index, + io_ty, + } = self; + f.debug_struct("ExternModuleSimulationState") + .field("sim_impl", &DebugAsDisplay("...")) + .field("module_index", module_index) + .field("io_ty", io_ty) + .finish() + } +} + +impl ExternModuleSimulationState { + pub fn canonical(self) -> ExternModuleSimulationState { + let Self { + sim_impl, + module_index, + io_ty, + } = self; + ExternModuleSimulationState { + sim_impl, + module_index, + io_ty: Bundle::from_canonical(io_ty.canonical()), + } + } + pub fn from_canonical(sim: ExternModuleSimulationState) -> Self { + let ExternModuleSimulationState { + sim_impl, + module_index, + io_ty, + } = sim; + Self { + sim_impl, + module_index, + io_ty: T::from_canonical(io_ty.canonical()), + } + } + pub async fn settle(&mut self) { + SimulationImpl::yield_advance_time_or_settle(self.sim_impl.clone(), self.module_index, None) + .await + } + pub async fn advance_time(&mut self, duration: SimDuration) { + SimulationImpl::yield_advance_time_or_settle( + self.sim_impl.clone(), + self.module_index, + Some(duration), + ) + .await + } + async fn settle_if_needed(&mut self, v: MaybeNeedsSettle) -> O + where + for<'a> F: MaybeNeedsSettleFn<&'a mut interpreter::State, Output = O>, + { + SimulationImpl::yield_settle_if_needed(&self.sim_impl, self.module_index, v).await + } + impl_simulation_methods!( + async_await = (async, await), + track_caller = (), + which_module = |self| WhichModule::Extern { module_index: self.module_index }, + ); +} + +pub trait ExternModuleSimGenerator: + Clone + Eq + std::hash::Hash + Any + Send + Sync + fmt::Debug +{ + type IOType: BundleType; + + fn run<'a>( + &'a self, + sim: ExternModuleSimulationState, + ) -> impl IntoFuture + 'a; +} + +trait DynExternModuleSimGenerator: Any + Send + Sync + SupportsPtrEqWithTypeId + fmt::Debug { + fn dyn_run<'a>( + &'a self, + sim: ExternModuleSimulationState, + ) -> Box + 'a>; + #[track_caller] + fn check_io_ty(&self, io_ty: Bundle); +} + +impl DynExternModuleSimGenerator for T { + fn dyn_run<'a>( + &'a self, + sim: ExternModuleSimulationState, + ) -> Box + 'a> { + Box::new( + self.run(ExternModuleSimulationState::from_canonical(sim)) + .into_future(), + ) + } + #[track_caller] + fn check_io_ty(&self, io_ty: Bundle) { + T::IOType::from_canonical(io_ty.canonical()); + } +} + +impl InternedCompare for dyn DynExternModuleSimGenerator { + type InternedCompareKey = PtrEqWithTypeId; + + fn interned_compare_key_ref(this: &Self) -> Self::InternedCompareKey { + this.get_ptr_eq_with_type_id() + } +} + +#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)] +pub struct ExternModuleSimulation { + generator: Interned, + source_location: SourceLocation, + _phantom: PhantomData, +} + +impl ExternModuleSimulation { + pub fn new_with_loc( + source_location: SourceLocation, + generator: G, + ) -> Self { + Self { + generator: Interned::cast_unchecked( + generator.intern(), + |v| -> &dyn DynExternModuleSimGenerator { v }, + ), + source_location, + _phantom: PhantomData, + } + } + #[track_caller] + pub fn new(generator: G) -> Self { + Self::new_with_loc(SourceLocation::caller(), generator) + } + pub fn canonical(self) -> ExternModuleSimulation { + let Self { + generator, + source_location, + _phantom: _, + } = self; + ExternModuleSimulation { + generator, + source_location, + _phantom: PhantomData, + } + } + pub fn from_canonical(v: ExternModuleSimulation) -> Self { + let ExternModuleSimulation { + generator, + source_location, + _phantom: _, + } = v; + Self { + generator, + source_location, + _phantom: PhantomData, + } + } +} + +impl ExternModuleSimulation { + fn run( + &self, + sim: ExternModuleSimulationState, + ) -> Box + 'static> { + Interned::into_inner(self.generator).dyn_run(sim) + } + #[track_caller] + pub fn check_io_ty(self, io_ty: Bundle) { + self.generator.check_io_ty(io_ty); + } +} diff --git a/crates/fayalite/tests/sim.rs b/crates/fayalite/tests/sim.rs index 8c8a10f..0265a7a 100644 --- a/crates/fayalite/tests/sim.rs +++ b/crates/fayalite/tests/sim.rs @@ -5,11 +5,14 @@ use fayalite::{ int::UIntValue, prelude::*, reset::ResetType, - sim::{time::SimDuration, vcd::VcdWriterDecls, Simulation, ToSimValue}, + sim::{ + time::SimDuration, vcd::VcdWriterDecls, ExternModuleSimGenerator, + ExternModuleSimulationState, Simulation, ToSimValue, + }, ty::StaticType, util::RcWriter, }; -use std::num::NonZeroUsize; +use std::{future::IntoFuture, num::NonZeroUsize}; #[hdl_module(outline_generated)] pub fn connect_const() { @@ -1443,3 +1446,61 @@ fn test_conditional_assignment_last() { panic!(); } } + +#[hdl_module(outline_generated, extern)] +pub fn extern_module() { + #[hdl] + let i: Bool = m.input(); + #[hdl] + let o: Bool = m.output(); + #[derive(Clone, Eq, PartialEq, Hash, Debug)] + struct Sim { + i: Expr, + o: Expr, + } + impl ExternModuleSimGenerator for Sim { + type IOType = extern_module; + + fn run<'a>( + &'a self, + mut sim: ExternModuleSimulationState, + ) -> impl IntoFuture + 'a { + let Self { i, o } = *self; + async move { + sim.write(o, true).await; + sim.advance_time(SimDuration::from_nanos(500)).await; + let mut invert = false; + loop { + sim.advance_time(SimDuration::from_micros(1)).await; + let v = sim.read_bool(i).await; + sim.write(o, v ^ invert).await; + invert = !invert; + } + } + } + } + m.extern_module_simulation(Sim { i, o }); +} + +#[test] +fn test_extern_module() { + let _n = SourceLocation::normalize_files_for_tests(); + let mut sim = Simulation::new(extern_module()); + let mut writer = RcWriter::default(); + sim.add_trace_writer(VcdWriterDecls::new(writer.clone())); + sim.write(sim.io().i, false); + sim.advance_time(SimDuration::from_micros(10)); + sim.write(sim.io().i, true); + sim.advance_time(SimDuration::from_micros(10)); + sim.flush_traces().unwrap(); + let vcd = String::from_utf8(writer.take()).unwrap(); + println!("####### VCD:\n{vcd}\n#######"); + if vcd != include_str!("sim/expected/extern_module.vcd") { + panic!(); + } + let sim_debug = format!("{sim:#?}"); + println!("#######\n{sim_debug}\n#######"); + if sim_debug != include_str!("sim/expected/extern_module.txt") { + panic!(); + } +} diff --git a/crates/fayalite/tests/sim/expected/array_rw.txt b/crates/fayalite/tests/sim/expected/array_rw.txt index f016e72..34643f2 100644 --- a/crates/fayalite/tests/sim/expected/array_rw.txt +++ b/crates/fayalite/tests/sim/expected/array_rw.txt @@ -488,1501 +488,338 @@ Simulation { .. }, }, - uninitialized_inputs: {}, - io_targets: { - Instance { - name: ::array_rw, - instantiated: Module { - name: array_rw, - .. - }, - }.array_in: CompiledValue { - layout: CompiledTypeLayout { - ty: Array, 16>, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 16, - debug_data: [ - SlotDebugData { - name: "InstantiatedModule(array_rw: array_rw).array_rw::array_in[0]", - ty: UInt<8>, - }, - SlotDebugData { - name: "InstantiatedModule(array_rw: array_rw).array_rw::array_in[1]", - ty: UInt<8>, - }, - SlotDebugData { - name: "InstantiatedModule(array_rw: array_rw).array_rw::array_in[2]", - ty: UInt<8>, - }, - SlotDebugData { - name: "InstantiatedModule(array_rw: array_rw).array_rw::array_in[3]", - ty: UInt<8>, - }, - SlotDebugData { - name: "InstantiatedModule(array_rw: array_rw).array_rw::array_in[4]", - ty: UInt<8>, - }, - SlotDebugData { - name: "InstantiatedModule(array_rw: array_rw).array_rw::array_in[5]", - ty: UInt<8>, - }, - SlotDebugData { - name: "InstantiatedModule(array_rw: array_rw).array_rw::array_in[6]", - ty: UInt<8>, - }, - SlotDebugData { - name: "InstantiatedModule(array_rw: array_rw).array_rw::array_in[7]", - ty: UInt<8>, - }, - SlotDebugData { - name: "InstantiatedModule(array_rw: array_rw).array_rw::array_in[8]", - ty: UInt<8>, - }, - SlotDebugData { - name: "InstantiatedModule(array_rw: array_rw).array_rw::array_in[9]", - ty: UInt<8>, - }, - SlotDebugData { - name: "InstantiatedModule(array_rw: array_rw).array_rw::array_in[10]", - ty: UInt<8>, - }, - SlotDebugData { - name: "InstantiatedModule(array_rw: array_rw).array_rw::array_in[11]", - ty: UInt<8>, - }, - SlotDebugData { - name: "InstantiatedModule(array_rw: array_rw).array_rw::array_in[12]", - ty: UInt<8>, - }, - SlotDebugData { - name: "InstantiatedModule(array_rw: array_rw).array_rw::array_in[13]", - ty: UInt<8>, - }, - SlotDebugData { - name: "InstantiatedModule(array_rw: array_rw).array_rw::array_in[14]", - ty: UInt<8>, - }, - SlotDebugData { - name: "InstantiatedModule(array_rw: array_rw).array_rw::array_in[15]", - ty: UInt<8>, - }, - ], - .. - }, + main_module: SimulationModuleState { + base_targets: [ + Instance { + name: ::array_rw, + instantiated: Module { + name: array_rw, + .. }, - body: Array { - element: CompiledTypeLayout { - ty: UInt<8>, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: UInt<8>, - }, - ], - .. - }, - }, - body: Scalar, - }, + }.array_in, + Instance { + name: ::array_rw, + instantiated: Module { + name: array_rw, + .. }, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 0, len: 16 }, - }, - write: None, - }, - Instance { - name: ::array_rw, - instantiated: Module { - name: array_rw, - .. - }, - }.array_in[0]: CompiledValue { - layout: CompiledTypeLayout { - ty: UInt<8>, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: UInt<8>, - }, - ], - .. - }, - }, - body: Scalar, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 0, len: 1 }, - }, - write: None, - }, - Instance { - name: ::array_rw, - instantiated: Module { - name: array_rw, - .. - }, - }.array_in[10]: CompiledValue { - layout: CompiledTypeLayout { - ty: UInt<8>, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: UInt<8>, - }, - ], - .. - }, - }, - body: Scalar, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 10, len: 1 }, - }, - write: None, - }, - Instance { - name: ::array_rw, - instantiated: Module { - name: array_rw, - .. - }, - }.array_in[11]: CompiledValue { - layout: CompiledTypeLayout { - ty: UInt<8>, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: UInt<8>, - }, - ], - .. - }, - }, - body: Scalar, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 11, len: 1 }, - }, - write: None, - }, - Instance { - name: ::array_rw, - instantiated: Module { - name: array_rw, - .. - }, - }.array_in[12]: CompiledValue { - layout: CompiledTypeLayout { - ty: UInt<8>, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: UInt<8>, - }, - ], - .. - }, - }, - body: Scalar, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 12, len: 1 }, - }, - write: None, - }, - Instance { - name: ::array_rw, - instantiated: Module { - name: array_rw, - .. - }, - }.array_in[13]: CompiledValue { - layout: CompiledTypeLayout { - ty: UInt<8>, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: UInt<8>, - }, - ], - .. - }, - }, - body: Scalar, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 13, len: 1 }, - }, - write: None, - }, - Instance { - name: ::array_rw, - instantiated: Module { - name: array_rw, - .. - }, - }.array_in[14]: CompiledValue { - layout: CompiledTypeLayout { - ty: UInt<8>, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: UInt<8>, - }, - ], - .. - }, - }, - body: Scalar, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 14, len: 1 }, - }, - write: None, - }, - Instance { - name: ::array_rw, - instantiated: Module { - name: array_rw, - .. - }, - }.array_in[15]: CompiledValue { - layout: CompiledTypeLayout { - ty: UInt<8>, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: UInt<8>, - }, - ], - .. - }, - }, - body: Scalar, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 15, len: 1 }, - }, - write: None, - }, - Instance { - name: ::array_rw, - instantiated: Module { - name: array_rw, - .. - }, - }.array_in[1]: CompiledValue { - layout: CompiledTypeLayout { - ty: UInt<8>, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: UInt<8>, - }, - ], - .. - }, - }, - body: Scalar, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 1, len: 1 }, - }, - write: None, - }, - Instance { - name: ::array_rw, - instantiated: Module { - name: array_rw, - .. - }, - }.array_in[2]: CompiledValue { - layout: CompiledTypeLayout { - ty: UInt<8>, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: UInt<8>, - }, - ], - .. - }, - }, - body: Scalar, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 2, len: 1 }, - }, - write: None, - }, - Instance { - name: ::array_rw, - instantiated: Module { - name: array_rw, - .. - }, - }.array_in[3]: CompiledValue { - layout: CompiledTypeLayout { - ty: UInt<8>, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: UInt<8>, - }, - ], - .. - }, - }, - body: Scalar, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 3, len: 1 }, - }, - write: None, - }, - Instance { - name: ::array_rw, - instantiated: Module { - name: array_rw, - .. - }, - }.array_in[4]: CompiledValue { - layout: CompiledTypeLayout { - ty: UInt<8>, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: UInt<8>, - }, - ], - .. - }, - }, - body: Scalar, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 4, len: 1 }, - }, - write: None, - }, - Instance { - name: ::array_rw, - instantiated: Module { - name: array_rw, - .. - }, - }.array_in[5]: CompiledValue { - layout: CompiledTypeLayout { - ty: UInt<8>, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: UInt<8>, - }, - ], - .. - }, - }, - body: Scalar, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 5, len: 1 }, - }, - write: None, - }, - Instance { - name: ::array_rw, - instantiated: Module { - name: array_rw, - .. - }, - }.array_in[6]: CompiledValue { - layout: CompiledTypeLayout { - ty: UInt<8>, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: UInt<8>, - }, - ], - .. - }, - }, - body: Scalar, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 6, len: 1 }, - }, - write: None, - }, - Instance { - name: ::array_rw, - instantiated: Module { - name: array_rw, - .. - }, - }.array_in[7]: CompiledValue { - layout: CompiledTypeLayout { - ty: UInt<8>, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: UInt<8>, - }, - ], - .. - }, - }, - body: Scalar, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 7, len: 1 }, - }, - write: None, - }, - Instance { - name: ::array_rw, - instantiated: Module { - name: array_rw, - .. - }, - }.array_in[8]: CompiledValue { - layout: CompiledTypeLayout { - ty: UInt<8>, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: UInt<8>, - }, - ], - .. - }, - }, - body: Scalar, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 8, len: 1 }, - }, - write: None, - }, - Instance { - name: ::array_rw, - instantiated: Module { - name: array_rw, - .. - }, - }.array_in[9]: CompiledValue { - layout: CompiledTypeLayout { - ty: UInt<8>, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: UInt<8>, - }, - ], - .. - }, - }, - body: Scalar, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 9, len: 1 }, - }, - write: None, - }, - Instance { - name: ::array_rw, - instantiated: Module { - name: array_rw, - .. - }, - }.array_out: CompiledValue { - layout: CompiledTypeLayout { - ty: Array, 16>, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 16, - debug_data: [ - SlotDebugData { - name: "InstantiatedModule(array_rw: array_rw).array_rw::array_out[0]", - ty: UInt<8>, - }, - SlotDebugData { - name: "InstantiatedModule(array_rw: array_rw).array_rw::array_out[1]", - ty: UInt<8>, - }, - SlotDebugData { - name: "InstantiatedModule(array_rw: array_rw).array_rw::array_out[2]", - ty: UInt<8>, - }, - SlotDebugData { - name: "InstantiatedModule(array_rw: array_rw).array_rw::array_out[3]", - ty: UInt<8>, - }, - SlotDebugData { - name: "InstantiatedModule(array_rw: array_rw).array_rw::array_out[4]", - ty: UInt<8>, - }, - SlotDebugData { - name: "InstantiatedModule(array_rw: array_rw).array_rw::array_out[5]", - ty: UInt<8>, - }, - SlotDebugData { - name: "InstantiatedModule(array_rw: array_rw).array_rw::array_out[6]", - ty: UInt<8>, - }, - SlotDebugData { - name: "InstantiatedModule(array_rw: array_rw).array_rw::array_out[7]", - ty: UInt<8>, - }, - SlotDebugData { - name: "InstantiatedModule(array_rw: array_rw).array_rw::array_out[8]", - ty: UInt<8>, - }, - SlotDebugData { - name: "InstantiatedModule(array_rw: array_rw).array_rw::array_out[9]", - ty: UInt<8>, - }, - SlotDebugData { - name: "InstantiatedModule(array_rw: array_rw).array_rw::array_out[10]", - ty: UInt<8>, - }, - SlotDebugData { - name: "InstantiatedModule(array_rw: array_rw).array_rw::array_out[11]", - ty: UInt<8>, - }, - SlotDebugData { - name: "InstantiatedModule(array_rw: array_rw).array_rw::array_out[12]", - ty: UInt<8>, - }, - SlotDebugData { - name: "InstantiatedModule(array_rw: array_rw).array_rw::array_out[13]", - ty: UInt<8>, - }, - SlotDebugData { - name: "InstantiatedModule(array_rw: array_rw).array_rw::array_out[14]", - ty: UInt<8>, - }, - SlotDebugData { - name: "InstantiatedModule(array_rw: array_rw).array_rw::array_out[15]", - ty: UInt<8>, - }, - ], - .. - }, - }, - body: Array { - element: CompiledTypeLayout { - ty: UInt<8>, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: UInt<8>, - }, - ], - .. - }, - }, - body: Scalar, - }, - }, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 16, len: 16 }, - }, - write: None, - }, - Instance { - name: ::array_rw, - instantiated: Module { - name: array_rw, - .. - }, - }.array_out[0]: CompiledValue { - layout: CompiledTypeLayout { - ty: UInt<8>, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: UInt<8>, - }, - ], - .. - }, - }, - body: Scalar, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 16, len: 1 }, - }, - write: None, - }, - Instance { - name: ::array_rw, - instantiated: Module { - name: array_rw, - .. - }, - }.array_out[10]: CompiledValue { - layout: CompiledTypeLayout { - ty: UInt<8>, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: UInt<8>, - }, - ], - .. - }, - }, - body: Scalar, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 26, len: 1 }, - }, - write: None, - }, - Instance { - name: ::array_rw, - instantiated: Module { - name: array_rw, - .. - }, - }.array_out[11]: CompiledValue { - layout: CompiledTypeLayout { - ty: UInt<8>, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: UInt<8>, - }, - ], - .. - }, - }, - body: Scalar, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 27, len: 1 }, - }, - write: None, - }, - Instance { - name: ::array_rw, - instantiated: Module { - name: array_rw, - .. - }, - }.array_out[12]: CompiledValue { - layout: CompiledTypeLayout { - ty: UInt<8>, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: UInt<8>, - }, - ], - .. - }, - }, - body: Scalar, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 28, len: 1 }, - }, - write: None, - }, - Instance { - name: ::array_rw, - instantiated: Module { - name: array_rw, - .. - }, - }.array_out[13]: CompiledValue { - layout: CompiledTypeLayout { - ty: UInt<8>, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: UInt<8>, - }, - ], - .. - }, - }, - body: Scalar, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 29, len: 1 }, - }, - write: None, - }, - Instance { - name: ::array_rw, - instantiated: Module { - name: array_rw, - .. - }, - }.array_out[14]: CompiledValue { - layout: CompiledTypeLayout { - ty: UInt<8>, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: UInt<8>, - }, - ], - .. - }, - }, - body: Scalar, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 30, len: 1 }, - }, - write: None, - }, - Instance { - name: ::array_rw, - instantiated: Module { - name: array_rw, - .. - }, - }.array_out[15]: CompiledValue { - layout: CompiledTypeLayout { - ty: UInt<8>, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: UInt<8>, - }, - ], - .. - }, - }, - body: Scalar, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 31, len: 1 }, - }, - write: None, - }, - Instance { - name: ::array_rw, - instantiated: Module { - name: array_rw, - .. - }, - }.array_out[1]: CompiledValue { - layout: CompiledTypeLayout { - ty: UInt<8>, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: UInt<8>, - }, - ], - .. - }, - }, - body: Scalar, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 17, len: 1 }, - }, - write: None, - }, - Instance { - name: ::array_rw, - instantiated: Module { - name: array_rw, - .. - }, - }.array_out[2]: CompiledValue { - layout: CompiledTypeLayout { - ty: UInt<8>, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: UInt<8>, - }, - ], - .. - }, - }, - body: Scalar, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 18, len: 1 }, - }, - write: None, - }, - Instance { - name: ::array_rw, - instantiated: Module { - name: array_rw, - .. - }, - }.array_out[3]: CompiledValue { - layout: CompiledTypeLayout { - ty: UInt<8>, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: UInt<8>, - }, - ], - .. - }, - }, - body: Scalar, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 19, len: 1 }, - }, - write: None, - }, - Instance { - name: ::array_rw, - instantiated: Module { - name: array_rw, - .. - }, - }.array_out[4]: CompiledValue { - layout: CompiledTypeLayout { - ty: UInt<8>, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: UInt<8>, - }, - ], - .. - }, - }, - body: Scalar, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 20, len: 1 }, - }, - write: None, - }, - Instance { - name: ::array_rw, - instantiated: Module { - name: array_rw, - .. - }, - }.array_out[5]: CompiledValue { - layout: CompiledTypeLayout { - ty: UInt<8>, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: UInt<8>, - }, - ], - .. - }, - }, - body: Scalar, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 21, len: 1 }, - }, - write: None, - }, - Instance { - name: ::array_rw, - instantiated: Module { - name: array_rw, - .. - }, - }.array_out[6]: CompiledValue { - layout: CompiledTypeLayout { - ty: UInt<8>, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: UInt<8>, - }, - ], - .. - }, - }, - body: Scalar, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 22, len: 1 }, - }, - write: None, - }, - Instance { - name: ::array_rw, - instantiated: Module { - name: array_rw, - .. - }, - }.array_out[7]: CompiledValue { - layout: CompiledTypeLayout { - ty: UInt<8>, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: UInt<8>, - }, - ], - .. - }, - }, - body: Scalar, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 23, len: 1 }, - }, - write: None, - }, - Instance { - name: ::array_rw, - instantiated: Module { - name: array_rw, - .. - }, - }.array_out[8]: CompiledValue { - layout: CompiledTypeLayout { - ty: UInt<8>, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: UInt<8>, - }, - ], - .. - }, - }, - body: Scalar, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 24, len: 1 }, - }, - write: None, - }, - Instance { - name: ::array_rw, - instantiated: Module { - name: array_rw, - .. - }, - }.array_out[9]: CompiledValue { - layout: CompiledTypeLayout { - ty: UInt<8>, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: UInt<8>, - }, - ], - .. - }, - }, - body: Scalar, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 25, len: 1 }, - }, - write: None, - }, - Instance { - name: ::array_rw, - instantiated: Module { - name: array_rw, - .. - }, - }.read_data: CompiledValue { - layout: CompiledTypeLayout { - ty: UInt<8>, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "InstantiatedModule(array_rw: array_rw).array_rw::read_data", - ty: UInt<8>, - }, - ], - .. - }, - }, - body: Scalar, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 33, len: 1 }, - }, - write: None, - }, - Instance { - name: ::array_rw, - instantiated: Module { - name: array_rw, - .. - }, - }.read_index: CompiledValue { - layout: CompiledTypeLayout { - ty: UInt<8>, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "InstantiatedModule(array_rw: array_rw).array_rw::read_index", - ty: UInt<8>, - }, - ], - .. - }, - }, - body: Scalar, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 32, len: 1 }, - }, - write: None, - }, - Instance { - name: ::array_rw, - instantiated: Module { - name: array_rw, - .. - }, - }.write_data: CompiledValue { - layout: CompiledTypeLayout { - ty: UInt<8>, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "InstantiatedModule(array_rw: array_rw).array_rw::write_data", - ty: UInt<8>, - }, - ], - .. - }, - }, - body: Scalar, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 35, len: 1 }, - }, - write: None, - }, - Instance { - name: ::array_rw, - instantiated: Module { - name: array_rw, - .. - }, - }.write_en: CompiledValue { - layout: CompiledTypeLayout { - ty: Bool, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "InstantiatedModule(array_rw: array_rw).array_rw::write_en", - ty: Bool, - }, - ], - .. - }, - }, - body: Scalar, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 36, len: 1 }, - }, - write: None, - }, - Instance { - name: ::array_rw, - instantiated: Module { - name: array_rw, - .. - }, - }.write_index: CompiledValue { - layout: CompiledTypeLayout { - ty: UInt<8>, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "InstantiatedModule(array_rw: array_rw).array_rw::write_index", - ty: UInt<8>, - }, - ], - .. - }, - }, - body: Scalar, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 34, len: 1 }, - }, - write: None, + }.array_out, + Instance { + name: ::array_rw, + instantiated: Module { + name: array_rw, + .. + }, + }.read_index, + Instance { + name: ::array_rw, + instantiated: Module { + name: array_rw, + .. + }, + }.read_data, + Instance { + name: ::array_rw, + instantiated: Module { + name: array_rw, + .. + }, + }.write_index, + Instance { + name: ::array_rw, + instantiated: Module { + name: array_rw, + .. + }, + }.write_data, + Instance { + name: ::array_rw, + instantiated: Module { + name: array_rw, + .. + }, + }.write_en, + ], + uninitialized_ios: {}, + io_targets: { + Instance { + name: ::array_rw, + instantiated: Module { + name: array_rw, + .. + }, + }.array_in, + Instance { + name: ::array_rw, + instantiated: Module { + name: array_rw, + .. + }, + }.array_in[0], + Instance { + name: ::array_rw, + instantiated: Module { + name: array_rw, + .. + }, + }.array_in[10], + Instance { + name: ::array_rw, + instantiated: Module { + name: array_rw, + .. + }, + }.array_in[11], + Instance { + name: ::array_rw, + instantiated: Module { + name: array_rw, + .. + }, + }.array_in[12], + Instance { + name: ::array_rw, + instantiated: Module { + name: array_rw, + .. + }, + }.array_in[13], + Instance { + name: ::array_rw, + instantiated: Module { + name: array_rw, + .. + }, + }.array_in[14], + Instance { + name: ::array_rw, + instantiated: Module { + name: array_rw, + .. + }, + }.array_in[15], + Instance { + name: ::array_rw, + instantiated: Module { + name: array_rw, + .. + }, + }.array_in[1], + Instance { + name: ::array_rw, + instantiated: Module { + name: array_rw, + .. + }, + }.array_in[2], + Instance { + name: ::array_rw, + instantiated: Module { + name: array_rw, + .. + }, + }.array_in[3], + Instance { + name: ::array_rw, + instantiated: Module { + name: array_rw, + .. + }, + }.array_in[4], + Instance { + name: ::array_rw, + instantiated: Module { + name: array_rw, + .. + }, + }.array_in[5], + Instance { + name: ::array_rw, + instantiated: Module { + name: array_rw, + .. + }, + }.array_in[6], + Instance { + name: ::array_rw, + instantiated: Module { + name: array_rw, + .. + }, + }.array_in[7], + Instance { + name: ::array_rw, + instantiated: Module { + name: array_rw, + .. + }, + }.array_in[8], + Instance { + name: ::array_rw, + instantiated: Module { + name: array_rw, + .. + }, + }.array_in[9], + Instance { + name: ::array_rw, + instantiated: Module { + name: array_rw, + .. + }, + }.array_out, + Instance { + name: ::array_rw, + instantiated: Module { + name: array_rw, + .. + }, + }.array_out[0], + Instance { + name: ::array_rw, + instantiated: Module { + name: array_rw, + .. + }, + }.array_out[10], + Instance { + name: ::array_rw, + instantiated: Module { + name: array_rw, + .. + }, + }.array_out[11], + Instance { + name: ::array_rw, + instantiated: Module { + name: array_rw, + .. + }, + }.array_out[12], + Instance { + name: ::array_rw, + instantiated: Module { + name: array_rw, + .. + }, + }.array_out[13], + Instance { + name: ::array_rw, + instantiated: Module { + name: array_rw, + .. + }, + }.array_out[14], + Instance { + name: ::array_rw, + instantiated: Module { + name: array_rw, + .. + }, + }.array_out[15], + Instance { + name: ::array_rw, + instantiated: Module { + name: array_rw, + .. + }, + }.array_out[1], + Instance { + name: ::array_rw, + instantiated: Module { + name: array_rw, + .. + }, + }.array_out[2], + Instance { + name: ::array_rw, + instantiated: Module { + name: array_rw, + .. + }, + }.array_out[3], + Instance { + name: ::array_rw, + instantiated: Module { + name: array_rw, + .. + }, + }.array_out[4], + Instance { + name: ::array_rw, + instantiated: Module { + name: array_rw, + .. + }, + }.array_out[5], + Instance { + name: ::array_rw, + instantiated: Module { + name: array_rw, + .. + }, + }.array_out[6], + Instance { + name: ::array_rw, + instantiated: Module { + name: array_rw, + .. + }, + }.array_out[7], + Instance { + name: ::array_rw, + instantiated: Module { + name: array_rw, + .. + }, + }.array_out[8], + Instance { + name: ::array_rw, + instantiated: Module { + name: array_rw, + .. + }, + }.array_out[9], + Instance { + name: ::array_rw, + instantiated: Module { + name: array_rw, + .. + }, + }.read_data, + Instance { + name: ::array_rw, + instantiated: Module { + name: array_rw, + .. + }, + }.read_index, + Instance { + name: ::array_rw, + instantiated: Module { + name: array_rw, + .. + }, + }.write_data, + Instance { + name: ::array_rw, + instantiated: Module { + name: array_rw, + .. + }, + }.write_en, + Instance { + name: ::array_rw, + instantiated: Module { + name: array_rw, + .. + }, + }.write_index, }, + did_initial_settle: true, }, - made_initial_step: true, - needs_settle: false, + extern_modules: [], + state_ready_to_run: false, trace_decls: TraceModule { name: "array_rw", children: [ diff --git a/crates/fayalite/tests/sim/expected/conditional_assignment_last.txt b/crates/fayalite/tests/sim/expected/conditional_assignment_last.txt index 186e5a5..c4242c4 100644 --- a/crates/fayalite/tests/sim/expected/conditional_assignment_last.txt +++ b/crates/fayalite/tests/sim/expected/conditional_assignment_last.txt @@ -92,45 +92,30 @@ Simulation { .. }, }, - uninitialized_inputs: {}, - io_targets: { - Instance { - name: ::conditional_assignment_last, - instantiated: Module { - name: conditional_assignment_last, - .. - }, - }.i: CompiledValue { - layout: CompiledTypeLayout { - ty: Bool, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "InstantiatedModule(conditional_assignment_last: conditional_assignment_last).conditional_assignment_last::i", - ty: Bool, - }, - ], - .. - }, + main_module: SimulationModuleState { + base_targets: [ + Instance { + name: ::conditional_assignment_last, + instantiated: Module { + name: conditional_assignment_last, + .. }, - body: Scalar, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 0, len: 1 }, - }, - write: None, + }.i, + ], + uninitialized_ios: {}, + io_targets: { + Instance { + name: ::conditional_assignment_last, + instantiated: Module { + name: conditional_assignment_last, + .. + }, + }.i, }, + did_initial_settle: true, }, - made_initial_step: true, - needs_settle: false, + extern_modules: [], + state_ready_to_run: false, trace_decls: TraceModule { name: "conditional_assignment_last", children: [ diff --git a/crates/fayalite/tests/sim/expected/connect_const.txt b/crates/fayalite/tests/sim/expected/connect_const.txt index e44c50d..d357741 100644 --- a/crates/fayalite/tests/sim/expected/connect_const.txt +++ b/crates/fayalite/tests/sim/expected/connect_const.txt @@ -68,45 +68,30 @@ Simulation { .. }, }, - uninitialized_inputs: {}, - io_targets: { - Instance { - name: ::connect_const, - instantiated: Module { - name: connect_const, - .. - }, - }.o: CompiledValue { - layout: CompiledTypeLayout { - ty: UInt<8>, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "InstantiatedModule(connect_const: connect_const).connect_const::o", - ty: UInt<8>, - }, - ], - .. - }, + main_module: SimulationModuleState { + base_targets: [ + Instance { + name: ::connect_const, + instantiated: Module { + name: connect_const, + .. }, - body: Scalar, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 0, len: 1 }, - }, - write: None, + }.o, + ], + uninitialized_ios: {}, + io_targets: { + Instance { + name: ::connect_const, + instantiated: Module { + name: connect_const, + .. + }, + }.o, }, + did_initial_settle: true, }, - made_initial_step: true, - needs_settle: false, + extern_modules: [], + state_ready_to_run: false, trace_decls: TraceModule { name: "connect_const", children: [ diff --git a/crates/fayalite/tests/sim/expected/connect_const_reset.txt b/crates/fayalite/tests/sim/expected/connect_const_reset.txt index d1ab998..b3eb3ea 100644 --- a/crates/fayalite/tests/sim/expected/connect_const_reset.txt +++ b/crates/fayalite/tests/sim/expected/connect_const_reset.txt @@ -97,79 +97,44 @@ Simulation { .. }, }, - uninitialized_inputs: {}, - io_targets: { - Instance { - name: ::connect_const_reset, - instantiated: Module { - name: connect_const_reset, - .. - }, - }.bit_out: CompiledValue { - layout: CompiledTypeLayout { - ty: Bool, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "InstantiatedModule(connect_const_reset: connect_const_reset).connect_const_reset::bit_out", - ty: Bool, - }, - ], - .. - }, + main_module: SimulationModuleState { + base_targets: [ + Instance { + name: ::connect_const_reset, + instantiated: Module { + name: connect_const_reset, + .. }, - body: Scalar, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 1, len: 1 }, - }, - write: None, - }, - Instance { - name: ::connect_const_reset, - instantiated: Module { - name: connect_const_reset, - .. - }, - }.reset_out: CompiledValue { - layout: CompiledTypeLayout { - ty: AsyncReset, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "InstantiatedModule(connect_const_reset: connect_const_reset).connect_const_reset::reset_out", - ty: AsyncReset, - }, - ], - .. - }, + }.reset_out, + Instance { + name: ::connect_const_reset, + instantiated: Module { + name: connect_const_reset, + .. }, - body: Scalar, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 0, len: 1 }, - }, - write: None, + }.bit_out, + ], + uninitialized_ios: {}, + io_targets: { + Instance { + name: ::connect_const_reset, + instantiated: Module { + name: connect_const_reset, + .. + }, + }.bit_out, + Instance { + name: ::connect_const_reset, + instantiated: Module { + name: connect_const_reset, + .. + }, + }.reset_out, }, + did_initial_settle: true, }, - made_initial_step: true, - needs_settle: false, + extern_modules: [], + state_ready_to_run: false, trace_decls: TraceModule { name: "connect_const_reset", children: [ diff --git a/crates/fayalite/tests/sim/expected/counter_async.txt b/crates/fayalite/tests/sim/expected/counter_async.txt index 2e005a0..558d943 100644 --- a/crates/fayalite/tests/sim/expected/counter_async.txt +++ b/crates/fayalite/tests/sim/expected/counter_async.txt @@ -203,213 +203,58 @@ Simulation { .. }, }, - uninitialized_inputs: {}, - io_targets: { - Instance { - name: ::counter, - instantiated: Module { - name: counter, - .. - }, - }.cd: CompiledValue { - layout: CompiledTypeLayout { - ty: Bundle { - /* offset = 0 */ - clk: Clock, - /* offset = 1 */ - rst: AsyncReset, + main_module: SimulationModuleState { + base_targets: [ + Instance { + name: ::counter, + instantiated: Module { + name: counter, + .. }, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 2, - debug_data: [ - SlotDebugData { - name: "InstantiatedModule(counter: counter).counter::cd.clk", - ty: Clock, - }, - SlotDebugData { - name: "InstantiatedModule(counter: counter).counter::cd.rst", - ty: AsyncReset, - }, - ], - .. - }, + }.cd, + Instance { + name: ::counter, + instantiated: Module { + name: counter, + .. }, - body: Bundle { - fields: [ - CompiledBundleField { - offset: TypeIndex { - small_slots: StatePartIndex(0), - big_slots: StatePartIndex(0), - }, - ty: CompiledTypeLayout { - ty: Clock, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: Clock, - }, - ], - .. - }, - }, - body: Scalar, - }, - }, - CompiledBundleField { - offset: TypeIndex { - small_slots: StatePartIndex(0), - big_slots: StatePartIndex(1), - }, - ty: CompiledTypeLayout { - ty: AsyncReset, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: AsyncReset, - }, - ], - .. - }, - }, - body: Scalar, - }, - }, - ], + }.count, + ], + uninitialized_ios: {}, + io_targets: { + Instance { + name: ::counter, + instantiated: Module { + name: counter, + .. }, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 0, len: 2 }, - }, - write: None, - }, - Instance { - name: ::counter, - instantiated: Module { - name: counter, - .. - }, - }.cd.clk: CompiledValue { - layout: CompiledTypeLayout { - ty: Clock, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: Clock, - }, - ], - .. - }, - }, - body: Scalar, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 0, len: 1 }, - }, - write: None, - }, - Instance { - name: ::counter, - instantiated: Module { - name: counter, - .. - }, - }.cd.rst: CompiledValue { - layout: CompiledTypeLayout { - ty: AsyncReset, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: AsyncReset, - }, - ], - .. - }, - }, - body: Scalar, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 1, len: 1 }, - }, - write: None, - }, - Instance { - name: ::counter, - instantiated: Module { - name: counter, - .. - }, - }.count: CompiledValue { - layout: CompiledTypeLayout { - ty: UInt<4>, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "InstantiatedModule(counter: counter).counter::count", - ty: UInt<4>, - }, - ], - .. - }, - }, - body: Scalar, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 2, len: 1 }, - }, - write: None, + }.cd, + Instance { + name: ::counter, + instantiated: Module { + name: counter, + .. + }, + }.cd.clk, + Instance { + name: ::counter, + instantiated: Module { + name: counter, + .. + }, + }.cd.rst, + Instance { + name: ::counter, + instantiated: Module { + name: counter, + .. + }, + }.count, }, + did_initial_settle: true, }, - made_initial_step: true, - needs_settle: false, + extern_modules: [], + state_ready_to_run: false, trace_decls: TraceModule { name: "counter", children: [ diff --git a/crates/fayalite/tests/sim/expected/counter_sync.txt b/crates/fayalite/tests/sim/expected/counter_sync.txt index 78fc200..d31db25 100644 --- a/crates/fayalite/tests/sim/expected/counter_sync.txt +++ b/crates/fayalite/tests/sim/expected/counter_sync.txt @@ -184,213 +184,58 @@ Simulation { .. }, }, - uninitialized_inputs: {}, - io_targets: { - Instance { - name: ::counter, - instantiated: Module { - name: counter, - .. - }, - }.cd: CompiledValue { - layout: CompiledTypeLayout { - ty: Bundle { - /* offset = 0 */ - clk: Clock, - /* offset = 1 */ - rst: SyncReset, + main_module: SimulationModuleState { + base_targets: [ + Instance { + name: ::counter, + instantiated: Module { + name: counter, + .. }, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 2, - debug_data: [ - SlotDebugData { - name: "InstantiatedModule(counter: counter).counter::cd.clk", - ty: Clock, - }, - SlotDebugData { - name: "InstantiatedModule(counter: counter).counter::cd.rst", - ty: SyncReset, - }, - ], - .. - }, + }.cd, + Instance { + name: ::counter, + instantiated: Module { + name: counter, + .. }, - body: Bundle { - fields: [ - CompiledBundleField { - offset: TypeIndex { - small_slots: StatePartIndex(0), - big_slots: StatePartIndex(0), - }, - ty: CompiledTypeLayout { - ty: Clock, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: Clock, - }, - ], - .. - }, - }, - body: Scalar, - }, - }, - CompiledBundleField { - offset: TypeIndex { - small_slots: StatePartIndex(0), - big_slots: StatePartIndex(1), - }, - ty: CompiledTypeLayout { - ty: SyncReset, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: SyncReset, - }, - ], - .. - }, - }, - body: Scalar, - }, - }, - ], + }.count, + ], + uninitialized_ios: {}, + io_targets: { + Instance { + name: ::counter, + instantiated: Module { + name: counter, + .. }, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 0, len: 2 }, - }, - write: None, - }, - Instance { - name: ::counter, - instantiated: Module { - name: counter, - .. - }, - }.cd.clk: CompiledValue { - layout: CompiledTypeLayout { - ty: Clock, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: Clock, - }, - ], - .. - }, - }, - body: Scalar, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 0, len: 1 }, - }, - write: None, - }, - Instance { - name: ::counter, - instantiated: Module { - name: counter, - .. - }, - }.cd.rst: CompiledValue { - layout: CompiledTypeLayout { - ty: SyncReset, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: SyncReset, - }, - ], - .. - }, - }, - body: Scalar, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 1, len: 1 }, - }, - write: None, - }, - Instance { - name: ::counter, - instantiated: Module { - name: counter, - .. - }, - }.count: CompiledValue { - layout: CompiledTypeLayout { - ty: UInt<4>, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "InstantiatedModule(counter: counter).counter::count", - ty: UInt<4>, - }, - ], - .. - }, - }, - body: Scalar, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 2, len: 1 }, - }, - write: None, + }.cd, + Instance { + name: ::counter, + instantiated: Module { + name: counter, + .. + }, + }.cd.clk, + Instance { + name: ::counter, + instantiated: Module { + name: counter, + .. + }, + }.cd.rst, + Instance { + name: ::counter, + instantiated: Module { + name: counter, + .. + }, + }.count, }, + did_initial_settle: true, }, - made_initial_step: true, - needs_settle: false, + extern_modules: [], + state_ready_to_run: false, trace_decls: TraceModule { name: "counter", children: [ diff --git a/crates/fayalite/tests/sim/expected/duplicate_names.txt b/crates/fayalite/tests/sim/expected/duplicate_names.txt index 8a59861..5c6c18a 100644 --- a/crates/fayalite/tests/sim/expected/duplicate_names.txt +++ b/crates/fayalite/tests/sim/expected/duplicate_names.txt @@ -88,10 +88,14 @@ Simulation { .. }, }, - uninitialized_inputs: {}, - io_targets: {}, - made_initial_step: true, - needs_settle: false, + main_module: SimulationModuleState { + base_targets: [], + uninitialized_ios: {}, + io_targets: {}, + did_initial_settle: true, + }, + extern_modules: [], + state_ready_to_run: false, trace_decls: TraceModule { name: "duplicate_names", children: [ diff --git a/crates/fayalite/tests/sim/expected/enums.txt b/crates/fayalite/tests/sim/expected/enums.txt index ebfae3e..089ea31 100644 --- a/crates/fayalite/tests/sim/expected/enums.txt +++ b/crates/fayalite/tests/sim/expected/enums.txt @@ -1215,389 +1215,128 @@ Simulation { .. }, }, - uninitialized_inputs: {}, - io_targets: { - Instance { - name: ::enums, - instantiated: Module { - name: enums, - .. - }, - }.b_out: CompiledValue { - layout: CompiledTypeLayout { - ty: Enum { - HdlNone, - HdlSome(Bundle {0: UInt<1>, 1: Bool}), + main_module: SimulationModuleState { + base_targets: [ + Instance { + name: ::enums, + instantiated: Module { + name: enums, + .. }, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "InstantiatedModule(enums: enums).enums::b_out", - ty: Enum { - HdlNone, - HdlSome(Bundle {0: UInt<1>, 1: Bool}), - }, - }, - ], - .. - }, + }.cd, + Instance { + name: ::enums, + instantiated: Module { + name: enums, + .. }, - body: Scalar, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 7, len: 1 }, - }, - write: None, - }, - Instance { - name: ::enums, - instantiated: Module { - name: enums, - .. - }, - }.cd: CompiledValue { - layout: CompiledTypeLayout { - ty: Bundle { - /* offset = 0 */ - clk: Clock, - /* offset = 1 */ - rst: SyncReset, - }, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 2, - debug_data: [ - SlotDebugData { - name: "InstantiatedModule(enums: enums).enums::cd.clk", - ty: Clock, - }, - SlotDebugData { - name: "InstantiatedModule(enums: enums).enums::cd.rst", - ty: SyncReset, - }, - ], - .. - }, - }, - body: Bundle { - fields: [ - CompiledBundleField { - offset: TypeIndex { - small_slots: StatePartIndex(0), - big_slots: StatePartIndex(0), - }, - ty: CompiledTypeLayout { - ty: Clock, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: Clock, - }, - ], - .. - }, - }, - body: Scalar, - }, - }, - CompiledBundleField { - offset: TypeIndex { - small_slots: StatePartIndex(0), - big_slots: StatePartIndex(1), - }, - ty: CompiledTypeLayout { - ty: SyncReset, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: SyncReset, - }, - ], - .. - }, - }, - body: Scalar, - }, - }, - ], - }, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 0, len: 2 }, - }, - write: None, - }, - Instance { - name: ::enums, - instantiated: Module { - name: enums, - .. - }, - }.cd.clk: CompiledValue { - layout: CompiledTypeLayout { - ty: Clock, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: Clock, - }, - ], - .. - }, - }, - body: Scalar, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 0, len: 1 }, - }, - write: None, - }, - Instance { - name: ::enums, - instantiated: Module { - name: enums, - .. - }, - }.cd.rst: CompiledValue { - layout: CompiledTypeLayout { - ty: SyncReset, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: SyncReset, - }, - ], - .. - }, - }, - body: Scalar, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 1, len: 1 }, - }, - write: None, - }, - Instance { - name: ::enums, - instantiated: Module { - name: enums, - .. - }, - }.data_in: CompiledValue { - layout: CompiledTypeLayout { - ty: UInt<4>, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "InstantiatedModule(enums: enums).enums::data_in", - ty: UInt<4>, - }, - ], - .. - }, - }, - body: Scalar, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 4, len: 1 }, - }, - write: None, - }, - Instance { - name: ::enums, - instantiated: Module { - name: enums, - .. - }, - }.data_out: CompiledValue { - layout: CompiledTypeLayout { - ty: UInt<4>, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "InstantiatedModule(enums: enums).enums::data_out", - ty: UInt<4>, - }, - ], - .. - }, - }, - body: Scalar, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 6, len: 1 }, - }, - write: None, - }, - Instance { - name: ::enums, - instantiated: Module { - name: enums, - .. - }, - }.en: CompiledValue { - layout: CompiledTypeLayout { - ty: Bool, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "InstantiatedModule(enums: enums).enums::en", - ty: Bool, - }, - ], - .. - }, - }, - body: Scalar, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 2, len: 1 }, - }, - write: None, - }, - Instance { - name: ::enums, - instantiated: Module { - name: enums, - .. - }, - }.which_in: CompiledValue { - layout: CompiledTypeLayout { - ty: UInt<2>, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "InstantiatedModule(enums: enums).enums::which_in", - ty: UInt<2>, - }, - ], - .. - }, - }, - body: Scalar, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 3, len: 1 }, - }, - write: None, - }, - Instance { - name: ::enums, - instantiated: Module { - name: enums, - .. - }, - }.which_out: CompiledValue { - layout: CompiledTypeLayout { - ty: UInt<2>, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "InstantiatedModule(enums: enums).enums::which_out", - ty: UInt<2>, - }, - ], - .. - }, - }, - body: Scalar, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 5, len: 1 }, - }, - write: None, + }.en, + Instance { + name: ::enums, + instantiated: Module { + name: enums, + .. + }, + }.which_in, + Instance { + name: ::enums, + instantiated: Module { + name: enums, + .. + }, + }.data_in, + Instance { + name: ::enums, + instantiated: Module { + name: enums, + .. + }, + }.which_out, + Instance { + name: ::enums, + instantiated: Module { + name: enums, + .. + }, + }.data_out, + Instance { + name: ::enums, + instantiated: Module { + name: enums, + .. + }, + }.b_out, + ], + uninitialized_ios: {}, + io_targets: { + Instance { + name: ::enums, + instantiated: Module { + name: enums, + .. + }, + }.b_out, + Instance { + name: ::enums, + instantiated: Module { + name: enums, + .. + }, + }.cd, + Instance { + name: ::enums, + instantiated: Module { + name: enums, + .. + }, + }.cd.clk, + Instance { + name: ::enums, + instantiated: Module { + name: enums, + .. + }, + }.cd.rst, + Instance { + name: ::enums, + instantiated: Module { + name: enums, + .. + }, + }.data_in, + Instance { + name: ::enums, + instantiated: Module { + name: enums, + .. + }, + }.data_out, + Instance { + name: ::enums, + instantiated: Module { + name: enums, + .. + }, + }.en, + Instance { + name: ::enums, + instantiated: Module { + name: enums, + .. + }, + }.which_in, + Instance { + name: ::enums, + instantiated: Module { + name: enums, + .. + }, + }.which_out, }, + did_initial_settle: true, }, - made_initial_step: true, - needs_settle: false, + extern_modules: [], + state_ready_to_run: false, trace_decls: TraceModule { name: "enums", children: [ diff --git a/crates/fayalite/tests/sim/expected/extern_module.txt b/crates/fayalite/tests/sim/expected/extern_module.txt new file mode 100644 index 0000000..cb575a5 --- /dev/null +++ b/crates/fayalite/tests/sim/expected/extern_module.txt @@ -0,0 +1,224 @@ +Simulation { + state: State { + insns: Insns { + state_layout: StateLayout { + ty: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 2, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(extern_module: extern_module).extern_module::i", + ty: Bool, + }, + SlotDebugData { + name: "InstantiatedModule(extern_module: extern_module).extern_module::o", + ty: Bool, + }, + ], + .. + }, + }, + memories: StatePartLayout { + len: 0, + debug_data: [], + layout_data: [], + .. + }, + }, + insns: [ + // at: module-XXXXXXXXXX.rs:1:1 + 0: Return, + ], + .. + }, + pc: 0, + memory_write_log: [], + memories: StatePart { + value: [], + }, + small_slots: StatePart { + value: [], + }, + big_slots: StatePart { + value: [ + 1, + 1, + ], + }, + }, + io: Instance { + name: ::extern_module, + instantiated: Module { + name: extern_module, + .. + }, + }, + main_module: SimulationModuleState { + base_targets: [ + Instance { + name: ::extern_module, + instantiated: Module { + name: extern_module, + .. + }, + }.i, + Instance { + name: ::extern_module, + instantiated: Module { + name: extern_module, + .. + }, + }.o, + ], + uninitialized_ios: {}, + io_targets: { + Instance { + name: ::extern_module, + instantiated: Module { + name: extern_module, + .. + }, + }.i, + Instance { + name: ::extern_module, + instantiated: Module { + name: extern_module, + .. + }, + }.o, + }, + did_initial_settle: true, + }, + extern_modules: [ + SimulationExternModuleState { + module_state: SimulationModuleState { + base_targets: [ + ModuleIO { + name: extern_module::i, + is_input: true, + ty: Bool, + .. + }, + ModuleIO { + name: extern_module::o, + is_input: false, + ty: Bool, + .. + }, + ], + uninitialized_ios: {}, + io_targets: { + ModuleIO { + name: extern_module::i, + is_input: true, + ty: Bool, + .. + }, + ModuleIO { + name: extern_module::o, + is_input: false, + ty: Bool, + .. + }, + }, + did_initial_settle: true, + }, + io_ty: Bundle { + #[hdl(flip)] /* offset = 0 */ + i: Bool, + /* offset = 1 */ + o: Bool, + }, + sim: ExternModuleSimulation { + generator: Sim { + i: ModuleIO { + name: extern_module::i, + is_input: true, + ty: Bool, + .. + }, + o: ModuleIO { + name: extern_module::o, + is_input: false, + ty: Bool, + .. + }, + }, + source_location: SourceLocation( + module-XXXXXXXXXX.rs:4:1, + ), + _phantom: PhantomData, + }, + running_generator: Some( + ..., + ), + wait_target: Some( + Instant( + 20.500000000000 μs, + ), + ), + }, + ], + state_ready_to_run: false, + trace_decls: TraceModule { + name: "extern_module", + children: [ + TraceModuleIO { + name: "i", + child: TraceBool { + location: TraceScalarId(0), + name: "i", + flow: Source, + }, + ty: Bool, + flow: Source, + }, + TraceModuleIO { + name: "o", + child: TraceBool { + location: TraceScalarId(1), + name: "o", + flow: Sink, + }, + ty: Bool, + flow: Sink, + }, + ], + }, + traces: [ + SimTrace { + id: TraceScalarId(0), + kind: BigBool { + index: StatePartIndex(0), + }, + state: 0x1, + last_state: 0x1, + }, + SimTrace { + id: TraceScalarId(1), + kind: BigBool { + index: StatePartIndex(1), + }, + state: 0x1, + last_state: 0x1, + }, + ], + trace_memories: {}, + trace_writers: [ + Running( + VcdWriter { + finished_init: true, + timescale: 1 ps, + .. + }, + ), + ], + instant: 20 μs, + clocks_triggered: [], + .. +} \ No newline at end of file diff --git a/crates/fayalite/tests/sim/expected/extern_module.vcd b/crates/fayalite/tests/sim/expected/extern_module.vcd new file mode 100644 index 0000000..e026a50 --- /dev/null +++ b/crates/fayalite/tests/sim/expected/extern_module.vcd @@ -0,0 +1,51 @@ +$timescale 1 ps $end +$scope module extern_module $end +$var wire 1 ! i $end +$var wire 1 " o $end +$upscope $end +$enddefinitions $end +$dumpvars +0! +1" +$end +#500000 +#1500000 +0" +#2500000 +1" +#3500000 +0" +#4500000 +1" +#5500000 +0" +#6500000 +1" +#7500000 +0" +#8500000 +1" +#9500000 +0" +#10000000 +1! +#10500000 +#11500000 +1" +#12500000 +0" +#13500000 +1" +#14500000 +0" +#15500000 +1" +#16500000 +0" +#17500000 +1" +#18500000 +0" +#19500000 +1" +#20000000 diff --git a/crates/fayalite/tests/sim/expected/memories.txt b/crates/fayalite/tests/sim/expected/memories.txt index afccd8a..cd778d4 100644 --- a/crates/fayalite/tests/sim/expected/memories.txt +++ b/crates/fayalite/tests/sim/expected/memories.txt @@ -570,1309 +570,149 @@ Simulation { .. }, }, - uninitialized_inputs: {}, - io_targets: { - Instance { - name: ::memories, - instantiated: Module { - name: memories, - .. - }, - }.r: CompiledValue { - layout: CompiledTypeLayout { - ty: Bundle { - /* offset = 0 */ - addr: UInt<4>, - /* offset = 4 */ - en: Bool, - /* offset = 5 */ - clk: Clock, - #[hdl(flip)] /* offset = 6 */ - data: Bundle { - /* offset = 0 */ - 0: UInt<8>, - /* offset = 8 */ - 1: SInt<8>, - }, + main_module: SimulationModuleState { + base_targets: [ + Instance { + name: ::memories, + instantiated: Module { + name: memories, + .. }, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 5, - debug_data: [ - SlotDebugData { - name: "InstantiatedModule(memories: memories).memories::r.addr", - ty: UInt<4>, - }, - SlotDebugData { - name: "InstantiatedModule(memories: memories).memories::r.en", - ty: Bool, - }, - SlotDebugData { - name: "InstantiatedModule(memories: memories).memories::r.clk", - ty: Clock, - }, - SlotDebugData { - name: "InstantiatedModule(memories: memories).memories::r.data.0", - ty: UInt<8>, - }, - SlotDebugData { - name: "InstantiatedModule(memories: memories).memories::r.data.1", - ty: SInt<8>, - }, - ], - .. - }, + }.r, + Instance { + name: ::memories, + instantiated: Module { + name: memories, + .. }, - body: Bundle { - fields: [ - CompiledBundleField { - offset: TypeIndex { - small_slots: StatePartIndex(0), - big_slots: StatePartIndex(0), - }, - ty: CompiledTypeLayout { - ty: UInt<4>, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: UInt<4>, - }, - ], - .. - }, - }, - body: Scalar, - }, - }, - CompiledBundleField { - offset: TypeIndex { - small_slots: StatePartIndex(0), - big_slots: StatePartIndex(1), - }, - ty: CompiledTypeLayout { - ty: Bool, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: Bool, - }, - ], - .. - }, - }, - body: Scalar, - }, - }, - CompiledBundleField { - offset: TypeIndex { - small_slots: StatePartIndex(0), - big_slots: StatePartIndex(2), - }, - ty: CompiledTypeLayout { - ty: Clock, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: Clock, - }, - ], - .. - }, - }, - body: Scalar, - }, - }, - CompiledBundleField { - offset: TypeIndex { - small_slots: StatePartIndex(0), - big_slots: StatePartIndex(3), - }, - ty: CompiledTypeLayout { - ty: Bundle { - /* offset = 0 */ - 0: UInt<8>, - /* offset = 8 */ - 1: SInt<8>, - }, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 2, - debug_data: [ - SlotDebugData { - name: ".0", - ty: UInt<8>, - }, - SlotDebugData { - name: ".1", - ty: SInt<8>, - }, - ], - .. - }, - }, - body: Bundle { - fields: [ - CompiledBundleField { - offset: TypeIndex { - small_slots: StatePartIndex(0), - big_slots: StatePartIndex(0), - }, - ty: CompiledTypeLayout { - ty: UInt<8>, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: UInt<8>, - }, - ], - .. - }, - }, - body: Scalar, - }, - }, - CompiledBundleField { - offset: TypeIndex { - small_slots: StatePartIndex(0), - big_slots: StatePartIndex(1), - }, - ty: CompiledTypeLayout { - ty: SInt<8>, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: SInt<8>, - }, - ], - .. - }, - }, - body: Scalar, - }, - }, - ], - }, - }, - }, - ], + }.w, + ], + uninitialized_ios: {}, + io_targets: { + Instance { + name: ::memories, + instantiated: Module { + name: memories, + .. }, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 0, len: 5 }, - }, - write: None, - }, - Instance { - name: ::memories, - instantiated: Module { - name: memories, - .. - }, - }.r.addr: CompiledValue { - layout: CompiledTypeLayout { - ty: UInt<4>, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: UInt<4>, - }, - ], - .. - }, - }, - body: Scalar, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 0, len: 1 }, - }, - write: None, - }, - Instance { - name: ::memories, - instantiated: Module { - name: memories, - .. - }, - }.r.clk: CompiledValue { - layout: CompiledTypeLayout { - ty: Clock, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: Clock, - }, - ], - .. - }, - }, - body: Scalar, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 2, len: 1 }, - }, - write: None, - }, - Instance { - name: ::memories, - instantiated: Module { - name: memories, - .. - }, - }.r.data: CompiledValue { - layout: CompiledTypeLayout { - ty: Bundle { - /* offset = 0 */ - 0: UInt<8>, - /* offset = 8 */ - 1: SInt<8>, - }, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 2, - debug_data: [ - SlotDebugData { - name: ".0", - ty: UInt<8>, - }, - SlotDebugData { - name: ".1", - ty: SInt<8>, - }, - ], - .. - }, - }, - body: Bundle { - fields: [ - CompiledBundleField { - offset: TypeIndex { - small_slots: StatePartIndex(0), - big_slots: StatePartIndex(0), - }, - ty: CompiledTypeLayout { - ty: UInt<8>, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: UInt<8>, - }, - ], - .. - }, - }, - body: Scalar, - }, - }, - CompiledBundleField { - offset: TypeIndex { - small_slots: StatePartIndex(0), - big_slots: StatePartIndex(1), - }, - ty: CompiledTypeLayout { - ty: SInt<8>, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: SInt<8>, - }, - ], - .. - }, - }, - body: Scalar, - }, - }, - ], - }, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 3, len: 2 }, - }, - write: None, - }, - Instance { - name: ::memories, - instantiated: Module { - name: memories, - .. - }, - }.r.data.0: CompiledValue { - layout: CompiledTypeLayout { - ty: UInt<8>, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: UInt<8>, - }, - ], - .. - }, - }, - body: Scalar, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 3, len: 1 }, - }, - write: None, - }, - Instance { - name: ::memories, - instantiated: Module { - name: memories, - .. - }, - }.r.data.1: CompiledValue { - layout: CompiledTypeLayout { - ty: SInt<8>, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: SInt<8>, - }, - ], - .. - }, - }, - body: Scalar, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 4, len: 1 }, - }, - write: None, - }, - Instance { - name: ::memories, - instantiated: Module { - name: memories, - .. - }, - }.r.en: CompiledValue { - layout: CompiledTypeLayout { - ty: Bool, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: Bool, - }, - ], - .. - }, - }, - body: Scalar, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 1, len: 1 }, - }, - write: None, - }, - Instance { - name: ::memories, - instantiated: Module { - name: memories, - .. - }, - }.w: CompiledValue { - layout: CompiledTypeLayout { - ty: Bundle { - /* offset = 0 */ - addr: UInt<4>, - /* offset = 4 */ - en: Bool, - /* offset = 5 */ - clk: Clock, - /* offset = 6 */ - data: Bundle { - /* offset = 0 */ - 0: UInt<8>, - /* offset = 8 */ - 1: SInt<8>, - }, - /* offset = 22 */ - mask: Bundle { - /* offset = 0 */ - 0: Bool, - /* offset = 1 */ - 1: Bool, - }, - }, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 7, - debug_data: [ - SlotDebugData { - name: "InstantiatedModule(memories: memories).memories::w.addr", - ty: UInt<4>, - }, - SlotDebugData { - name: "InstantiatedModule(memories: memories).memories::w.en", - ty: Bool, - }, - SlotDebugData { - name: "InstantiatedModule(memories: memories).memories::w.clk", - ty: Clock, - }, - SlotDebugData { - name: "InstantiatedModule(memories: memories).memories::w.data.0", - ty: UInt<8>, - }, - SlotDebugData { - name: "InstantiatedModule(memories: memories).memories::w.data.1", - ty: SInt<8>, - }, - SlotDebugData { - name: "InstantiatedModule(memories: memories).memories::w.mask.0", - ty: Bool, - }, - SlotDebugData { - name: "InstantiatedModule(memories: memories).memories::w.mask.1", - ty: Bool, - }, - ], - .. - }, - }, - body: Bundle { - fields: [ - CompiledBundleField { - offset: TypeIndex { - small_slots: StatePartIndex(0), - big_slots: StatePartIndex(0), - }, - ty: CompiledTypeLayout { - ty: UInt<4>, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: UInt<4>, - }, - ], - .. - }, - }, - body: Scalar, - }, - }, - CompiledBundleField { - offset: TypeIndex { - small_slots: StatePartIndex(0), - big_slots: StatePartIndex(1), - }, - ty: CompiledTypeLayout { - ty: Bool, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: Bool, - }, - ], - .. - }, - }, - body: Scalar, - }, - }, - CompiledBundleField { - offset: TypeIndex { - small_slots: StatePartIndex(0), - big_slots: StatePartIndex(2), - }, - ty: CompiledTypeLayout { - ty: Clock, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: Clock, - }, - ], - .. - }, - }, - body: Scalar, - }, - }, - CompiledBundleField { - offset: TypeIndex { - small_slots: StatePartIndex(0), - big_slots: StatePartIndex(3), - }, - ty: CompiledTypeLayout { - ty: Bundle { - /* offset = 0 */ - 0: UInt<8>, - /* offset = 8 */ - 1: SInt<8>, - }, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 2, - debug_data: [ - SlotDebugData { - name: ".0", - ty: UInt<8>, - }, - SlotDebugData { - name: ".1", - ty: SInt<8>, - }, - ], - .. - }, - }, - body: Bundle { - fields: [ - CompiledBundleField { - offset: TypeIndex { - small_slots: StatePartIndex(0), - big_slots: StatePartIndex(0), - }, - ty: CompiledTypeLayout { - ty: UInt<8>, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: UInt<8>, - }, - ], - .. - }, - }, - body: Scalar, - }, - }, - CompiledBundleField { - offset: TypeIndex { - small_slots: StatePartIndex(0), - big_slots: StatePartIndex(1), - }, - ty: CompiledTypeLayout { - ty: SInt<8>, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: SInt<8>, - }, - ], - .. - }, - }, - body: Scalar, - }, - }, - ], - }, - }, - }, - CompiledBundleField { - offset: TypeIndex { - small_slots: StatePartIndex(0), - big_slots: StatePartIndex(5), - }, - ty: CompiledTypeLayout { - ty: Bundle { - /* offset = 0 */ - 0: Bool, - /* offset = 1 */ - 1: Bool, - }, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 2, - debug_data: [ - SlotDebugData { - name: ".0", - ty: Bool, - }, - SlotDebugData { - name: ".1", - ty: Bool, - }, - ], - .. - }, - }, - body: Bundle { - fields: [ - CompiledBundleField { - offset: TypeIndex { - small_slots: StatePartIndex(0), - big_slots: StatePartIndex(0), - }, - ty: CompiledTypeLayout { - ty: Bool, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: Bool, - }, - ], - .. - }, - }, - body: Scalar, - }, - }, - CompiledBundleField { - offset: TypeIndex { - small_slots: StatePartIndex(0), - big_slots: StatePartIndex(1), - }, - ty: CompiledTypeLayout { - ty: Bool, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: Bool, - }, - ], - .. - }, - }, - body: Scalar, - }, - }, - ], - }, - }, - }, - ], - }, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 5, len: 7 }, - }, - write: None, - }, - Instance { - name: ::memories, - instantiated: Module { - name: memories, - .. - }, - }.w.addr: CompiledValue { - layout: CompiledTypeLayout { - ty: UInt<4>, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: UInt<4>, - }, - ], - .. - }, - }, - body: Scalar, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 5, len: 1 }, - }, - write: None, - }, - Instance { - name: ::memories, - instantiated: Module { - name: memories, - .. - }, - }.w.clk: CompiledValue { - layout: CompiledTypeLayout { - ty: Clock, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: Clock, - }, - ], - .. - }, - }, - body: Scalar, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 7, len: 1 }, - }, - write: None, - }, - Instance { - name: ::memories, - instantiated: Module { - name: memories, - .. - }, - }.w.data: CompiledValue { - layout: CompiledTypeLayout { - ty: Bundle { - /* offset = 0 */ - 0: UInt<8>, - /* offset = 8 */ - 1: SInt<8>, - }, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 2, - debug_data: [ - SlotDebugData { - name: ".0", - ty: UInt<8>, - }, - SlotDebugData { - name: ".1", - ty: SInt<8>, - }, - ], - .. - }, - }, - body: Bundle { - fields: [ - CompiledBundleField { - offset: TypeIndex { - small_slots: StatePartIndex(0), - big_slots: StatePartIndex(0), - }, - ty: CompiledTypeLayout { - ty: UInt<8>, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: UInt<8>, - }, - ], - .. - }, - }, - body: Scalar, - }, - }, - CompiledBundleField { - offset: TypeIndex { - small_slots: StatePartIndex(0), - big_slots: StatePartIndex(1), - }, - ty: CompiledTypeLayout { - ty: SInt<8>, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: SInt<8>, - }, - ], - .. - }, - }, - body: Scalar, - }, - }, - ], - }, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 8, len: 2 }, - }, - write: None, - }, - Instance { - name: ::memories, - instantiated: Module { - name: memories, - .. - }, - }.w.data.0: CompiledValue { - layout: CompiledTypeLayout { - ty: UInt<8>, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: UInt<8>, - }, - ], - .. - }, - }, - body: Scalar, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 8, len: 1 }, - }, - write: None, - }, - Instance { - name: ::memories, - instantiated: Module { - name: memories, - .. - }, - }.w.data.1: CompiledValue { - layout: CompiledTypeLayout { - ty: SInt<8>, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: SInt<8>, - }, - ], - .. - }, - }, - body: Scalar, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 9, len: 1 }, - }, - write: None, - }, - Instance { - name: ::memories, - instantiated: Module { - name: memories, - .. - }, - }.w.en: CompiledValue { - layout: CompiledTypeLayout { - ty: Bool, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: Bool, - }, - ], - .. - }, - }, - body: Scalar, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 6, len: 1 }, - }, - write: None, - }, - Instance { - name: ::memories, - instantiated: Module { - name: memories, - .. - }, - }.w.mask: CompiledValue { - layout: CompiledTypeLayout { - ty: Bundle { - /* offset = 0 */ - 0: Bool, - /* offset = 1 */ - 1: Bool, - }, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 2, - debug_data: [ - SlotDebugData { - name: ".0", - ty: Bool, - }, - SlotDebugData { - name: ".1", - ty: Bool, - }, - ], - .. - }, - }, - body: Bundle { - fields: [ - CompiledBundleField { - offset: TypeIndex { - small_slots: StatePartIndex(0), - big_slots: StatePartIndex(0), - }, - ty: CompiledTypeLayout { - ty: Bool, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: Bool, - }, - ], - .. - }, - }, - body: Scalar, - }, - }, - CompiledBundleField { - offset: TypeIndex { - small_slots: StatePartIndex(0), - big_slots: StatePartIndex(1), - }, - ty: CompiledTypeLayout { - ty: Bool, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: Bool, - }, - ], - .. - }, - }, - body: Scalar, - }, - }, - ], - }, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 10, len: 2 }, - }, - write: None, - }, - Instance { - name: ::memories, - instantiated: Module { - name: memories, - .. - }, - }.w.mask.0: CompiledValue { - layout: CompiledTypeLayout { - ty: Bool, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: Bool, - }, - ], - .. - }, - }, - body: Scalar, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 10, len: 1 }, - }, - write: None, - }, - Instance { - name: ::memories, - instantiated: Module { - name: memories, - .. - }, - }.w.mask.1: CompiledValue { - layout: CompiledTypeLayout { - ty: Bool, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: Bool, - }, - ], - .. - }, - }, - body: Scalar, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 11, len: 1 }, - }, - write: None, + }.r, + Instance { + name: ::memories, + instantiated: Module { + name: memories, + .. + }, + }.r.addr, + Instance { + name: ::memories, + instantiated: Module { + name: memories, + .. + }, + }.r.clk, + Instance { + name: ::memories, + instantiated: Module { + name: memories, + .. + }, + }.r.data, + Instance { + name: ::memories, + instantiated: Module { + name: memories, + .. + }, + }.r.data.0, + Instance { + name: ::memories, + instantiated: Module { + name: memories, + .. + }, + }.r.data.1, + Instance { + name: ::memories, + instantiated: Module { + name: memories, + .. + }, + }.r.en, + Instance { + name: ::memories, + instantiated: Module { + name: memories, + .. + }, + }.w, + Instance { + name: ::memories, + instantiated: Module { + name: memories, + .. + }, + }.w.addr, + Instance { + name: ::memories, + instantiated: Module { + name: memories, + .. + }, + }.w.clk, + Instance { + name: ::memories, + instantiated: Module { + name: memories, + .. + }, + }.w.data, + Instance { + name: ::memories, + instantiated: Module { + name: memories, + .. + }, + }.w.data.0, + Instance { + name: ::memories, + instantiated: Module { + name: memories, + .. + }, + }.w.data.1, + Instance { + name: ::memories, + instantiated: Module { + name: memories, + .. + }, + }.w.en, + Instance { + name: ::memories, + instantiated: Module { + name: memories, + .. + }, + }.w.mask, + Instance { + name: ::memories, + instantiated: Module { + name: memories, + .. + }, + }.w.mask.0, + Instance { + name: ::memories, + instantiated: Module { + name: memories, + .. + }, + }.w.mask.1, }, + did_initial_settle: true, }, - made_initial_step: true, - needs_settle: false, + extern_modules: [], + state_ready_to_run: false, trace_decls: TraceModule { name: "memories", children: [ diff --git a/crates/fayalite/tests/sim/expected/memories2.txt b/crates/fayalite/tests/sim/expected/memories2.txt index 5d90815..2359749 100644 --- a/crates/fayalite/tests/sim/expected/memories2.txt +++ b/crates/fayalite/tests/sim/expected/memories2.txt @@ -598,514 +598,79 @@ Simulation { .. }, }, - uninitialized_inputs: {}, - io_targets: { - Instance { - name: ::memories2, - instantiated: Module { - name: memories2, - .. - }, - }.rw: CompiledValue { - layout: CompiledTypeLayout { - ty: Bundle { - /* offset = 0 */ - addr: UInt<3>, - /* offset = 3 */ - en: Bool, - /* offset = 4 */ - clk: Clock, - #[hdl(flip)] /* offset = 5 */ - rdata: UInt<2>, - /* offset = 7 */ - wmode: Bool, - /* offset = 8 */ - wdata: UInt<2>, - /* offset = 10 */ - wmask: Bool, + main_module: SimulationModuleState { + base_targets: [ + Instance { + name: ::memories2, + instantiated: Module { + name: memories2, + .. }, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 7, - debug_data: [ - SlotDebugData { - name: "InstantiatedModule(memories2: memories2).memories2::rw.addr", - ty: UInt<3>, - }, - SlotDebugData { - name: "InstantiatedModule(memories2: memories2).memories2::rw.en", - ty: Bool, - }, - SlotDebugData { - name: "InstantiatedModule(memories2: memories2).memories2::rw.clk", - ty: Clock, - }, - SlotDebugData { - name: "InstantiatedModule(memories2: memories2).memories2::rw.rdata", - ty: UInt<2>, - }, - SlotDebugData { - name: "InstantiatedModule(memories2: memories2).memories2::rw.wmode", - ty: Bool, - }, - SlotDebugData { - name: "InstantiatedModule(memories2: memories2).memories2::rw.wdata", - ty: UInt<2>, - }, - SlotDebugData { - name: "InstantiatedModule(memories2: memories2).memories2::rw.wmask", - ty: Bool, - }, - ], - .. - }, + }.rw, + ], + uninitialized_ios: {}, + io_targets: { + Instance { + name: ::memories2, + instantiated: Module { + name: memories2, + .. }, - body: Bundle { - fields: [ - CompiledBundleField { - offset: TypeIndex { - small_slots: StatePartIndex(0), - big_slots: StatePartIndex(0), - }, - ty: CompiledTypeLayout { - ty: UInt<3>, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: UInt<3>, - }, - ], - .. - }, - }, - body: Scalar, - }, - }, - CompiledBundleField { - offset: TypeIndex { - small_slots: StatePartIndex(0), - big_slots: StatePartIndex(1), - }, - ty: CompiledTypeLayout { - ty: Bool, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: Bool, - }, - ], - .. - }, - }, - body: Scalar, - }, - }, - CompiledBundleField { - offset: TypeIndex { - small_slots: StatePartIndex(0), - big_slots: StatePartIndex(2), - }, - ty: CompiledTypeLayout { - ty: Clock, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: Clock, - }, - ], - .. - }, - }, - body: Scalar, - }, - }, - CompiledBundleField { - offset: TypeIndex { - small_slots: StatePartIndex(0), - big_slots: StatePartIndex(3), - }, - ty: CompiledTypeLayout { - ty: UInt<2>, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: UInt<2>, - }, - ], - .. - }, - }, - body: Scalar, - }, - }, - CompiledBundleField { - offset: TypeIndex { - small_slots: StatePartIndex(0), - big_slots: StatePartIndex(4), - }, - ty: CompiledTypeLayout { - ty: Bool, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: Bool, - }, - ], - .. - }, - }, - body: Scalar, - }, - }, - CompiledBundleField { - offset: TypeIndex { - small_slots: StatePartIndex(0), - big_slots: StatePartIndex(5), - }, - ty: CompiledTypeLayout { - ty: UInt<2>, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: UInt<2>, - }, - ], - .. - }, - }, - body: Scalar, - }, - }, - CompiledBundleField { - offset: TypeIndex { - small_slots: StatePartIndex(0), - big_slots: StatePartIndex(6), - }, - ty: CompiledTypeLayout { - ty: Bool, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: Bool, - }, - ], - .. - }, - }, - body: Scalar, - }, - }, - ], + }.rw, + Instance { + name: ::memories2, + instantiated: Module { + name: memories2, + .. }, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 0, len: 7 }, - }, - write: None, - }, - Instance { - name: ::memories2, - instantiated: Module { - name: memories2, - .. - }, - }.rw.addr: CompiledValue { - layout: CompiledTypeLayout { - ty: UInt<3>, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: UInt<3>, - }, - ], - .. - }, - }, - body: Scalar, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 0, len: 1 }, - }, - write: None, - }, - Instance { - name: ::memories2, - instantiated: Module { - name: memories2, - .. - }, - }.rw.clk: CompiledValue { - layout: CompiledTypeLayout { - ty: Clock, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: Clock, - }, - ], - .. - }, - }, - body: Scalar, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 2, len: 1 }, - }, - write: None, - }, - Instance { - name: ::memories2, - instantiated: Module { - name: memories2, - .. - }, - }.rw.en: CompiledValue { - layout: CompiledTypeLayout { - ty: Bool, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: Bool, - }, - ], - .. - }, - }, - body: Scalar, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 1, len: 1 }, - }, - write: None, - }, - Instance { - name: ::memories2, - instantiated: Module { - name: memories2, - .. - }, - }.rw.rdata: CompiledValue { - layout: CompiledTypeLayout { - ty: UInt<2>, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: UInt<2>, - }, - ], - .. - }, - }, - body: Scalar, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 3, len: 1 }, - }, - write: None, - }, - Instance { - name: ::memories2, - instantiated: Module { - name: memories2, - .. - }, - }.rw.wdata: CompiledValue { - layout: CompiledTypeLayout { - ty: UInt<2>, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: UInt<2>, - }, - ], - .. - }, - }, - body: Scalar, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 5, len: 1 }, - }, - write: None, - }, - Instance { - name: ::memories2, - instantiated: Module { - name: memories2, - .. - }, - }.rw.wmask: CompiledValue { - layout: CompiledTypeLayout { - ty: Bool, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: Bool, - }, - ], - .. - }, - }, - body: Scalar, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 6, len: 1 }, - }, - write: None, - }, - Instance { - name: ::memories2, - instantiated: Module { - name: memories2, - .. - }, - }.rw.wmode: CompiledValue { - layout: CompiledTypeLayout { - ty: Bool, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: Bool, - }, - ], - .. - }, - }, - body: Scalar, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 4, len: 1 }, - }, - write: None, + }.rw.addr, + Instance { + name: ::memories2, + instantiated: Module { + name: memories2, + .. + }, + }.rw.clk, + Instance { + name: ::memories2, + instantiated: Module { + name: memories2, + .. + }, + }.rw.en, + Instance { + name: ::memories2, + instantiated: Module { + name: memories2, + .. + }, + }.rw.rdata, + Instance { + name: ::memories2, + instantiated: Module { + name: memories2, + .. + }, + }.rw.wdata, + Instance { + name: ::memories2, + instantiated: Module { + name: memories2, + .. + }, + }.rw.wmask, + Instance { + name: ::memories2, + instantiated: Module { + name: memories2, + .. + }, + }.rw.wmode, }, + did_initial_settle: true, }, - made_initial_step: true, - needs_settle: false, + extern_modules: [], + state_ready_to_run: false, trace_decls: TraceModule { name: "memories2", children: [ diff --git a/crates/fayalite/tests/sim/expected/memories3.txt b/crates/fayalite/tests/sim/expected/memories3.txt index 7860bc5..ad12aa4 100644 --- a/crates/fayalite/tests/sim/expected/memories3.txt +++ b/crates/fayalite/tests/sim/expected/memories3.txt @@ -1486,1882 +1486,275 @@ Simulation { .. }, }, - uninitialized_inputs: {}, - io_targets: { - Instance { - name: ::memories3, - instantiated: Module { - name: memories3, - .. - }, - }.r: CompiledValue { - layout: CompiledTypeLayout { - ty: Bundle { - /* offset = 0 */ - addr: UInt<3>, - /* offset = 3 */ - en: Bool, - /* offset = 4 */ - clk: Clock, - #[hdl(flip)] /* offset = 5 */ - data: Array, 8>, + main_module: SimulationModuleState { + base_targets: [ + Instance { + name: ::memories3, + instantiated: Module { + name: memories3, + .. }, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 11, - debug_data: [ - SlotDebugData { - name: "InstantiatedModule(memories3: memories3).memories3::r.addr", - ty: UInt<3>, - }, - SlotDebugData { - name: "InstantiatedModule(memories3: memories3).memories3::r.en", - ty: Bool, - }, - SlotDebugData { - name: "InstantiatedModule(memories3: memories3).memories3::r.clk", - ty: Clock, - }, - SlotDebugData { - name: "InstantiatedModule(memories3: memories3).memories3::r.data[0]", - ty: UInt<8>, - }, - SlotDebugData { - name: "InstantiatedModule(memories3: memories3).memories3::r.data[1]", - ty: UInt<8>, - }, - SlotDebugData { - name: "InstantiatedModule(memories3: memories3).memories3::r.data[2]", - ty: UInt<8>, - }, - SlotDebugData { - name: "InstantiatedModule(memories3: memories3).memories3::r.data[3]", - ty: UInt<8>, - }, - SlotDebugData { - name: "InstantiatedModule(memories3: memories3).memories3::r.data[4]", - ty: UInt<8>, - }, - SlotDebugData { - name: "InstantiatedModule(memories3: memories3).memories3::r.data[5]", - ty: UInt<8>, - }, - SlotDebugData { - name: "InstantiatedModule(memories3: memories3).memories3::r.data[6]", - ty: UInt<8>, - }, - SlotDebugData { - name: "InstantiatedModule(memories3: memories3).memories3::r.data[7]", - ty: UInt<8>, - }, - ], - .. - }, + }.r, + Instance { + name: ::memories3, + instantiated: Module { + name: memories3, + .. }, - body: Bundle { - fields: [ - CompiledBundleField { - offset: TypeIndex { - small_slots: StatePartIndex(0), - big_slots: StatePartIndex(0), - }, - ty: CompiledTypeLayout { - ty: UInt<3>, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: UInt<3>, - }, - ], - .. - }, - }, - body: Scalar, - }, - }, - CompiledBundleField { - offset: TypeIndex { - small_slots: StatePartIndex(0), - big_slots: StatePartIndex(1), - }, - ty: CompiledTypeLayout { - ty: Bool, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: Bool, - }, - ], - .. - }, - }, - body: Scalar, - }, - }, - CompiledBundleField { - offset: TypeIndex { - small_slots: StatePartIndex(0), - big_slots: StatePartIndex(2), - }, - ty: CompiledTypeLayout { - ty: Clock, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: Clock, - }, - ], - .. - }, - }, - body: Scalar, - }, - }, - CompiledBundleField { - offset: TypeIndex { - small_slots: StatePartIndex(0), - big_slots: StatePartIndex(3), - }, - ty: CompiledTypeLayout { - ty: Array, 8>, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 8, - debug_data: [ - SlotDebugData { - name: "[0]", - ty: UInt<8>, - }, - SlotDebugData { - name: "[1]", - ty: UInt<8>, - }, - SlotDebugData { - name: "[2]", - ty: UInt<8>, - }, - SlotDebugData { - name: "[3]", - ty: UInt<8>, - }, - SlotDebugData { - name: "[4]", - ty: UInt<8>, - }, - SlotDebugData { - name: "[5]", - ty: UInt<8>, - }, - SlotDebugData { - name: "[6]", - ty: UInt<8>, - }, - SlotDebugData { - name: "[7]", - ty: UInt<8>, - }, - ], - .. - }, - }, - body: Array { - element: CompiledTypeLayout { - ty: UInt<8>, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: UInt<8>, - }, - ], - .. - }, - }, - body: Scalar, - }, - }, - }, - }, - ], + }.w, + ], + uninitialized_ios: {}, + io_targets: { + Instance { + name: ::memories3, + instantiated: Module { + name: memories3, + .. }, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 0, len: 11 }, - }, - write: None, - }, - Instance { - name: ::memories3, - instantiated: Module { - name: memories3, - .. - }, - }.r.addr: CompiledValue { - layout: CompiledTypeLayout { - ty: UInt<3>, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: UInt<3>, - }, - ], - .. - }, - }, - body: Scalar, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 0, len: 1 }, - }, - write: None, - }, - Instance { - name: ::memories3, - instantiated: Module { - name: memories3, - .. - }, - }.r.clk: CompiledValue { - layout: CompiledTypeLayout { - ty: Clock, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: Clock, - }, - ], - .. - }, - }, - body: Scalar, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 2, len: 1 }, - }, - write: None, - }, - Instance { - name: ::memories3, - instantiated: Module { - name: memories3, - .. - }, - }.r.data: CompiledValue { - layout: CompiledTypeLayout { - ty: Array, 8>, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 8, - debug_data: [ - SlotDebugData { - name: "[0]", - ty: UInt<8>, - }, - SlotDebugData { - name: "[1]", - ty: UInt<8>, - }, - SlotDebugData { - name: "[2]", - ty: UInt<8>, - }, - SlotDebugData { - name: "[3]", - ty: UInt<8>, - }, - SlotDebugData { - name: "[4]", - ty: UInt<8>, - }, - SlotDebugData { - name: "[5]", - ty: UInt<8>, - }, - SlotDebugData { - name: "[6]", - ty: UInt<8>, - }, - SlotDebugData { - name: "[7]", - ty: UInt<8>, - }, - ], - .. - }, - }, - body: Array { - element: CompiledTypeLayout { - ty: UInt<8>, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: UInt<8>, - }, - ], - .. - }, - }, - body: Scalar, - }, - }, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 3, len: 8 }, - }, - write: None, - }, - Instance { - name: ::memories3, - instantiated: Module { - name: memories3, - .. - }, - }.r.data[0]: CompiledValue { - layout: CompiledTypeLayout { - ty: UInt<8>, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: UInt<8>, - }, - ], - .. - }, - }, - body: Scalar, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 3, len: 1 }, - }, - write: None, - }, - Instance { - name: ::memories3, - instantiated: Module { - name: memories3, - .. - }, - }.r.data[1]: CompiledValue { - layout: CompiledTypeLayout { - ty: UInt<8>, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: UInt<8>, - }, - ], - .. - }, - }, - body: Scalar, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 4, len: 1 }, - }, - write: None, - }, - Instance { - name: ::memories3, - instantiated: Module { - name: memories3, - .. - }, - }.r.data[2]: CompiledValue { - layout: CompiledTypeLayout { - ty: UInt<8>, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: UInt<8>, - }, - ], - .. - }, - }, - body: Scalar, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 5, len: 1 }, - }, - write: None, - }, - Instance { - name: ::memories3, - instantiated: Module { - name: memories3, - .. - }, - }.r.data[3]: CompiledValue { - layout: CompiledTypeLayout { - ty: UInt<8>, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: UInt<8>, - }, - ], - .. - }, - }, - body: Scalar, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 6, len: 1 }, - }, - write: None, - }, - Instance { - name: ::memories3, - instantiated: Module { - name: memories3, - .. - }, - }.r.data[4]: CompiledValue { - layout: CompiledTypeLayout { - ty: UInt<8>, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: UInt<8>, - }, - ], - .. - }, - }, - body: Scalar, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 7, len: 1 }, - }, - write: None, - }, - Instance { - name: ::memories3, - instantiated: Module { - name: memories3, - .. - }, - }.r.data[5]: CompiledValue { - layout: CompiledTypeLayout { - ty: UInt<8>, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: UInt<8>, - }, - ], - .. - }, - }, - body: Scalar, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 8, len: 1 }, - }, - write: None, - }, - Instance { - name: ::memories3, - instantiated: Module { - name: memories3, - .. - }, - }.r.data[6]: CompiledValue { - layout: CompiledTypeLayout { - ty: UInt<8>, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: UInt<8>, - }, - ], - .. - }, - }, - body: Scalar, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 9, len: 1 }, - }, - write: None, - }, - Instance { - name: ::memories3, - instantiated: Module { - name: memories3, - .. - }, - }.r.data[7]: CompiledValue { - layout: CompiledTypeLayout { - ty: UInt<8>, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: UInt<8>, - }, - ], - .. - }, - }, - body: Scalar, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 10, len: 1 }, - }, - write: None, - }, - Instance { - name: ::memories3, - instantiated: Module { - name: memories3, - .. - }, - }.r.en: CompiledValue { - layout: CompiledTypeLayout { - ty: Bool, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: Bool, - }, - ], - .. - }, - }, - body: Scalar, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 1, len: 1 }, - }, - write: None, - }, - Instance { - name: ::memories3, - instantiated: Module { - name: memories3, - .. - }, - }.w: CompiledValue { - layout: CompiledTypeLayout { - ty: Bundle { - /* offset = 0 */ - addr: UInt<3>, - /* offset = 3 */ - en: Bool, - /* offset = 4 */ - clk: Clock, - /* offset = 5 */ - data: Array, 8>, - /* offset = 69 */ - mask: Array, - }, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 19, - debug_data: [ - SlotDebugData { - name: "InstantiatedModule(memories3: memories3).memories3::w.addr", - ty: UInt<3>, - }, - SlotDebugData { - name: "InstantiatedModule(memories3: memories3).memories3::w.en", - ty: Bool, - }, - SlotDebugData { - name: "InstantiatedModule(memories3: memories3).memories3::w.clk", - ty: Clock, - }, - SlotDebugData { - name: "InstantiatedModule(memories3: memories3).memories3::w.data[0]", - ty: UInt<8>, - }, - SlotDebugData { - name: "InstantiatedModule(memories3: memories3).memories3::w.data[1]", - ty: UInt<8>, - }, - SlotDebugData { - name: "InstantiatedModule(memories3: memories3).memories3::w.data[2]", - ty: UInt<8>, - }, - SlotDebugData { - name: "InstantiatedModule(memories3: memories3).memories3::w.data[3]", - ty: UInt<8>, - }, - SlotDebugData { - name: "InstantiatedModule(memories3: memories3).memories3::w.data[4]", - ty: UInt<8>, - }, - SlotDebugData { - name: "InstantiatedModule(memories3: memories3).memories3::w.data[5]", - ty: UInt<8>, - }, - SlotDebugData { - name: "InstantiatedModule(memories3: memories3).memories3::w.data[6]", - ty: UInt<8>, - }, - SlotDebugData { - name: "InstantiatedModule(memories3: memories3).memories3::w.data[7]", - ty: UInt<8>, - }, - SlotDebugData { - name: "InstantiatedModule(memories3: memories3).memories3::w.mask[0]", - ty: Bool, - }, - SlotDebugData { - name: "InstantiatedModule(memories3: memories3).memories3::w.mask[1]", - ty: Bool, - }, - SlotDebugData { - name: "InstantiatedModule(memories3: memories3).memories3::w.mask[2]", - ty: Bool, - }, - SlotDebugData { - name: "InstantiatedModule(memories3: memories3).memories3::w.mask[3]", - ty: Bool, - }, - SlotDebugData { - name: "InstantiatedModule(memories3: memories3).memories3::w.mask[4]", - ty: Bool, - }, - SlotDebugData { - name: "InstantiatedModule(memories3: memories3).memories3::w.mask[5]", - ty: Bool, - }, - SlotDebugData { - name: "InstantiatedModule(memories3: memories3).memories3::w.mask[6]", - ty: Bool, - }, - SlotDebugData { - name: "InstantiatedModule(memories3: memories3).memories3::w.mask[7]", - ty: Bool, - }, - ], - .. - }, - }, - body: Bundle { - fields: [ - CompiledBundleField { - offset: TypeIndex { - small_slots: StatePartIndex(0), - big_slots: StatePartIndex(0), - }, - ty: CompiledTypeLayout { - ty: UInt<3>, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: UInt<3>, - }, - ], - .. - }, - }, - body: Scalar, - }, - }, - CompiledBundleField { - offset: TypeIndex { - small_slots: StatePartIndex(0), - big_slots: StatePartIndex(1), - }, - ty: CompiledTypeLayout { - ty: Bool, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: Bool, - }, - ], - .. - }, - }, - body: Scalar, - }, - }, - CompiledBundleField { - offset: TypeIndex { - small_slots: StatePartIndex(0), - big_slots: StatePartIndex(2), - }, - ty: CompiledTypeLayout { - ty: Clock, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: Clock, - }, - ], - .. - }, - }, - body: Scalar, - }, - }, - CompiledBundleField { - offset: TypeIndex { - small_slots: StatePartIndex(0), - big_slots: StatePartIndex(3), - }, - ty: CompiledTypeLayout { - ty: Array, 8>, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 8, - debug_data: [ - SlotDebugData { - name: "[0]", - ty: UInt<8>, - }, - SlotDebugData { - name: "[1]", - ty: UInt<8>, - }, - SlotDebugData { - name: "[2]", - ty: UInt<8>, - }, - SlotDebugData { - name: "[3]", - ty: UInt<8>, - }, - SlotDebugData { - name: "[4]", - ty: UInt<8>, - }, - SlotDebugData { - name: "[5]", - ty: UInt<8>, - }, - SlotDebugData { - name: "[6]", - ty: UInt<8>, - }, - SlotDebugData { - name: "[7]", - ty: UInt<8>, - }, - ], - .. - }, - }, - body: Array { - element: CompiledTypeLayout { - ty: UInt<8>, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: UInt<8>, - }, - ], - .. - }, - }, - body: Scalar, - }, - }, - }, - }, - CompiledBundleField { - offset: TypeIndex { - small_slots: StatePartIndex(0), - big_slots: StatePartIndex(11), - }, - ty: CompiledTypeLayout { - ty: Array, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 8, - debug_data: [ - SlotDebugData { - name: "[0]", - ty: Bool, - }, - SlotDebugData { - name: "[1]", - ty: Bool, - }, - SlotDebugData { - name: "[2]", - ty: Bool, - }, - SlotDebugData { - name: "[3]", - ty: Bool, - }, - SlotDebugData { - name: "[4]", - ty: Bool, - }, - SlotDebugData { - name: "[5]", - ty: Bool, - }, - SlotDebugData { - name: "[6]", - ty: Bool, - }, - SlotDebugData { - name: "[7]", - ty: Bool, - }, - ], - .. - }, - }, - body: Array { - element: CompiledTypeLayout { - ty: Bool, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: Bool, - }, - ], - .. - }, - }, - body: Scalar, - }, - }, - }, - }, - ], - }, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 11, len: 19 }, - }, - write: None, - }, - Instance { - name: ::memories3, - instantiated: Module { - name: memories3, - .. - }, - }.w.addr: CompiledValue { - layout: CompiledTypeLayout { - ty: UInt<3>, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: UInt<3>, - }, - ], - .. - }, - }, - body: Scalar, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 11, len: 1 }, - }, - write: None, - }, - Instance { - name: ::memories3, - instantiated: Module { - name: memories3, - .. - }, - }.w.clk: CompiledValue { - layout: CompiledTypeLayout { - ty: Clock, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: Clock, - }, - ], - .. - }, - }, - body: Scalar, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 13, len: 1 }, - }, - write: None, - }, - Instance { - name: ::memories3, - instantiated: Module { - name: memories3, - .. - }, - }.w.data: CompiledValue { - layout: CompiledTypeLayout { - ty: Array, 8>, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 8, - debug_data: [ - SlotDebugData { - name: "[0]", - ty: UInt<8>, - }, - SlotDebugData { - name: "[1]", - ty: UInt<8>, - }, - SlotDebugData { - name: "[2]", - ty: UInt<8>, - }, - SlotDebugData { - name: "[3]", - ty: UInt<8>, - }, - SlotDebugData { - name: "[4]", - ty: UInt<8>, - }, - SlotDebugData { - name: "[5]", - ty: UInt<8>, - }, - SlotDebugData { - name: "[6]", - ty: UInt<8>, - }, - SlotDebugData { - name: "[7]", - ty: UInt<8>, - }, - ], - .. - }, - }, - body: Array { - element: CompiledTypeLayout { - ty: UInt<8>, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: UInt<8>, - }, - ], - .. - }, - }, - body: Scalar, - }, - }, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 14, len: 8 }, - }, - write: None, - }, - Instance { - name: ::memories3, - instantiated: Module { - name: memories3, - .. - }, - }.w.data[0]: CompiledValue { - layout: CompiledTypeLayout { - ty: UInt<8>, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: UInt<8>, - }, - ], - .. - }, - }, - body: Scalar, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 14, len: 1 }, - }, - write: None, - }, - Instance { - name: ::memories3, - instantiated: Module { - name: memories3, - .. - }, - }.w.data[1]: CompiledValue { - layout: CompiledTypeLayout { - ty: UInt<8>, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: UInt<8>, - }, - ], - .. - }, - }, - body: Scalar, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 15, len: 1 }, - }, - write: None, - }, - Instance { - name: ::memories3, - instantiated: Module { - name: memories3, - .. - }, - }.w.data[2]: CompiledValue { - layout: CompiledTypeLayout { - ty: UInt<8>, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: UInt<8>, - }, - ], - .. - }, - }, - body: Scalar, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 16, len: 1 }, - }, - write: None, - }, - Instance { - name: ::memories3, - instantiated: Module { - name: memories3, - .. - }, - }.w.data[3]: CompiledValue { - layout: CompiledTypeLayout { - ty: UInt<8>, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: UInt<8>, - }, - ], - .. - }, - }, - body: Scalar, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 17, len: 1 }, - }, - write: None, - }, - Instance { - name: ::memories3, - instantiated: Module { - name: memories3, - .. - }, - }.w.data[4]: CompiledValue { - layout: CompiledTypeLayout { - ty: UInt<8>, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: UInt<8>, - }, - ], - .. - }, - }, - body: Scalar, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 18, len: 1 }, - }, - write: None, - }, - Instance { - name: ::memories3, - instantiated: Module { - name: memories3, - .. - }, - }.w.data[5]: CompiledValue { - layout: CompiledTypeLayout { - ty: UInt<8>, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: UInt<8>, - }, - ], - .. - }, - }, - body: Scalar, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 19, len: 1 }, - }, - write: None, - }, - Instance { - name: ::memories3, - instantiated: Module { - name: memories3, - .. - }, - }.w.data[6]: CompiledValue { - layout: CompiledTypeLayout { - ty: UInt<8>, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: UInt<8>, - }, - ], - .. - }, - }, - body: Scalar, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 20, len: 1 }, - }, - write: None, - }, - Instance { - name: ::memories3, - instantiated: Module { - name: memories3, - .. - }, - }.w.data[7]: CompiledValue { - layout: CompiledTypeLayout { - ty: UInt<8>, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: UInt<8>, - }, - ], - .. - }, - }, - body: Scalar, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 21, len: 1 }, - }, - write: None, - }, - Instance { - name: ::memories3, - instantiated: Module { - name: memories3, - .. - }, - }.w.en: CompiledValue { - layout: CompiledTypeLayout { - ty: Bool, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: Bool, - }, - ], - .. - }, - }, - body: Scalar, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 12, len: 1 }, - }, - write: None, - }, - Instance { - name: ::memories3, - instantiated: Module { - name: memories3, - .. - }, - }.w.mask: CompiledValue { - layout: CompiledTypeLayout { - ty: Array, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 8, - debug_data: [ - SlotDebugData { - name: "[0]", - ty: Bool, - }, - SlotDebugData { - name: "[1]", - ty: Bool, - }, - SlotDebugData { - name: "[2]", - ty: Bool, - }, - SlotDebugData { - name: "[3]", - ty: Bool, - }, - SlotDebugData { - name: "[4]", - ty: Bool, - }, - SlotDebugData { - name: "[5]", - ty: Bool, - }, - SlotDebugData { - name: "[6]", - ty: Bool, - }, - SlotDebugData { - name: "[7]", - ty: Bool, - }, - ], - .. - }, - }, - body: Array { - element: CompiledTypeLayout { - ty: Bool, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: Bool, - }, - ], - .. - }, - }, - body: Scalar, - }, - }, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 22, len: 8 }, - }, - write: None, - }, - Instance { - name: ::memories3, - instantiated: Module { - name: memories3, - .. - }, - }.w.mask[0]: CompiledValue { - layout: CompiledTypeLayout { - ty: Bool, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: Bool, - }, - ], - .. - }, - }, - body: Scalar, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 22, len: 1 }, - }, - write: None, - }, - Instance { - name: ::memories3, - instantiated: Module { - name: memories3, - .. - }, - }.w.mask[1]: CompiledValue { - layout: CompiledTypeLayout { - ty: Bool, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: Bool, - }, - ], - .. - }, - }, - body: Scalar, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 23, len: 1 }, - }, - write: None, - }, - Instance { - name: ::memories3, - instantiated: Module { - name: memories3, - .. - }, - }.w.mask[2]: CompiledValue { - layout: CompiledTypeLayout { - ty: Bool, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: Bool, - }, - ], - .. - }, - }, - body: Scalar, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 24, len: 1 }, - }, - write: None, - }, - Instance { - name: ::memories3, - instantiated: Module { - name: memories3, - .. - }, - }.w.mask[3]: CompiledValue { - layout: CompiledTypeLayout { - ty: Bool, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: Bool, - }, - ], - .. - }, - }, - body: Scalar, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 25, len: 1 }, - }, - write: None, - }, - Instance { - name: ::memories3, - instantiated: Module { - name: memories3, - .. - }, - }.w.mask[4]: CompiledValue { - layout: CompiledTypeLayout { - ty: Bool, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: Bool, - }, - ], - .. - }, - }, - body: Scalar, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 26, len: 1 }, - }, - write: None, - }, - Instance { - name: ::memories3, - instantiated: Module { - name: memories3, - .. - }, - }.w.mask[5]: CompiledValue { - layout: CompiledTypeLayout { - ty: Bool, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: Bool, - }, - ], - .. - }, - }, - body: Scalar, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 27, len: 1 }, - }, - write: None, - }, - Instance { - name: ::memories3, - instantiated: Module { - name: memories3, - .. - }, - }.w.mask[6]: CompiledValue { - layout: CompiledTypeLayout { - ty: Bool, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: Bool, - }, - ], - .. - }, - }, - body: Scalar, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 28, len: 1 }, - }, - write: None, - }, - Instance { - name: ::memories3, - instantiated: Module { - name: memories3, - .. - }, - }.w.mask[7]: CompiledValue { - layout: CompiledTypeLayout { - ty: Bool, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: Bool, - }, - ], - .. - }, - }, - body: Scalar, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 29, len: 1 }, - }, - write: None, + }.r, + Instance { + name: ::memories3, + instantiated: Module { + name: memories3, + .. + }, + }.r.addr, + Instance { + name: ::memories3, + instantiated: Module { + name: memories3, + .. + }, + }.r.clk, + Instance { + name: ::memories3, + instantiated: Module { + name: memories3, + .. + }, + }.r.data, + Instance { + name: ::memories3, + instantiated: Module { + name: memories3, + .. + }, + }.r.data[0], + Instance { + name: ::memories3, + instantiated: Module { + name: memories3, + .. + }, + }.r.data[1], + Instance { + name: ::memories3, + instantiated: Module { + name: memories3, + .. + }, + }.r.data[2], + Instance { + name: ::memories3, + instantiated: Module { + name: memories3, + .. + }, + }.r.data[3], + Instance { + name: ::memories3, + instantiated: Module { + name: memories3, + .. + }, + }.r.data[4], + Instance { + name: ::memories3, + instantiated: Module { + name: memories3, + .. + }, + }.r.data[5], + Instance { + name: ::memories3, + instantiated: Module { + name: memories3, + .. + }, + }.r.data[6], + Instance { + name: ::memories3, + instantiated: Module { + name: memories3, + .. + }, + }.r.data[7], + Instance { + name: ::memories3, + instantiated: Module { + name: memories3, + .. + }, + }.r.en, + Instance { + name: ::memories3, + instantiated: Module { + name: memories3, + .. + }, + }.w, + Instance { + name: ::memories3, + instantiated: Module { + name: memories3, + .. + }, + }.w.addr, + Instance { + name: ::memories3, + instantiated: Module { + name: memories3, + .. + }, + }.w.clk, + Instance { + name: ::memories3, + instantiated: Module { + name: memories3, + .. + }, + }.w.data, + Instance { + name: ::memories3, + instantiated: Module { + name: memories3, + .. + }, + }.w.data[0], + Instance { + name: ::memories3, + instantiated: Module { + name: memories3, + .. + }, + }.w.data[1], + Instance { + name: ::memories3, + instantiated: Module { + name: memories3, + .. + }, + }.w.data[2], + Instance { + name: ::memories3, + instantiated: Module { + name: memories3, + .. + }, + }.w.data[3], + Instance { + name: ::memories3, + instantiated: Module { + name: memories3, + .. + }, + }.w.data[4], + Instance { + name: ::memories3, + instantiated: Module { + name: memories3, + .. + }, + }.w.data[5], + Instance { + name: ::memories3, + instantiated: Module { + name: memories3, + .. + }, + }.w.data[6], + Instance { + name: ::memories3, + instantiated: Module { + name: memories3, + .. + }, + }.w.data[7], + Instance { + name: ::memories3, + instantiated: Module { + name: memories3, + .. + }, + }.w.en, + Instance { + name: ::memories3, + instantiated: Module { + name: memories3, + .. + }, + }.w.mask, + Instance { + name: ::memories3, + instantiated: Module { + name: memories3, + .. + }, + }.w.mask[0], + Instance { + name: ::memories3, + instantiated: Module { + name: memories3, + .. + }, + }.w.mask[1], + Instance { + name: ::memories3, + instantiated: Module { + name: memories3, + .. + }, + }.w.mask[2], + Instance { + name: ::memories3, + instantiated: Module { + name: memories3, + .. + }, + }.w.mask[3], + Instance { + name: ::memories3, + instantiated: Module { + name: memories3, + .. + }, + }.w.mask[4], + Instance { + name: ::memories3, + instantiated: Module { + name: memories3, + .. + }, + }.w.mask[5], + Instance { + name: ::memories3, + instantiated: Module { + name: memories3, + .. + }, + }.w.mask[6], + Instance { + name: ::memories3, + instantiated: Module { + name: memories3, + .. + }, + }.w.mask[7], }, + did_initial_settle: true, }, - made_initial_step: true, - needs_settle: false, + extern_modules: [], + state_ready_to_run: false, trace_decls: TraceModule { name: "memories3", children: [ diff --git a/crates/fayalite/tests/sim/expected/mod1.txt b/crates/fayalite/tests/sim/expected/mod1.txt index 5c2b7eb..3656247 100644 --- a/crates/fayalite/tests/sim/expected/mod1.txt +++ b/crates/fayalite/tests/sim/expected/mod1.txt @@ -216,313 +216,58 @@ Simulation { .. }, }, - uninitialized_inputs: {}, - io_targets: { - Instance { - name: ::mod1, - instantiated: Module { - name: mod1, - .. - }, - }.o: CompiledValue { - layout: CompiledTypeLayout { - ty: Bundle { - #[hdl(flip)] /* offset = 0 */ - i: UInt<4>, - /* offset = 4 */ - o: SInt<2>, - #[hdl(flip)] /* offset = 6 */ - i2: SInt<2>, - /* offset = 8 */ - o2: UInt<4>, + main_module: SimulationModuleState { + base_targets: [ + Instance { + name: ::mod1, + instantiated: Module { + name: mod1, + .. }, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 4, - debug_data: [ - SlotDebugData { - name: "InstantiatedModule(mod1: mod1).mod1::o.i", - ty: UInt<4>, - }, - SlotDebugData { - name: "InstantiatedModule(mod1: mod1).mod1::o.o", - ty: SInt<2>, - }, - SlotDebugData { - name: "InstantiatedModule(mod1: mod1).mod1::o.i2", - ty: SInt<2>, - }, - SlotDebugData { - name: "InstantiatedModule(mod1: mod1).mod1::o.o2", - ty: UInt<4>, - }, - ], - .. - }, + }.o, + ], + uninitialized_ios: {}, + io_targets: { + Instance { + name: ::mod1, + instantiated: Module { + name: mod1, + .. }, - body: Bundle { - fields: [ - CompiledBundleField { - offset: TypeIndex { - small_slots: StatePartIndex(0), - big_slots: StatePartIndex(0), - }, - ty: CompiledTypeLayout { - ty: UInt<4>, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: UInt<4>, - }, - ], - .. - }, - }, - body: Scalar, - }, - }, - CompiledBundleField { - offset: TypeIndex { - small_slots: StatePartIndex(0), - big_slots: StatePartIndex(1), - }, - ty: CompiledTypeLayout { - ty: SInt<2>, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: SInt<2>, - }, - ], - .. - }, - }, - body: Scalar, - }, - }, - CompiledBundleField { - offset: TypeIndex { - small_slots: StatePartIndex(0), - big_slots: StatePartIndex(2), - }, - ty: CompiledTypeLayout { - ty: SInt<2>, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: SInt<2>, - }, - ], - .. - }, - }, - body: Scalar, - }, - }, - CompiledBundleField { - offset: TypeIndex { - small_slots: StatePartIndex(0), - big_slots: StatePartIndex(3), - }, - ty: CompiledTypeLayout { - ty: UInt<4>, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: UInt<4>, - }, - ], - .. - }, - }, - body: Scalar, - }, - }, - ], + }.o, + Instance { + name: ::mod1, + instantiated: Module { + name: mod1, + .. }, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 0, len: 4 }, - }, - write: None, - }, - Instance { - name: ::mod1, - instantiated: Module { - name: mod1, - .. - }, - }.o.i: CompiledValue { - layout: CompiledTypeLayout { - ty: UInt<4>, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: UInt<4>, - }, - ], - .. - }, - }, - body: Scalar, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 0, len: 1 }, - }, - write: None, - }, - Instance { - name: ::mod1, - instantiated: Module { - name: mod1, - .. - }, - }.o.i2: CompiledValue { - layout: CompiledTypeLayout { - ty: SInt<2>, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: SInt<2>, - }, - ], - .. - }, - }, - body: Scalar, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 2, len: 1 }, - }, - write: None, - }, - Instance { - name: ::mod1, - instantiated: Module { - name: mod1, - .. - }, - }.o.o: CompiledValue { - layout: CompiledTypeLayout { - ty: SInt<2>, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: SInt<2>, - }, - ], - .. - }, - }, - body: Scalar, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 1, len: 1 }, - }, - write: None, - }, - Instance { - name: ::mod1, - instantiated: Module { - name: mod1, - .. - }, - }.o.o2: CompiledValue { - layout: CompiledTypeLayout { - ty: UInt<4>, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: UInt<4>, - }, - ], - .. - }, - }, - body: Scalar, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 3, len: 1 }, - }, - write: None, + }.o.i, + Instance { + name: ::mod1, + instantiated: Module { + name: mod1, + .. + }, + }.o.i2, + Instance { + name: ::mod1, + instantiated: Module { + name: mod1, + .. + }, + }.o.o, + Instance { + name: ::mod1, + instantiated: Module { + name: mod1, + .. + }, + }.o.o2, }, + did_initial_settle: true, }, - made_initial_step: true, - needs_settle: false, + extern_modules: [], + state_ready_to_run: false, trace_decls: TraceModule { name: "mod1", children: [ diff --git a/crates/fayalite/tests/sim/expected/shift_register.txt b/crates/fayalite/tests/sim/expected/shift_register.txt index 73f6263..901cf70 100644 --- a/crates/fayalite/tests/sim/expected/shift_register.txt +++ b/crates/fayalite/tests/sim/expected/shift_register.txt @@ -265,247 +265,72 @@ Simulation { .. }, }, - uninitialized_inputs: {}, - io_targets: { - Instance { - name: ::shift_register, - instantiated: Module { - name: shift_register, - .. - }, - }.cd: CompiledValue { - layout: CompiledTypeLayout { - ty: Bundle { - /* offset = 0 */ - clk: Clock, - /* offset = 1 */ - rst: SyncReset, + main_module: SimulationModuleState { + base_targets: [ + Instance { + name: ::shift_register, + instantiated: Module { + name: shift_register, + .. }, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 2, - debug_data: [ - SlotDebugData { - name: "InstantiatedModule(shift_register: shift_register).shift_register::cd.clk", - ty: Clock, - }, - SlotDebugData { - name: "InstantiatedModule(shift_register: shift_register).shift_register::cd.rst", - ty: SyncReset, - }, - ], - .. - }, + }.cd, + Instance { + name: ::shift_register, + instantiated: Module { + name: shift_register, + .. }, - body: Bundle { - fields: [ - CompiledBundleField { - offset: TypeIndex { - small_slots: StatePartIndex(0), - big_slots: StatePartIndex(0), - }, - ty: CompiledTypeLayout { - ty: Clock, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: Clock, - }, - ], - .. - }, - }, - body: Scalar, - }, - }, - CompiledBundleField { - offset: TypeIndex { - small_slots: StatePartIndex(0), - big_slots: StatePartIndex(1), - }, - ty: CompiledTypeLayout { - ty: SyncReset, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: SyncReset, - }, - ], - .. - }, - }, - body: Scalar, - }, - }, - ], + }.d, + Instance { + name: ::shift_register, + instantiated: Module { + name: shift_register, + .. }, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 0, len: 2 }, - }, - write: None, - }, - Instance { - name: ::shift_register, - instantiated: Module { - name: shift_register, - .. - }, - }.cd.clk: CompiledValue { - layout: CompiledTypeLayout { - ty: Clock, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: Clock, - }, - ], - .. - }, - }, - body: Scalar, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 0, len: 1 }, - }, - write: None, - }, - Instance { - name: ::shift_register, - instantiated: Module { - name: shift_register, - .. - }, - }.cd.rst: CompiledValue { - layout: CompiledTypeLayout { - ty: SyncReset, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: SyncReset, - }, - ], - .. - }, - }, - body: Scalar, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 1, len: 1 }, - }, - write: None, - }, - Instance { - name: ::shift_register, - instantiated: Module { - name: shift_register, - .. - }, - }.d: CompiledValue { - layout: CompiledTypeLayout { - ty: Bool, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "InstantiatedModule(shift_register: shift_register).shift_register::d", - ty: Bool, - }, - ], - .. - }, - }, - body: Scalar, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 2, len: 1 }, - }, - write: None, - }, - Instance { - name: ::shift_register, - instantiated: Module { - name: shift_register, - .. - }, - }.q: CompiledValue { - layout: CompiledTypeLayout { - ty: Bool, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "InstantiatedModule(shift_register: shift_register).shift_register::q", - ty: Bool, - }, - ], - .. - }, - }, - body: Scalar, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 3, len: 1 }, - }, - write: None, + }.q, + ], + uninitialized_ios: {}, + io_targets: { + Instance { + name: ::shift_register, + instantiated: Module { + name: shift_register, + .. + }, + }.cd, + Instance { + name: ::shift_register, + instantiated: Module { + name: shift_register, + .. + }, + }.cd.clk, + Instance { + name: ::shift_register, + instantiated: Module { + name: shift_register, + .. + }, + }.cd.rst, + Instance { + name: ::shift_register, + instantiated: Module { + name: shift_register, + .. + }, + }.d, + Instance { + name: ::shift_register, + instantiated: Module { + name: shift_register, + .. + }, + }.q, }, + did_initial_settle: true, }, - made_initial_step: true, - needs_settle: false, + extern_modules: [], + state_ready_to_run: false, trace_decls: TraceModule { name: "shift_register", children: [ diff --git a/crates/fayalite/visit_types.json b/crates/fayalite/visit_types.json index b284372..451dc90 100644 --- a/crates/fayalite/visit_types.json +++ b/crates/fayalite/visit_types.json @@ -160,7 +160,8 @@ "data": { "$kind": "Struct", "verilog_name": "Visible", - "parameters": "Visible" + "parameters": "Visible", + "simulation": "Visible" } }, "ExternModuleParameter": { @@ -1269,6 +1270,12 @@ "$kind": "Opaque" }, "generics": "" + }, + "ExternModuleSimulation": { + "data": { + "$kind": "Opaque" + }, + "generics": "" } } } \ No newline at end of file From ab9ff4f2db235fb605d0ec9ea7568d78f54f2575 Mon Sep 17 00:00:00 2001 From: Jacob Lifshay Date: Fri, 21 Mar 2025 17:08:29 -0700 Subject: [PATCH 13/38] simplify setting an extern module simulation --- crates/fayalite/src/module.rs | 22 ++- crates/fayalite/src/sim.rs | 170 +++++++----------- crates/fayalite/tests/sim.rs | 43 ++--- .../tests/sim/expected/extern_module.txt | 36 ++-- crates/fayalite/visit_types.json | 3 +- 5 files changed, 111 insertions(+), 163 deletions(-) diff --git a/crates/fayalite/src/module.rs b/crates/fayalite/src/module.rs index 87f86cc..1fcb529 100644 --- a/crates/fayalite/src/module.rs +++ b/crates/fayalite/src/module.rs @@ -34,6 +34,7 @@ use std::{ collections::VecDeque, convert::Infallible, fmt, + future::IntoFuture, hash::{Hash, Hasher}, iter::FusedIterator, marker::PhantomData, @@ -1082,7 +1083,7 @@ pub struct ExternModuleBody< > { pub verilog_name: Interned, pub parameters: P, - pub simulation: Option>, + pub simulation: Option, } impl From>> for ExternModuleBody { @@ -1767,12 +1768,8 @@ impl AssertValidityState { ModuleBody::Extern(ExternModuleBody { verilog_name: _, parameters: _, - simulation, - }) => { - if let Some(simulation) = simulation { - simulation.check_io_ty(self.module.io_ty); - } - } + simulation: _, + }) => {} ModuleBody::Normal(NormalModuleBody { body }) => { let body = self.make_block_index(body); assert_eq!(body, 0); @@ -2250,6 +2247,17 @@ impl ModuleBuilder { } *simulation = Some(ExternModuleSimulation::new(generator)); } + #[track_caller] + pub fn extern_module_simulation_fn< + Args: fmt::Debug + Clone + Hash + Eq + Send + Sync + 'static, + Fut: IntoFuture + 'static, + >( + &self, + args: Args, + f: fn(Args, crate::sim::ExternModuleSimulationState) -> Fut, + ) { + self.extern_module_simulation(crate::sim::SimGeneratorFn { args, f }); + } } #[track_caller] diff --git a/crates/fayalite/src/sim.rs b/crates/fayalite/src/sim.rs index a5d7d13..b12e9a8 100644 --- a/crates/fayalite/src/sim.rs +++ b/crates/fayalite/src/sim.rs @@ -60,6 +60,7 @@ use std::{ collections::BTreeSet, fmt, future::{Future, IntoFuture}, + hash::Hash, marker::PhantomData, mem, ops::IndexMut, @@ -1634,10 +1635,9 @@ impl fmt::Debug for DebugOpaque { #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] struct CompiledExternModule { - io_ty: Bundle, module_io_targets: Interned<[Target]>, module_io: Interned<[CompiledValue]>, - simulation: ExternModuleSimulation, + simulation: ExternModuleSimulation, } #[derive(Debug)] @@ -4713,7 +4713,6 @@ impl Compiler { ); }; self.extern_modules.push(CompiledExternModule { - io_ty: module.leaf_module().io_ty(), module_io_targets: module .leaf_module() .module_io() @@ -6822,8 +6821,7 @@ impl Ord for WaitTarget { struct SimulationExternModuleState { module_state: SimulationModuleState, - io_ty: Bundle, - sim: ExternModuleSimulation, + sim: ExternModuleSimulation, running_generator: Option + 'static>>>, wait_target: Option, } @@ -6832,14 +6830,12 @@ impl fmt::Debug for SimulationExternModuleState { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let Self { module_state, - io_ty, sim, running_generator, wait_target, } = self; f.debug_struct("SimulationExternModuleState") .field("module_state", module_state) - .field("io_ty", io_ty) .field("sim", sim) .field( "running_generator", @@ -7008,7 +7004,6 @@ impl SimulationImpl { let io_target = Target::from(compiled.io); let extern_modules = Box::from_iter(compiled.extern_modules.iter().map( |&CompiledExternModule { - io_ty, module_io_targets, module_io, simulation, @@ -7020,7 +7015,6 @@ impl SimulationImpl { .copied() .zip(module_io.iter().copied()), ), - io_ty, sim: simulation, running_generator: None, wait_target: Some(WaitTarget::Settle), @@ -7337,12 +7331,10 @@ impl SimulationImpl { extern_module.wait_target = None; let mut generator = if !extern_module.module_state.did_initial_settle { let sim = extern_module.sim; - let io_ty = extern_module.io_ty; drop(this); Box::into_pin(sim.run(ExternModuleSimulationState { sim_impl: this_ref.clone(), module_index, - io_ty, })) } else if let Some(generator) = extern_module.running_generator.take() { drop(this); @@ -8013,52 +8005,25 @@ impl Simulation { } } -pub struct ExternModuleSimulationState { +pub struct ExternModuleSimulationState { sim_impl: Rc>, module_index: usize, - io_ty: T, } -impl fmt::Debug for ExternModuleSimulationState { +impl fmt::Debug for ExternModuleSimulationState { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let Self { sim_impl: _, module_index, - io_ty, } = self; f.debug_struct("ExternModuleSimulationState") .field("sim_impl", &DebugAsDisplay("...")) .field("module_index", module_index) - .field("io_ty", io_ty) .finish() } } -impl ExternModuleSimulationState { - pub fn canonical(self) -> ExternModuleSimulationState { - let Self { - sim_impl, - module_index, - io_ty, - } = self; - ExternModuleSimulationState { - sim_impl, - module_index, - io_ty: Bundle::from_canonical(io_ty.canonical()), - } - } - pub fn from_canonical(sim: ExternModuleSimulationState) -> Self { - let ExternModuleSimulationState { - sim_impl, - module_index, - io_ty, - } = sim; - Self { - sim_impl, - module_index, - io_ty: T::from_canonical(io_ty.canonical()), - } - } +impl ExternModuleSimulationState { pub async fn settle(&mut self) { SimulationImpl::yield_advance_time_or_settle(self.sim_impl.clone(), self.module_index, None) .await @@ -8084,39 +8049,74 @@ impl ExternModuleSimulationState { ); } -pub trait ExternModuleSimGenerator: - Clone + Eq + std::hash::Hash + Any + Send + Sync + fmt::Debug -{ - type IOType: BundleType; +pub trait ExternModuleSimGenerator: Clone + Eq + Hash + Any + Send + Sync + fmt::Debug { + fn run<'a>(&'a self, sim: ExternModuleSimulationState) -> impl IntoFuture + 'a; +} - fn run<'a>( - &'a self, - sim: ExternModuleSimulationState, - ) -> impl IntoFuture + 'a; +pub struct SimGeneratorFn { + pub args: Args, + pub f: fn(Args, ExternModuleSimulationState) -> Fut, +} + +impl fmt::Debug for SimGeneratorFn { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let Self { args, f: _ } = self; + f.debug_struct("SimGeneratorFn") + .field("args", args) + .field("f", &DebugAsDisplay("...")) + .finish() + } +} + +impl Hash for SimGeneratorFn { + fn hash(&self, state: &mut H) { + let Self { args, f } = self; + args.hash(state); + f.hash(state); + } +} + +impl Eq for SimGeneratorFn {} + +impl PartialEq for SimGeneratorFn { + fn eq(&self, other: &Self) -> bool { + let Self { args, f } = self; + *args == other.args && *f == other.f + } +} + +impl Clone for SimGeneratorFn { + fn clone(&self) -> Self { + Self { + args: self.args.clone(), + f: self.f, + } + } +} + +impl Copy for SimGeneratorFn {} + +impl< + T: fmt::Debug + Clone + Eq + Hash + Send + Sync + 'static, + Fut: IntoFuture + 'static, + > ExternModuleSimGenerator for SimGeneratorFn +{ + fn run<'a>(&'a self, sim: ExternModuleSimulationState) -> impl IntoFuture + 'a { + (self.f)(self.args.clone(), sim) + } } trait DynExternModuleSimGenerator: Any + Send + Sync + SupportsPtrEqWithTypeId + fmt::Debug { - fn dyn_run<'a>( - &'a self, - sim: ExternModuleSimulationState, - ) -> Box + 'a>; - #[track_caller] - fn check_io_ty(&self, io_ty: Bundle); + fn dyn_run<'a>(&'a self, sim: ExternModuleSimulationState) + -> Box + 'a>; } impl DynExternModuleSimGenerator for T { fn dyn_run<'a>( &'a self, - sim: ExternModuleSimulationState, + sim: ExternModuleSimulationState, ) -> Box + 'a> { - Box::new( - self.run(ExternModuleSimulationState::from_canonical(sim)) - .into_future(), - ) - } - #[track_caller] - fn check_io_ty(&self, io_ty: Bundle) { - T::IOType::from_canonical(io_ty.canonical()); + Box::new(self.run(sim).into_future()) } } @@ -8129,13 +8129,12 @@ impl InternedCompare for dyn DynExternModuleSimGenerator { } #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)] -pub struct ExternModuleSimulation { +pub struct ExternModuleSimulation { generator: Interned, source_location: SourceLocation, - _phantom: PhantomData, } -impl ExternModuleSimulation { +impl ExternModuleSimulation { pub fn new_with_loc( source_location: SourceLocation, generator: G, @@ -8146,48 +8145,13 @@ impl ExternModuleSimulation { |v| -> &dyn DynExternModuleSimGenerator { v }, ), source_location, - _phantom: PhantomData, } } #[track_caller] pub fn new(generator: G) -> Self { Self::new_with_loc(SourceLocation::caller(), generator) } - pub fn canonical(self) -> ExternModuleSimulation { - let Self { - generator, - source_location, - _phantom: _, - } = self; - ExternModuleSimulation { - generator, - source_location, - _phantom: PhantomData, - } - } - pub fn from_canonical(v: ExternModuleSimulation) -> Self { - let ExternModuleSimulation { - generator, - source_location, - _phantom: _, - } = v; - Self { - generator, - source_location, - _phantom: PhantomData, - } - } -} - -impl ExternModuleSimulation { - fn run( - &self, - sim: ExternModuleSimulationState, - ) -> Box + 'static> { + fn run(&self, sim: ExternModuleSimulationState) -> Box + 'static> { Interned::into_inner(self.generator).dyn_run(sim) } - #[track_caller] - pub fn check_io_ty(self, io_ty: Bundle) { - self.generator.check_io_ty(io_ty); - } } diff --git a/crates/fayalite/tests/sim.rs b/crates/fayalite/tests/sim.rs index 0265a7a..e516f22 100644 --- a/crates/fayalite/tests/sim.rs +++ b/crates/fayalite/tests/sim.rs @@ -5,14 +5,11 @@ use fayalite::{ int::UIntValue, prelude::*, reset::ResetType, - sim::{ - time::SimDuration, vcd::VcdWriterDecls, ExternModuleSimGenerator, - ExternModuleSimulationState, Simulation, ToSimValue, - }, + sim::{time::SimDuration, vcd::VcdWriterDecls, Simulation, ToSimValue}, ty::StaticType, util::RcWriter, }; -use std::{future::IntoFuture, num::NonZeroUsize}; +use std::num::NonZeroUsize; #[hdl_module(outline_generated)] pub fn connect_const() { @@ -1453,33 +1450,17 @@ pub fn extern_module() { let i: Bool = m.input(); #[hdl] let o: Bool = m.output(); - #[derive(Clone, Eq, PartialEq, Hash, Debug)] - struct Sim { - i: Expr, - o: Expr, - } - impl ExternModuleSimGenerator for Sim { - type IOType = extern_module; - - fn run<'a>( - &'a self, - mut sim: ExternModuleSimulationState, - ) -> impl IntoFuture + 'a { - let Self { i, o } = *self; - async move { - sim.write(o, true).await; - sim.advance_time(SimDuration::from_nanos(500)).await; - let mut invert = false; - loop { - sim.advance_time(SimDuration::from_micros(1)).await; - let v = sim.read_bool(i).await; - sim.write(o, v ^ invert).await; - invert = !invert; - } - } + m.extern_module_simulation_fn((i, o), |(i, o), mut sim| async move { + sim.write(o, true).await; + sim.advance_time(SimDuration::from_nanos(500)).await; + let mut invert = false; + loop { + sim.advance_time(SimDuration::from_micros(1)).await; + let v = sim.read_bool(i).await; + sim.write(o, v ^ invert).await; + invert = !invert; } - } - m.extern_module_simulation(Sim { i, o }); + }); } #[test] diff --git a/crates/fayalite/tests/sim/expected/extern_module.txt b/crates/fayalite/tests/sim/expected/extern_module.txt index cb575a5..23af0b2 100644 --- a/crates/fayalite/tests/sim/expected/extern_module.txt +++ b/crates/fayalite/tests/sim/expected/extern_module.txt @@ -128,31 +128,27 @@ Simulation { }, did_initial_settle: true, }, - io_ty: Bundle { - #[hdl(flip)] /* offset = 0 */ - i: Bool, - /* offset = 1 */ - o: Bool, - }, sim: ExternModuleSimulation { - generator: Sim { - i: ModuleIO { - name: extern_module::i, - is_input: true, - ty: Bool, - .. - }, - o: ModuleIO { - name: extern_module::o, - is_input: false, - ty: Bool, - .. - }, + generator: SimGeneratorFn { + args: ( + ModuleIO { + name: extern_module::i, + is_input: true, + ty: Bool, + .. + }, + ModuleIO { + name: extern_module::o, + is_input: false, + ty: Bool, + .. + }, + ), + f: ..., }, source_location: SourceLocation( module-XXXXXXXXXX.rs:4:1, ), - _phantom: PhantomData, }, running_generator: Some( ..., diff --git a/crates/fayalite/visit_types.json b/crates/fayalite/visit_types.json index 451dc90..ff2050a 100644 --- a/crates/fayalite/visit_types.json +++ b/crates/fayalite/visit_types.json @@ -1274,8 +1274,7 @@ "ExternModuleSimulation": { "data": { "$kind": "Opaque" - }, - "generics": "" + } } } } \ No newline at end of file From a115585d5a7c20742bfcdd64d09416bb7fdf3a54 Mon Sep 17 00:00:00 2001 From: Jacob Lifshay Date: Tue, 25 Mar 2025 18:26:48 -0700 Subject: [PATCH 14/38] simulator: allow external module generators to wait for value changes and/or clock edges --- crates/fayalite/src/int.rs | 6 + crates/fayalite/src/sim.rs | 636 +++++++++++++----- crates/fayalite/tests/sim.rs | 47 ++ .../tests/sim/expected/extern_module.txt | 4 +- .../tests/sim/expected/extern_module2.txt | 308 +++++++++ .../tests/sim/expected/extern_module2.vcd | 150 +++++ 6 files changed, 969 insertions(+), 182 deletions(-) create mode 100644 crates/fayalite/tests/sim/expected/extern_module2.txt create mode 100644 crates/fayalite/tests/sim/expected/extern_module2.vcd diff --git a/crates/fayalite/src/int.rs b/crates/fayalite/src/int.rs index 5d10b29..236f240 100644 --- a/crates/fayalite/src/int.rs +++ b/crates/fayalite/src/int.rs @@ -621,6 +621,12 @@ pub trait BoolOrIntType: Type + sealed::BoolOrIntTypeSealed { let bitslice = &BitSlice::::from_slice(&bytes)[..width]; bits.clone_from_bitslice(bitslice); } + fn bits_equal_bigint_wrapping(v: &BigInt, bits: &BitSlice) -> bool { + bits.iter() + .by_vals() + .enumerate() + .all(|(bit_index, bit): (usize, bool)| v.bit(bit_index as u64) == bit) + } fn bits_to_bigint(bits: &BitSlice) -> BigInt { let sign_byte = if Self::Signed::VALUE && bits.last().as_deref().copied().unwrap_or(false) { 0xFF diff --git a/crates/fayalite/src/sim.rs b/crates/fayalite/src/sim.rs index b12e9a8..275b106 100644 --- a/crates/fayalite/src/sim.rs +++ b/crates/fayalite/src/sim.rs @@ -5904,12 +5904,21 @@ impl SimTraceKind { } } -#[derive(Clone, PartialEq, Eq, Hash, Debug)] +#[derive(Clone, PartialEq, Eq, Hash)] pub struct SimValue { ty: T, bits: BitVec, } +impl fmt::Debug for SimValue { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("SimValue") + .field("ty", &self.ty) + .field("bits", &BitSliceWriteWithBase(&self.bits)) + .finish() + } +} + impl SimValue { #[track_caller] fn to_expr_impl(ty: CanonicalType, bits: &BitSlice) -> Expr { @@ -6795,35 +6804,149 @@ impl SimulationModuleState { } } -#[derive(Copy, Clone, Debug, Eq, PartialEq)] -enum WaitTarget { - /// Settle is less than Instant +#[derive(Copy, Clone, Debug)] +enum WaitTarget { Settle, Instant(SimInstant), + Change { key: ChangeKey, value: ChangeValue }, } -impl PartialOrd for WaitTarget { - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) +#[derive(Clone)] +struct EarliestWaitTargets { + settle: bool, + instant: Option, + changes: HashMap, SimValue>, +} + +impl fmt::Debug for EarliestWaitTargets { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_set().entries(self.iter()).finish() } } -impl Ord for WaitTarget { - fn cmp(&self, other: &Self) -> std::cmp::Ordering { - match (self, other) { - (WaitTarget::Settle, WaitTarget::Settle) => std::cmp::Ordering::Equal, - (WaitTarget::Settle, WaitTarget::Instant(_)) => std::cmp::Ordering::Less, - (WaitTarget::Instant(_), WaitTarget::Settle) => std::cmp::Ordering::Greater, - (WaitTarget::Instant(l), WaitTarget::Instant(r)) => l.cmp(r), +impl Default for EarliestWaitTargets { + fn default() -> Self { + Self { + settle: false, + instant: None, + changes: HashMap::new(), } } } +impl EarliestWaitTargets { + fn settle() -> Self { + Self { + settle: true, + instant: None, + changes: HashMap::new(), + } + } + fn instant(instant: SimInstant) -> Self { + Self { + settle: false, + instant: Some(instant), + changes: HashMap::new(), + } + } + fn len(&self) -> usize { + self.settle as usize + self.instant.is_some() as usize + self.changes.len() + } + fn is_empty(&self) -> bool { + self.len() == 0 + } + fn clear(&mut self) { + let Self { + settle, + instant, + changes, + } = self; + *settle = false; + *instant = None; + changes.clear(); + } + fn insert( + &mut self, + value: impl std::borrow::Borrow, ChangeValue>>, + ) where + ChangeValue: std::borrow::Borrow>, + { + let value = value.borrow(); + match value { + WaitTarget::Settle => self.settle = true, + WaitTarget::Instant(instant) => { + if self.instant.is_none_or(|v| v > *instant) { + self.instant = Some(*instant); + } + } + WaitTarget::Change { key, value } => { + self.changes + .entry(*key) + .or_insert_with(|| value.borrow().clone()); + } + } + } + fn convert_earlier_instants_to_settle(&mut self, instant: SimInstant) { + if self.instant.is_some_and(|v| v <= instant) { + self.settle = true; + self.instant = None; + } + } + fn iter<'a>( + &'a self, + ) -> impl Clone + + Iterator, &'a SimValue>> + + 'a { + self.settle + .then_some(WaitTarget::Settle) + .into_iter() + .chain(self.instant.map(|instant| WaitTarget::Instant(instant))) + .chain( + self.changes + .iter() + .map(|(&key, value)| WaitTarget::Change { key, value }), + ) + } +} + +impl>> + Extend, ChangeValue>> for EarliestWaitTargets +{ + fn extend, ChangeValue>>>( + &mut self, + iter: T, + ) { + iter.into_iter().for_each(|v| self.insert(v)) + } +} + +impl<'a, ChangeValue: std::borrow::Borrow>> + Extend<&'a WaitTarget, ChangeValue>> for EarliestWaitTargets +{ + fn extend, ChangeValue>>>( + &mut self, + iter: T, + ) { + iter.into_iter().for_each(|v| self.insert(v)) + } +} + +impl FromIterator for EarliestWaitTargets +where + Self: Extend, +{ + fn from_iter>(iter: T) -> Self { + let mut retval = Self::default(); + retval.extend(iter); + retval + } +} + struct SimulationExternModuleState { module_state: SimulationModuleState, sim: ExternModuleSimulation, running_generator: Option + 'static>>>, - wait_target: Option, + wait_targets: EarliestWaitTargets, } impl fmt::Debug for SimulationExternModuleState { @@ -6832,7 +6955,7 @@ impl fmt::Debug for SimulationExternModuleState { module_state, sim, running_generator, - wait_target, + wait_targets, } = self; f.debug_struct("SimulationExternModuleState") .field("module_state", module_state) @@ -6841,7 +6964,7 @@ impl fmt::Debug for SimulationExternModuleState { "running_generator", &running_generator.as_ref().map(|_| DebugAsDisplay("...")), ) - .field("wait_target", wait_target) + .field("wait_targets", wait_targets) .finish() } } @@ -6896,29 +7019,19 @@ impl MaybeNeedsSettleFn<&'_ mut interpreter::State> for ReadBo struct ReadFn { compiled_value: CompiledValue, io: Expr, + bits: BitVec, } impl MaybeNeedsSettleFn<&'_ mut interpreter::State> for ReadFn { type Output = SimValue; fn call(self, state: &mut interpreter::State) -> Self::Output { - let Self { compiled_value, io } = self; - let mut bits = BitVec::repeat(false, compiled_value.layout.ty.bit_width()); - SimulationImpl::read_write_sim_value_helper( - state, + let Self { compiled_value, - &mut bits, - |_signed, bits, value| ::copy_bits_from_bigint_wrapping(value, bits), - |_signed, bits, value| { - let bytes = value.to_le_bytes(); - let bitslice = BitSlice::::from_slice(&bytes); - bits.clone_from_bitslice(&bitslice[..bits.len()]); - }, - ); - SimValue { - ty: Expr::ty(io), + io, bits, - } + } = self; + SimulationImpl::read_no_settle_helper(state, io, compiled_value, bits) } } @@ -7017,7 +7130,7 @@ impl SimulationImpl { ), sim: simulation, running_generator: None, - wait_target: Some(WaitTarget::Settle), + wait_targets: EarliestWaitTargets::settle(), } }, )); @@ -7200,22 +7313,23 @@ impl SimulationImpl { } #[track_caller] fn advance_time(this_ref: &Rc>, duration: SimDuration) { - let instant = this_ref.borrow().instant + duration; - Self::run_until(this_ref, WaitTarget::Instant(instant)); + let run_target = this_ref.borrow().instant + duration; + Self::run_until(this_ref, run_target); } + /// clears `targets` #[must_use] - fn yield_advance_time_or_settle( + fn yield_wait<'a>( this: Rc>, module_index: usize, - duration: Option, - ) -> impl Future + 'static { - struct MyGenerator { + targets: &'a mut EarliestWaitTargets, + ) -> impl Future + 'a { + struct MyGenerator<'a> { sim: Rc>, yielded_at_all: bool, module_index: usize, - target: WaitTarget, + targets: &'a mut EarliestWaitTargets, } - impl Future for MyGenerator { + impl Future for MyGenerator<'_> { type Output = (); fn poll( @@ -7223,65 +7337,92 @@ impl SimulationImpl { cx: &mut std::task::Context<'_>, ) -> Poll { let this = &mut *self; - let yielded_at_all = mem::replace(&mut this.yielded_at_all, true); let mut sim = this.sim.borrow_mut(); let sim = &mut *sim; assert!(cx.waker().will_wake(&sim.generator_waker), "can't use ExternModuleSimulationState's methods outside of ExternModuleSimulation"); - if let WaitTarget::Instant(target) = this.target { - if target < sim.instant { - this.target = WaitTarget::Settle; - } else if yielded_at_all && target == sim.instant { - this.target = WaitTarget::Settle; - } + this.targets.convert_earlier_instants_to_settle(sim.instant); + if this.targets.is_empty() { + this.targets.settle = true; } - if let WaitTarget::Settle = this.target { - if yielded_at_all { + if this.targets.settle { + if this.yielded_at_all { + this.targets.clear(); return Poll::Ready(()); } } - let wait_target = sim.extern_modules[this.module_index] - .wait_target - .get_or_insert(this.target); - *wait_target = (*wait_target).min(this.target); + sim.extern_modules[this.module_index] + .wait_targets + .extend(this.targets.iter()); + this.targets.clear(); + this.yielded_at_all = true; Poll::Pending } } - let target = duration.map_or(WaitTarget::Settle, |duration| { - WaitTarget::Instant(this.borrow().instant + duration) - }); MyGenerator { sim: this, yielded_at_all: false, module_index, - target, + targets, } } - /// returns the next `WaitTarget` and the set of things ready to run then. - fn get_ready_to_run_set(&self, ready_to_run_set: &mut ReadyToRunSet) -> Option { + async fn yield_advance_time_or_settle( + this: Rc>, + module_index: usize, + duration: Option, + ) { + let mut targets = duration.map_or(EarliestWaitTargets::settle(), |duration| { + EarliestWaitTargets::instant(this.borrow().instant + duration) + }); + Self::yield_wait(this, module_index, &mut targets).await; + } + fn is_extern_module_ready_to_run(&mut self, module_index: usize) -> Option { + let module = &self.extern_modules[module_index]; + let mut retval = None; + for wait_target in module.wait_targets.iter() { + retval = match (wait_target, retval) { + (WaitTarget::Settle, _) => Some(self.instant), + (WaitTarget::Instant(instant), _) if instant <= self.instant => Some(self.instant), + (WaitTarget::Instant(instant), None) => Some(instant), + (WaitTarget::Instant(instant), Some(retval)) => Some(instant.min(retval)), + (WaitTarget::Change { key, value }, retval) => { + if Self::value_changed(&mut self.state, key, &value.bits) { + Some(self.instant) + } else { + retval + } + } + }; + if retval == Some(self.instant) { + break; + } + } + retval + } + fn get_ready_to_run_set(&mut self, ready_to_run_set: &mut ReadyToRunSet) -> Option { ready_to_run_set.clear(); - let mut wait_target = None; + let mut retval = None; if self.state_ready_to_run { ready_to_run_set.state_ready_to_run = true; - wait_target = Some(WaitTarget::Settle); + retval = Some(self.instant); } - for (module_index, extern_module) in self.extern_modules.iter().enumerate() { - let Some(extern_module_wait_target) = extern_module.wait_target else { + for module_index in 0..self.extern_modules.len() { + let Some(instant) = self.is_extern_module_ready_to_run(module_index) else { continue; }; - if let Some(wait_target) = &mut wait_target { - match extern_module_wait_target.cmp(wait_target) { + if let Some(retval) = &mut retval { + match instant.cmp(retval) { std::cmp::Ordering::Less => ready_to_run_set.clear(), std::cmp::Ordering::Equal => {} std::cmp::Ordering::Greater => continue, } } else { - wait_target = Some(extern_module_wait_target); + retval = Some(instant); } ready_to_run_set .extern_modules_ready_to_run .push(module_index); } - wait_target + retval } fn set_instant_no_sim(&mut self, instant: SimInstant) { self.instant = instant; @@ -7296,8 +7437,98 @@ impl SimulationImpl { Ok(trace_writer_state) }); } + #[must_use] #[track_caller] - fn run_until(this_ref: &Rc>, run_target: WaitTarget) { + fn run_state_settle_cycle(&mut self) -> bool { + self.state_ready_to_run = false; + self.state.setup_call(0); + if self.breakpoints.is_some() { + loop { + match self + .state + .run(self.breakpoints.as_mut().expect("just checked")) + { + RunResult::Break(break_action) => { + println!( + "hit breakpoint at:\n{:?}", + self.state.debug_insn_at(self.state.pc), + ); + match break_action { + BreakAction::DumpStateAndContinue => { + println!("{self:#?}"); + } + BreakAction::Continue => {} + } + } + RunResult::Return(()) => break, + } + } + } else { + let RunResult::Return(()) = self.state.run(()); + } + if self + .clocks_triggered + .iter() + .any(|i| self.state.small_slots[*i] != 0) + { + self.state_ready_to_run = true; + true + } else { + false + } + } + #[track_caller] + fn run_extern_modules_cycle( + this_ref: &Rc>, + generator_waker: &std::task::Waker, + extern_modules_ready_to_run: &[usize], + ) { + let mut this = this_ref.borrow_mut(); + for module_index in extern_modules_ready_to_run.iter().copied() { + let extern_module = &mut this.extern_modules[module_index]; + extern_module.wait_targets.clear(); + let mut generator = if !extern_module.module_state.did_initial_settle { + let sim = extern_module.sim; + drop(this); + Box::into_pin(sim.run(ExternModuleSimulationState { + sim_impl: this_ref.clone(), + module_index, + wait_for_changes_wait_targets: EarliestWaitTargets::default(), + })) + } else if let Some(generator) = extern_module.running_generator.take() { + drop(this); + generator + } else { + continue; + }; + let generator = match generator + .as_mut() + .poll(&mut std::task::Context::from_waker(generator_waker)) + { + Poll::Ready(()) => None, + Poll::Pending => Some(generator), + }; + this = this_ref.borrow_mut(); + this.extern_modules[module_index] + .module_state + .did_initial_settle = true; + if !this.extern_modules[module_index] + .module_state + .uninitialized_ios + .is_empty() + { + panic!( + "extern module didn't initialize all outputs before \ + waiting, settling, or reading any inputs: {}", + this.extern_modules[module_index].sim.source_location + ); + } + this.extern_modules[module_index].running_generator = generator; + } + } + /// clears `targets` + #[track_caller] + fn run_until(this_ref: &Rc>, run_target: SimInstant) { let mut this = this_ref.borrow_mut(); let mut ready_to_run_set = ReadyToRunSet::default(); let generator_waker = this.generator_waker.clone(); @@ -7305,10 +7536,7 @@ impl SimulationImpl { this.main_module.uninitialized_ios.is_empty(), "didn't initialize all inputs", ); - match run_target { - WaitTarget::Settle => {} - WaitTarget::Instant(run_target) => assert!(run_target >= this.instant), - } + let run_target = run_target.max(this.instant); let mut settle_cycle = 0; let mut run_extern_modules = true; loop { @@ -7318,92 +7546,25 @@ impl SimulationImpl { Some(next_wait_target) if next_wait_target <= run_target => next_wait_target, _ => break, }; - match next_wait_target { - WaitTarget::Settle => {} - WaitTarget::Instant(instant) => { - settle_cycle = 0; - this.set_instant_no_sim(instant); - } + if next_wait_target > this.instant { + settle_cycle = 0; + this.set_instant_no_sim(next_wait_target); } if run_extern_modules { - for module_index in ready_to_run_set.extern_modules_ready_to_run.drain(..) { - let extern_module = &mut this.extern_modules[module_index]; - extern_module.wait_target = None; - let mut generator = if !extern_module.module_state.did_initial_settle { - let sim = extern_module.sim; - drop(this); - Box::into_pin(sim.run(ExternModuleSimulationState { - sim_impl: this_ref.clone(), - module_index, - })) - } else if let Some(generator) = extern_module.running_generator.take() { - drop(this); - generator - } else { - continue; - }; - let generator = match generator - .as_mut() - .poll(&mut std::task::Context::from_waker(&generator_waker)) - { - Poll::Ready(()) => None, - Poll::Pending => Some(generator), - }; - this = this_ref.borrow_mut(); - this.extern_modules[module_index] - .module_state - .did_initial_settle = true; - if !this.extern_modules[module_index] - .module_state - .uninitialized_ios - .is_empty() - { - panic!( - "extern module didn't initialize all outputs before \ - waiting, settling, or reading any inputs: {}", - this.extern_modules[module_index].sim.source_location - ); - } - this.extern_modules[module_index].running_generator = generator; - } + drop(this); + Self::run_extern_modules_cycle( + this_ref, + &generator_waker, + &ready_to_run_set.extern_modules_ready_to_run, + ); + this = this_ref.borrow_mut(); } if ready_to_run_set.state_ready_to_run { - this.state_ready_to_run = false; - run_extern_modules = true; - this.state.setup_call(0); - if this.breakpoints.is_some() { - loop { - let this = &mut *this; - match this - .state - .run(this.breakpoints.as_mut().expect("just checked")) - { - RunResult::Break(break_action) => { - println!( - "hit breakpoint at:\n{:?}", - this.state.debug_insn_at(this.state.pc), - ); - match break_action { - BreakAction::DumpStateAndContinue => { - println!("{this:#?}"); - } - BreakAction::Continue => {} - } - } - RunResult::Return(()) => break, - } - } - } else { - let RunResult::Return(()) = this.state.run(()); - } - if this - .clocks_triggered - .iter() - .any(|i| this.state.small_slots[*i] != 0) - { - this.state_ready_to_run = true; + if this.run_state_settle_cycle() { // wait for clocks to settle before running extern modules again run_extern_modules = false; + } else { + run_extern_modules = true; } } if this.main_module.did_initial_settle { @@ -7434,14 +7595,14 @@ impl SimulationImpl { }); this.state.memory_write_log.clear(); } - match run_target { - WaitTarget::Settle => {} - WaitTarget::Instant(instant) => this.set_instant_no_sim(instant), + if run_target > this.instant { + this.set_instant_no_sim(run_target); } } #[track_caller] fn settle(this_ref: &Rc>) { - Self::run_until(this_ref, WaitTarget::Settle); + let run_target = this_ref.borrow().instant; + Self::run_until(this_ref, run_target); } fn get_module(&self, which_module: WhichModule) -> &SimulationModuleState { match which_module { @@ -7522,12 +7683,13 @@ impl SimulationImpl { } } #[track_caller] - fn read_write_sim_value_helper( + fn read_write_sim_value_helper( state: &mut interpreter::State, compiled_value: CompiledValue, - bits: &mut BitSlice, - read_write_big_scalar: impl Fn(bool, &mut BitSlice, &mut BigInt) + Copy, - read_write_small_scalar: impl Fn(bool, &mut BitSlice, &mut SmallUInt) + Copy, + start_bit_index: usize, + bits: &mut Bits, + read_write_big_scalar: impl Fn(bool, std::ops::Range, &mut Bits, &mut BigInt) + Copy, + read_write_small_scalar: impl Fn(bool, std::ops::Range, &mut Bits, &mut SmallUInt) + Copy, ) { match compiled_value.layout.body { CompiledTypeLayoutBody::Scalar => { @@ -7544,14 +7706,18 @@ impl SimulationImpl { CanonicalType::Clock(_) => false, CanonicalType::PhantomConst(_) => unreachable!(), }; + let bit_indexes = + start_bit_index..start_bit_index + compiled_value.layout.ty.bit_width(); match compiled_value.range.len() { TypeLen::A_SMALL_SLOT => read_write_small_scalar( signed, + bit_indexes, bits, &mut state.small_slots[compiled_value.range.small_slots.start], ), TypeLen::A_BIG_SLOT => read_write_big_scalar( signed, + bit_indexes, bits, &mut state.big_slots[compiled_value.range.big_slots.start], ), @@ -7571,7 +7737,8 @@ impl SimulationImpl { .index_array(element.layout.len(), element_index), write: None, }, - &mut bits[element_index * element_bit_width..][..element_bit_width], + start_bit_index + element_index * element_bit_width, + bits, read_write_big_scalar, read_write_small_scalar, ); @@ -7580,7 +7747,7 @@ impl SimulationImpl { CompiledTypeLayoutBody::Bundle { fields } => { let ty = Bundle::from_canonical(compiled_value.layout.ty); for ( - (field, offset), + (_field, offset), CompiledBundleField { offset: layout_offset, ty: field_layout, @@ -7597,7 +7764,8 @@ impl SimulationImpl { )), write: None, }, - &mut bits[offset..][..field.ty.bit_width()], + start_bit_index + offset, + bits, read_write_big_scalar, read_write_small_scalar, ); @@ -7606,21 +7774,89 @@ impl SimulationImpl { } } #[track_caller] + fn read_no_settle_helper( + state: &mut interpreter::State, + io: Expr, + compiled_value: CompiledValue, + mut bits: BitVec, + ) -> SimValue { + bits.clear(); + bits.resize(compiled_value.layout.ty.bit_width(), false); + SimulationImpl::read_write_sim_value_helper( + state, + compiled_value, + 0, + &mut bits, + |_signed, bit_range, bits, value| { + ::copy_bits_from_bigint_wrapping(value, &mut bits[bit_range]); + }, + |_signed, bit_range, bits, value| { + let bytes = value.to_le_bytes(); + let bitslice = BitSlice::::from_slice(&bytes); + let bitslice = &bitslice[..bit_range.len()]; + bits[bit_range].clone_from_bitslice(bitslice); + }, + ); + SimValue { + ty: Expr::ty(io), + bits, + } + } + /// doesn't modify `bits` + fn value_changed( + state: &mut interpreter::State, + compiled_value: CompiledValue, + mut bits: &BitSlice, + ) -> bool { + assert_eq!(bits.len(), compiled_value.layout.ty.bit_width()); + let any_change = std::cell::Cell::new(false); + SimulationImpl::read_write_sim_value_helper( + state, + compiled_value, + 0, + &mut bits, + |_signed, bit_range, bits, value| { + if !::bits_equal_bigint_wrapping(value, &bits[bit_range]) { + any_change.set(true); + } + }, + |_signed, bit_range, bits, value| { + let bytes = value.to_le_bytes(); + let bitslice = BitSlice::::from_slice(&bytes); + let bitslice = &bitslice[..bit_range.len()]; + if bits[bit_range] != *bitslice { + any_change.set(true); + } + }, + ); + any_change.get() + } + #[track_caller] fn read( &mut self, io: Expr, which_module: WhichModule, - ) -> MaybeNeedsSettle> { - self.get_module(which_module) - .read_helper(io, which_module) - .map(|compiled_value| ReadFn { compiled_value, io }) - .apply_no_settle(&mut self.state) + ) -> ( + CompiledValue, + MaybeNeedsSettle>, + ) { + let compiled_value = self.get_module(which_module).read_helper(io, which_module); + let value = compiled_value + .map(|compiled_value| ReadFn { + compiled_value, + io, + bits: BitVec::new(), + }) + .apply_no_settle(&mut self.state); + let (MaybeNeedsSettle::NeedsSettle(compiled_value) + | MaybeNeedsSettle::NoSettleNeeded(compiled_value)) = compiled_value; + (compiled_value, value) } #[track_caller] fn write( &mut self, io: Expr, - value: SimValue, + value: &SimValue, which_module: WhichModule, ) { let compiled_value = self @@ -7631,20 +7867,22 @@ impl SimulationImpl { Self::read_write_sim_value_helper( &mut self.state, compiled_value, - &mut value.into_bits(), - |signed, bits, value| { + 0, + &mut value.bits(), + |signed, bit_range, bits, value| { if signed { - *value = SInt::bits_to_bigint(bits); + *value = SInt::bits_to_bigint(&bits[bit_range]); } else { - *value = UInt::bits_to_bigint(bits); + *value = UInt::bits_to_bigint(&bits[bit_range]); } }, - |signed, bits, value| { + |signed, bit_range, bits, value| { let mut small_value = [0; mem::size_of::()]; - if signed && bits.last().as_deref().copied() == Some(true) { + if signed && bits[bit_range.clone()].last().as_deref().copied() == Some(true) { small_value.fill(u8::MAX); } - small_value.view_bits_mut::()[0..bits.len()].clone_from_bitslice(bits); + small_value.view_bits_mut::()[0..bit_range.len()] + .clone_from_bitslice(&bits[bit_range]); *value = SmallUInt::from_le_bytes(small_value); }, ); @@ -7920,14 +8158,14 @@ macro_rules! impl_simulation_methods { let retval = $self .sim_impl .borrow_mut() - .read(Expr::canonical(io), $which_module); + .read(Expr::canonical(io), $which_module).1; SimValue::from_canonical($self.settle_if_needed(retval)$(.$await)?) } $(#[$track_caller])? pub $($async)? fn write>(&mut $self, io: Expr, value: V) { $self.sim_impl.borrow_mut().write( Expr::canonical(io), - value.into_sim_value(Expr::ty(io)).into_canonical(), + &value.into_sim_value(Expr::ty(io)).into_canonical(), $which_module, ); } @@ -8008,6 +8246,7 @@ impl Simulation { pub struct ExternModuleSimulationState { sim_impl: Rc>, module_index: usize, + wait_for_changes_wait_targets: EarliestWaitTargets, } impl fmt::Debug for ExternModuleSimulationState { @@ -8015,11 +8254,12 @@ impl fmt::Debug for ExternModuleSimulationState { let Self { sim_impl: _, module_index, + wait_for_changes_wait_targets: _, } = self; f.debug_struct("ExternModuleSimulationState") .field("sim_impl", &DebugAsDisplay("...")) .field("module_index", module_index) - .finish() + .finish_non_exhaustive() } } @@ -8036,6 +8276,42 @@ impl ExternModuleSimulationState { ) .await } + pub async fn wait_for_changes>( + &mut self, + iter: I, + timeout: Option, + ) { + self.wait_for_changes_wait_targets.clear(); + let which_module = WhichModule::Extern { + module_index: self.module_index, + }; + for io in iter { + let io = Expr::canonical(io.to_expr()); + let (key, value) = self.sim_impl.borrow_mut().read(io, which_module); + let value = self.settle_if_needed(value).await; + self.wait_for_changes_wait_targets + .insert(WaitTarget::Change { key, value }); + } + if let Some(timeout) = timeout { + self.wait_for_changes_wait_targets.instant = + Some(self.sim_impl.borrow().instant + timeout); + } + SimulationImpl::yield_wait( + self.sim_impl.clone(), + self.module_index, + &mut self.wait_for_changes_wait_targets, + ) + .await; + } + pub async fn wait_for_clock_edge(&mut self, clk: impl ToExpr) { + let clk = clk.to_expr(); + while self.read_clock(clk).await { + self.wait_for_changes([clk], None).await; + } + while !self.read_clock(clk).await { + self.wait_for_changes([clk], None).await; + } + } async fn settle_if_needed(&mut self, v: MaybeNeedsSettle) -> O where for<'a> F: MaybeNeedsSettleFn<&'a mut interpreter::State, Output = O>, diff --git a/crates/fayalite/tests/sim.rs b/crates/fayalite/tests/sim.rs index e516f22..6d20715 100644 --- a/crates/fayalite/tests/sim.rs +++ b/crates/fayalite/tests/sim.rs @@ -1485,3 +1485,50 @@ fn test_extern_module() { panic!(); } } + +#[hdl_module(outline_generated, extern)] +pub fn extern_module2() { + #[hdl] + let en: Bool = m.input(); + #[hdl] + let clk: Clock = m.input(); + #[hdl] + let o: UInt<8> = m.output(); + m.extern_module_simulation_fn((en, clk, o), |(en, clk, o), mut sim| async move { + for b in "Hello, World!\n".bytes().cycle() { + sim.write(o, b).await; + loop { + sim.wait_for_clock_edge(clk).await; + if sim.read_bool(en).await { + break; + } + } + } + }); +} + +#[test] +fn test_extern_module2() { + let _n = SourceLocation::normalize_files_for_tests(); + let mut sim = Simulation::new(extern_module2()); + let mut writer = RcWriter::default(); + sim.add_trace_writer(VcdWriterDecls::new(writer.clone())); + for i in 0..30 { + sim.write(sim.io().en, i % 10 < 5); + sim.write(sim.io().clk, false); + sim.advance_time(SimDuration::from_micros(1)); + sim.write(sim.io().clk, true); + sim.advance_time(SimDuration::from_micros(1)); + } + sim.flush_traces().unwrap(); + let vcd = String::from_utf8(writer.take()).unwrap(); + println!("####### VCD:\n{vcd}\n#######"); + if vcd != include_str!("sim/expected/extern_module2.vcd") { + panic!(); + } + let sim_debug = format!("{sim:#?}"); + println!("#######\n{sim_debug}\n#######"); + if sim_debug != include_str!("sim/expected/extern_module2.txt") { + panic!(); + } +} diff --git a/crates/fayalite/tests/sim/expected/extern_module.txt b/crates/fayalite/tests/sim/expected/extern_module.txt index 23af0b2..6cce70b 100644 --- a/crates/fayalite/tests/sim/expected/extern_module.txt +++ b/crates/fayalite/tests/sim/expected/extern_module.txt @@ -153,11 +153,11 @@ Simulation { running_generator: Some( ..., ), - wait_target: Some( + wait_targets: { Instant( 20.500000000000 μs, ), - ), + }, }, ], state_ready_to_run: false, diff --git a/crates/fayalite/tests/sim/expected/extern_module2.txt b/crates/fayalite/tests/sim/expected/extern_module2.txt new file mode 100644 index 0000000..96710fb --- /dev/null +++ b/crates/fayalite/tests/sim/expected/extern_module2.txt @@ -0,0 +1,308 @@ +Simulation { + state: State { + insns: Insns { + state_layout: StateLayout { + ty: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 3, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(extern_module2: extern_module2).extern_module2::en", + ty: Bool, + }, + SlotDebugData { + name: "InstantiatedModule(extern_module2: extern_module2).extern_module2::clk", + ty: Clock, + }, + SlotDebugData { + name: "InstantiatedModule(extern_module2: extern_module2).extern_module2::o", + ty: UInt<8>, + }, + ], + .. + }, + }, + memories: StatePartLayout { + len: 0, + debug_data: [], + layout_data: [], + .. + }, + }, + insns: [ + // at: module-XXXXXXXXXX.rs:1:1 + 0: Return, + ], + .. + }, + pc: 0, + memory_write_log: [], + memories: StatePart { + value: [], + }, + small_slots: StatePart { + value: [], + }, + big_slots: StatePart { + value: [ + 0, + 1, + 101, + ], + }, + }, + io: Instance { + name: ::extern_module2, + instantiated: Module { + name: extern_module2, + .. + }, + }, + main_module: SimulationModuleState { + base_targets: [ + Instance { + name: ::extern_module2, + instantiated: Module { + name: extern_module2, + .. + }, + }.en, + Instance { + name: ::extern_module2, + instantiated: Module { + name: extern_module2, + .. + }, + }.clk, + Instance { + name: ::extern_module2, + instantiated: Module { + name: extern_module2, + .. + }, + }.o, + ], + uninitialized_ios: {}, + io_targets: { + Instance { + name: ::extern_module2, + instantiated: Module { + name: extern_module2, + .. + }, + }.clk, + Instance { + name: ::extern_module2, + instantiated: Module { + name: extern_module2, + .. + }, + }.en, + Instance { + name: ::extern_module2, + instantiated: Module { + name: extern_module2, + .. + }, + }.o, + }, + did_initial_settle: true, + }, + extern_modules: [ + SimulationExternModuleState { + module_state: SimulationModuleState { + base_targets: [ + ModuleIO { + name: extern_module2::en, + is_input: true, + ty: Bool, + .. + }, + ModuleIO { + name: extern_module2::clk, + is_input: true, + ty: Clock, + .. + }, + ModuleIO { + name: extern_module2::o, + is_input: false, + ty: UInt<8>, + .. + }, + ], + uninitialized_ios: {}, + io_targets: { + ModuleIO { + name: extern_module2::clk, + is_input: true, + ty: Clock, + .. + }, + ModuleIO { + name: extern_module2::en, + is_input: true, + ty: Bool, + .. + }, + ModuleIO { + name: extern_module2::o, + is_input: false, + ty: UInt<8>, + .. + }, + }, + did_initial_settle: true, + }, + sim: ExternModuleSimulation { + generator: SimGeneratorFn { + args: ( + ModuleIO { + name: extern_module2::en, + is_input: true, + ty: Bool, + .. + }, + ModuleIO { + name: extern_module2::clk, + is_input: true, + ty: Clock, + .. + }, + ModuleIO { + name: extern_module2::o, + is_input: false, + ty: UInt<8>, + .. + }, + ), + f: ..., + }, + source_location: SourceLocation( + module-XXXXXXXXXX.rs:5:1, + ), + }, + running_generator: Some( + ..., + ), + wait_targets: { + Change { + key: CompiledValue { + layout: CompiledTypeLayout { + ty: Clock, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(extern_module2: extern_module2).extern_module2::clk", + ty: Clock, + }, + ], + .. + }, + }, + body: Scalar, + }, + range: TypeIndexRange { + small_slots: StatePartIndexRange { start: 0, len: 0 }, + big_slots: StatePartIndexRange { start: 1, len: 1 }, + }, + write: None, + }, + value: SimValue { + ty: Clock, + bits: 0x1, + }, + }, + }, + }, + ], + state_ready_to_run: false, + trace_decls: TraceModule { + name: "extern_module2", + children: [ + TraceModuleIO { + name: "en", + child: TraceBool { + location: TraceScalarId(0), + name: "en", + flow: Source, + }, + ty: Bool, + flow: Source, + }, + TraceModuleIO { + name: "clk", + child: TraceClock { + location: TraceScalarId(1), + name: "clk", + flow: Source, + }, + ty: Clock, + flow: Source, + }, + TraceModuleIO { + name: "o", + child: TraceUInt { + location: TraceScalarId(2), + name: "o", + ty: UInt<8>, + flow: Sink, + }, + ty: UInt<8>, + flow: Sink, + }, + ], + }, + traces: [ + SimTrace { + id: TraceScalarId(0), + kind: BigBool { + index: StatePartIndex(0), + }, + state: 0x0, + last_state: 0x0, + }, + SimTrace { + id: TraceScalarId(1), + kind: BigClock { + index: StatePartIndex(1), + }, + state: 0x1, + last_state: 0x1, + }, + SimTrace { + id: TraceScalarId(2), + kind: BigUInt { + index: StatePartIndex(2), + ty: UInt<8>, + }, + state: 0x65, + last_state: 0x65, + }, + ], + trace_memories: {}, + trace_writers: [ + Running( + VcdWriter { + finished_init: true, + timescale: 1 ps, + .. + }, + ), + ], + instant: 60 μs, + clocks_triggered: [], + .. +} \ No newline at end of file diff --git a/crates/fayalite/tests/sim/expected/extern_module2.vcd b/crates/fayalite/tests/sim/expected/extern_module2.vcd new file mode 100644 index 0000000..464f4bd --- /dev/null +++ b/crates/fayalite/tests/sim/expected/extern_module2.vcd @@ -0,0 +1,150 @@ +$timescale 1 ps $end +$scope module extern_module2 $end +$var wire 1 ! en $end +$var wire 1 " clk $end +$var wire 8 # o $end +$upscope $end +$enddefinitions $end +$dumpvars +1! +0" +b1001000 # +$end +#1000000 +1" +b1100101 # +#2000000 +0" +#3000000 +1" +b1101100 # +#4000000 +0" +#5000000 +1" +#6000000 +0" +#7000000 +1" +b1101111 # +#8000000 +0" +#9000000 +1" +b101100 # +#10000000 +0! +0" +#11000000 +1" +#12000000 +0" +#13000000 +1" +#14000000 +0" +#15000000 +1" +#16000000 +0" +#17000000 +1" +#18000000 +0" +#19000000 +1" +#20000000 +1! +0" +#21000000 +1" +b100000 # +#22000000 +0" +#23000000 +1" +b1010111 # +#24000000 +0" +#25000000 +1" +b1101111 # +#26000000 +0" +#27000000 +1" +b1110010 # +#28000000 +0" +#29000000 +1" +b1101100 # +#30000000 +0! +0" +#31000000 +1" +#32000000 +0" +#33000000 +1" +#34000000 +0" +#35000000 +1" +#36000000 +0" +#37000000 +1" +#38000000 +0" +#39000000 +1" +#40000000 +1! +0" +#41000000 +1" +b1100100 # +#42000000 +0" +#43000000 +1" +b100001 # +#44000000 +0" +#45000000 +1" +b1010 # +#46000000 +0" +#47000000 +1" +b1001000 # +#48000000 +0" +#49000000 +1" +b1100101 # +#50000000 +0! +0" +#51000000 +1" +#52000000 +0" +#53000000 +1" +#54000000 +0" +#55000000 +1" +#56000000 +0" +#57000000 +1" +#58000000 +0" +#59000000 +1" +#60000000 From fdc73b5f3b1d708cded491a1a16292e77a1f3daf Mon Sep 17 00:00:00 2001 From: Jacob Lifshay Date: Tue, 25 Mar 2025 18:53:46 -0700 Subject: [PATCH 15/38] add ripple counter test to test simulating alternating circuits and extern modules --- crates/fayalite/tests/sim.rs | 81 + .../tests/sim/expected/ripple_counter.txt | 1492 ++++++++++++++ .../tests/sim/expected/ripple_counter.vcd | 1753 +++++++++++++++++ 3 files changed, 3326 insertions(+) create mode 100644 crates/fayalite/tests/sim/expected/ripple_counter.txt create mode 100644 crates/fayalite/tests/sim/expected/ripple_counter.vcd diff --git a/crates/fayalite/tests/sim.rs b/crates/fayalite/tests/sim.rs index 6d20715..450be54 100644 --- a/crates/fayalite/tests/sim.rs +++ b/crates/fayalite/tests/sim.rs @@ -3,6 +3,7 @@ use fayalite::{ int::UIntValue, + module::{instance_with_loc, reg_builder_with_loc}, prelude::*, reset::ResetType, sim::{time::SimDuration, vcd::VcdWriterDecls, Simulation, ToSimValue}, @@ -1532,3 +1533,83 @@ fn test_extern_module2() { panic!(); } } + +// use an extern module to simulate a register to test that the +// simulator can handle chains of alternating circuits and extern modules. +#[hdl_module(outline_generated, extern)] +pub fn sw_reg() { + #[hdl] + let clk: Clock = m.input(); + #[hdl] + let o: Bool = m.output(); + m.extern_module_simulation_fn((clk, o), |(clk, o), mut sim| async move { + let mut state = false; + loop { + sim.write(o, state).await; + sim.wait_for_clock_edge(clk).await; + state = !state; + } + }); +} + +#[hdl_module(outline_generated)] +pub fn ripple_counter() { + #[hdl] + let clk: Clock = m.input(); + #[hdl] + let o: UInt<6> = m.output(); + + #[hdl] + let bits: Array = wire(); + + connect_any(o, bits.cast_to_bits()); + + let mut clk_in = clk; + for (i, bit) in bits.into_iter().enumerate() { + if i % 2 == 0 { + let bit_reg = reg_builder_with_loc(&format!("bit_reg_{i}"), SourceLocation::caller()) + .clock_domain( + #[hdl] + ClockDomain { + clk: clk_in, + rst: false.to_sync_reset(), + }, + ) + .no_reset(Bool) + .build(); + connect(bit, bit_reg); + connect(bit_reg, !bit_reg); + } else { + let bit_reg = + instance_with_loc(&format!("bit_reg_{i}"), sw_reg(), SourceLocation::caller()); + connect(bit_reg.clk, clk_in); + connect(bit, bit_reg.o); + } + clk_in = bit.to_clock(); + } +} + +#[test] +fn test_ripple_counter() { + let _n = SourceLocation::normalize_files_for_tests(); + let mut sim = Simulation::new(ripple_counter()); + let mut writer = RcWriter::default(); + sim.add_trace_writer(VcdWriterDecls::new(writer.clone())); + for _ in 0..0x80 { + sim.write(sim.io().clk, false); + sim.advance_time(SimDuration::from_micros(1)); + sim.write(sim.io().clk, true); + sim.advance_time(SimDuration::from_micros(1)); + } + sim.flush_traces().unwrap(); + let vcd = String::from_utf8(writer.take()).unwrap(); + println!("####### VCD:\n{vcd}\n#######"); + if vcd != include_str!("sim/expected/ripple_counter.vcd") { + panic!(); + } + let sim_debug = format!("{sim:#?}"); + println!("#######\n{sim_debug}\n#######"); + if sim_debug != include_str!("sim/expected/ripple_counter.txt") { + panic!(); + } +} diff --git a/crates/fayalite/tests/sim/expected/ripple_counter.txt b/crates/fayalite/tests/sim/expected/ripple_counter.txt new file mode 100644 index 0000000..e290ace --- /dev/null +++ b/crates/fayalite/tests/sim/expected/ripple_counter.txt @@ -0,0 +1,1492 @@ +Simulation { + state: State { + insns: Insns { + state_layout: StateLayout { + ty: TypeLayout { + small_slots: StatePartLayout { + len: 9, + debug_data: [ + SlotDebugData { + name: "", + ty: Bool, + }, + SlotDebugData { + name: "", + ty: Bool, + }, + SlotDebugData { + name: "", + ty: Bool, + }, + SlotDebugData { + name: "", + ty: Bool, + }, + SlotDebugData { + name: "", + ty: Bool, + }, + SlotDebugData { + name: "", + ty: Bool, + }, + SlotDebugData { + name: "", + ty: Bool, + }, + SlotDebugData { + name: "", + ty: Bool, + }, + SlotDebugData { + name: "", + ty: Bool, + }, + ], + .. + }, + big_slots: StatePartLayout { + len: 58, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(ripple_counter: ripple_counter).ripple_counter::clk", + ty: Clock, + }, + SlotDebugData { + name: "InstantiatedModule(ripple_counter: ripple_counter).ripple_counter::o", + ty: UInt<6>, + }, + SlotDebugData { + name: "InstantiatedModule(ripple_counter: ripple_counter).ripple_counter::bits[0]", + ty: Bool, + }, + SlotDebugData { + name: "InstantiatedModule(ripple_counter: ripple_counter).ripple_counter::bits[1]", + ty: Bool, + }, + SlotDebugData { + name: "InstantiatedModule(ripple_counter: ripple_counter).ripple_counter::bits[2]", + ty: Bool, + }, + SlotDebugData { + name: "InstantiatedModule(ripple_counter: ripple_counter).ripple_counter::bits[3]", + ty: Bool, + }, + SlotDebugData { + name: "InstantiatedModule(ripple_counter: ripple_counter).ripple_counter::bits[4]", + ty: Bool, + }, + SlotDebugData { + name: "InstantiatedModule(ripple_counter: ripple_counter).ripple_counter::bits[5]", + ty: Bool, + }, + SlotDebugData { + name: "", + ty: UInt<1>, + }, + SlotDebugData { + name: "", + ty: UInt<1>, + }, + SlotDebugData { + name: "", + ty: UInt<2>, + }, + SlotDebugData { + name: "", + ty: UInt<2>, + }, + SlotDebugData { + name: "", + ty: UInt<1>, + }, + SlotDebugData { + name: "", + ty: UInt<3>, + }, + SlotDebugData { + name: "", + ty: UInt<3>, + }, + SlotDebugData { + name: "", + ty: UInt<1>, + }, + SlotDebugData { + name: "", + ty: UInt<4>, + }, + SlotDebugData { + name: "", + ty: UInt<4>, + }, + SlotDebugData { + name: "", + ty: UInt<1>, + }, + SlotDebugData { + name: "", + ty: UInt<5>, + }, + SlotDebugData { + name: "", + ty: UInt<5>, + }, + SlotDebugData { + name: "", + ty: UInt<1>, + }, + SlotDebugData { + name: "", + ty: UInt<6>, + }, + SlotDebugData { + name: "", + ty: UInt<6>, + }, + SlotDebugData { + name: "InstantiatedModule(ripple_counter: ripple_counter).ripple_counter::bit_reg_0", + ty: Bool, + }, + SlotDebugData { + name: "InstantiatedModule(ripple_counter: ripple_counter).ripple_counter::bit_reg_0$next", + ty: Bool, + }, + SlotDebugData { + name: ".clk", + ty: Clock, + }, + SlotDebugData { + name: ".rst", + ty: SyncReset, + }, + SlotDebugData { + name: "", + ty: Bool, + }, + SlotDebugData { + name: "", + ty: SyncReset, + }, + SlotDebugData { + name: "", + ty: Bool, + }, + SlotDebugData { + name: "InstantiatedModule(ripple_counter: ripple_counter).ripple_counter::bit_reg_1.clk", + ty: Clock, + }, + SlotDebugData { + name: "InstantiatedModule(ripple_counter: ripple_counter).ripple_counter::bit_reg_1.o", + ty: Bool, + }, + SlotDebugData { + name: "InstantiatedModule(ripple_counter.bit_reg_1: sw_reg).sw_reg::clk", + ty: Clock, + }, + SlotDebugData { + name: "InstantiatedModule(ripple_counter.bit_reg_1: sw_reg).sw_reg::o", + ty: Bool, + }, + SlotDebugData { + name: "", + ty: Clock, + }, + SlotDebugData { + name: "InstantiatedModule(ripple_counter: ripple_counter).ripple_counter::bit_reg_2", + ty: Bool, + }, + SlotDebugData { + name: "InstantiatedModule(ripple_counter: ripple_counter).ripple_counter::bit_reg_2$next", + ty: Bool, + }, + SlotDebugData { + name: ".clk", + ty: Clock, + }, + SlotDebugData { + name: ".rst", + ty: SyncReset, + }, + SlotDebugData { + name: "", + ty: Clock, + }, + SlotDebugData { + name: "", + ty: Bool, + }, + SlotDebugData { + name: "InstantiatedModule(ripple_counter: ripple_counter).ripple_counter::bit_reg_3.clk", + ty: Clock, + }, + SlotDebugData { + name: "InstantiatedModule(ripple_counter: ripple_counter).ripple_counter::bit_reg_3.o", + ty: Bool, + }, + SlotDebugData { + name: "InstantiatedModule(ripple_counter.bit_reg_3: sw_reg).sw_reg::clk", + ty: Clock, + }, + SlotDebugData { + name: "InstantiatedModule(ripple_counter.bit_reg_3: sw_reg).sw_reg::o", + ty: Bool, + }, + SlotDebugData { + name: "", + ty: Clock, + }, + SlotDebugData { + name: "InstantiatedModule(ripple_counter: ripple_counter).ripple_counter::bit_reg_4", + ty: Bool, + }, + SlotDebugData { + name: "InstantiatedModule(ripple_counter: ripple_counter).ripple_counter::bit_reg_4$next", + ty: Bool, + }, + SlotDebugData { + name: ".clk", + ty: Clock, + }, + SlotDebugData { + name: ".rst", + ty: SyncReset, + }, + SlotDebugData { + name: "", + ty: Clock, + }, + SlotDebugData { + name: "", + ty: Bool, + }, + SlotDebugData { + name: "InstantiatedModule(ripple_counter: ripple_counter).ripple_counter::bit_reg_5.clk", + ty: Clock, + }, + SlotDebugData { + name: "InstantiatedModule(ripple_counter: ripple_counter).ripple_counter::bit_reg_5.o", + ty: Bool, + }, + SlotDebugData { + name: "InstantiatedModule(ripple_counter.bit_reg_5: sw_reg).sw_reg::clk", + ty: Clock, + }, + SlotDebugData { + name: "InstantiatedModule(ripple_counter.bit_reg_5: sw_reg).sw_reg::o", + ty: Bool, + }, + SlotDebugData { + name: "", + ty: Clock, + }, + ], + .. + }, + }, + memories: StatePartLayout { + len: 0, + debug_data: [], + layout_data: [], + .. + }, + }, + insns: [ + // at: module-XXXXXXXXXX.rs:9:1 + 0: Copy { + dest: StatePartIndex(54), // (0x0) SlotDebugData { name: "InstantiatedModule(ripple_counter: ripple_counter).ripple_counter::bit_reg_5.o", ty: Bool }, + src: StatePartIndex(56), // (0x0) SlotDebugData { name: "InstantiatedModule(ripple_counter.bit_reg_5: sw_reg).sw_reg::o", ty: Bool }, + }, + // at: module-XXXXXXXXXX.rs:11:1 + 1: Copy { + dest: StatePartIndex(7), // (0x0) SlotDebugData { name: "InstantiatedModule(ripple_counter: ripple_counter).ripple_counter::bits[5]", ty: Bool }, + src: StatePartIndex(54), // (0x0) SlotDebugData { name: "InstantiatedModule(ripple_counter: ripple_counter).ripple_counter::bit_reg_5.o", ty: Bool }, + }, + // at: module-XXXXXXXXXX.rs:1:1 + 2: NotU { + dest: StatePartIndex(52), // (0x1) SlotDebugData { name: "", ty: Bool }, + src: StatePartIndex(47), // (0x0) SlotDebugData { name: "InstantiatedModule(ripple_counter: ripple_counter).ripple_counter::bit_reg_4", ty: Bool }, + width: 1, + }, + // at: module-XXXXXXXXXX.rs:8:1 + 3: Copy { + dest: StatePartIndex(48), // (0x1) SlotDebugData { name: "InstantiatedModule(ripple_counter: ripple_counter).ripple_counter::bit_reg_4$next", ty: Bool }, + src: StatePartIndex(52), // (0x1) SlotDebugData { name: "", ty: Bool }, + }, + // at: module-XXXXXXXXXX.rs:7:1 + 4: Copy { + dest: StatePartIndex(6), // (0x0) SlotDebugData { name: "InstantiatedModule(ripple_counter: ripple_counter).ripple_counter::bits[4]", ty: Bool }, + src: StatePartIndex(47), // (0x0) SlotDebugData { name: "InstantiatedModule(ripple_counter: ripple_counter).ripple_counter::bit_reg_4", ty: Bool }, + }, + // at: module-XXXXXXXXXX.rs:1:1 + 5: Copy { + dest: StatePartIndex(57), // (0x0) SlotDebugData { name: "", ty: Clock }, + src: StatePartIndex(6), // (0x0) SlotDebugData { name: "InstantiatedModule(ripple_counter: ripple_counter).ripple_counter::bits[4]", ty: Bool }, + }, + // at: module-XXXXXXXXXX.rs:10:1 + 6: Copy { + dest: StatePartIndex(53), // (0x0) SlotDebugData { name: "InstantiatedModule(ripple_counter: ripple_counter).ripple_counter::bit_reg_5.clk", ty: Clock }, + src: StatePartIndex(57), // (0x0) SlotDebugData { name: "", ty: Clock }, + }, + // at: module-XXXXXXXXXX.rs:9:1 + 7: Copy { + dest: StatePartIndex(55), // (0x0) SlotDebugData { name: "InstantiatedModule(ripple_counter.bit_reg_5: sw_reg).sw_reg::clk", ty: Clock }, + src: StatePartIndex(53), // (0x0) SlotDebugData { name: "InstantiatedModule(ripple_counter: ripple_counter).ripple_counter::bit_reg_5.clk", ty: Clock }, + }, + 8: Copy { + dest: StatePartIndex(43), // (0x0) SlotDebugData { name: "InstantiatedModule(ripple_counter: ripple_counter).ripple_counter::bit_reg_3.o", ty: Bool }, + src: StatePartIndex(45), // (0x0) SlotDebugData { name: "InstantiatedModule(ripple_counter.bit_reg_3: sw_reg).sw_reg::o", ty: Bool }, + }, + // at: module-XXXXXXXXXX.rs:11:1 + 9: Copy { + dest: StatePartIndex(5), // (0x0) SlotDebugData { name: "InstantiatedModule(ripple_counter: ripple_counter).ripple_counter::bits[3]", ty: Bool }, + src: StatePartIndex(43), // (0x0) SlotDebugData { name: "InstantiatedModule(ripple_counter: ripple_counter).ripple_counter::bit_reg_3.o", ty: Bool }, + }, + // at: module-XXXXXXXXXX.rs:1:1 + 10: Copy { + dest: StatePartIndex(51), // (0x0) SlotDebugData { name: "", ty: Clock }, + src: StatePartIndex(5), // (0x0) SlotDebugData { name: "InstantiatedModule(ripple_counter: ripple_counter).ripple_counter::bits[3]", ty: Bool }, + }, + 11: NotU { + dest: StatePartIndex(41), // (0x1) SlotDebugData { name: "", ty: Bool }, + src: StatePartIndex(36), // (0x0) SlotDebugData { name: "InstantiatedModule(ripple_counter: ripple_counter).ripple_counter::bit_reg_2", ty: Bool }, + width: 1, + }, + // at: module-XXXXXXXXXX.rs:8:1 + 12: Copy { + dest: StatePartIndex(37), // (0x1) SlotDebugData { name: "InstantiatedModule(ripple_counter: ripple_counter).ripple_counter::bit_reg_2$next", ty: Bool }, + src: StatePartIndex(41), // (0x1) SlotDebugData { name: "", ty: Bool }, + }, + // at: module-XXXXXXXXXX.rs:7:1 + 13: Copy { + dest: StatePartIndex(4), // (0x0) SlotDebugData { name: "InstantiatedModule(ripple_counter: ripple_counter).ripple_counter::bits[2]", ty: Bool }, + src: StatePartIndex(36), // (0x0) SlotDebugData { name: "InstantiatedModule(ripple_counter: ripple_counter).ripple_counter::bit_reg_2", ty: Bool }, + }, + // at: module-XXXXXXXXXX.rs:1:1 + 14: Copy { + dest: StatePartIndex(46), // (0x0) SlotDebugData { name: "", ty: Clock }, + src: StatePartIndex(4), // (0x0) SlotDebugData { name: "InstantiatedModule(ripple_counter: ripple_counter).ripple_counter::bits[2]", ty: Bool }, + }, + // at: module-XXXXXXXXXX.rs:10:1 + 15: Copy { + dest: StatePartIndex(42), // (0x0) SlotDebugData { name: "InstantiatedModule(ripple_counter: ripple_counter).ripple_counter::bit_reg_3.clk", ty: Clock }, + src: StatePartIndex(46), // (0x0) SlotDebugData { name: "", ty: Clock }, + }, + // at: module-XXXXXXXXXX.rs:9:1 + 16: Copy { + dest: StatePartIndex(44), // (0x0) SlotDebugData { name: "InstantiatedModule(ripple_counter.bit_reg_3: sw_reg).sw_reg::clk", ty: Clock }, + src: StatePartIndex(42), // (0x0) SlotDebugData { name: "InstantiatedModule(ripple_counter: ripple_counter).ripple_counter::bit_reg_3.clk", ty: Clock }, + }, + 17: Copy { + dest: StatePartIndex(32), // (0x0) SlotDebugData { name: "InstantiatedModule(ripple_counter: ripple_counter).ripple_counter::bit_reg_1.o", ty: Bool }, + src: StatePartIndex(34), // (0x0) SlotDebugData { name: "InstantiatedModule(ripple_counter.bit_reg_1: sw_reg).sw_reg::o", ty: Bool }, + }, + // at: module-XXXXXXXXXX.rs:11:1 + 18: Copy { + dest: StatePartIndex(3), // (0x0) SlotDebugData { name: "InstantiatedModule(ripple_counter: ripple_counter).ripple_counter::bits[1]", ty: Bool }, + src: StatePartIndex(32), // (0x0) SlotDebugData { name: "InstantiatedModule(ripple_counter: ripple_counter).ripple_counter::bit_reg_1.o", ty: Bool }, + }, + // at: module-XXXXXXXXXX.rs:1:1 + 19: Copy { + dest: StatePartIndex(40), // (0x0) SlotDebugData { name: "", ty: Clock }, + src: StatePartIndex(3), // (0x0) SlotDebugData { name: "InstantiatedModule(ripple_counter: ripple_counter).ripple_counter::bits[1]", ty: Bool }, + }, + 20: NotU { + dest: StatePartIndex(30), // (0x1) SlotDebugData { name: "", ty: Bool }, + src: StatePartIndex(24), // (0x0) SlotDebugData { name: "InstantiatedModule(ripple_counter: ripple_counter).ripple_counter::bit_reg_0", ty: Bool }, + width: 1, + }, + // at: module-XXXXXXXXXX.rs:8:1 + 21: Copy { + dest: StatePartIndex(25), // (0x1) SlotDebugData { name: "InstantiatedModule(ripple_counter: ripple_counter).ripple_counter::bit_reg_0$next", ty: Bool }, + src: StatePartIndex(30), // (0x1) SlotDebugData { name: "", ty: Bool }, + }, + // at: module-XXXXXXXXXX.rs:7:1 + 22: Copy { + dest: StatePartIndex(2), // (0x0) SlotDebugData { name: "InstantiatedModule(ripple_counter: ripple_counter).ripple_counter::bits[0]", ty: Bool }, + src: StatePartIndex(24), // (0x0) SlotDebugData { name: "InstantiatedModule(ripple_counter: ripple_counter).ripple_counter::bit_reg_0", ty: Bool }, + }, + // at: module-XXXXXXXXXX.rs:1:1 + 23: Copy { + dest: StatePartIndex(35), // (0x0) SlotDebugData { name: "", ty: Clock }, + src: StatePartIndex(2), // (0x0) SlotDebugData { name: "InstantiatedModule(ripple_counter: ripple_counter).ripple_counter::bits[0]", ty: Bool }, + }, + // at: module-XXXXXXXXXX.rs:10:1 + 24: Copy { + dest: StatePartIndex(31), // (0x0) SlotDebugData { name: "InstantiatedModule(ripple_counter: ripple_counter).ripple_counter::bit_reg_1.clk", ty: Clock }, + src: StatePartIndex(35), // (0x0) SlotDebugData { name: "", ty: Clock }, + }, + // at: module-XXXXXXXXXX.rs:9:1 + 25: Copy { + dest: StatePartIndex(33), // (0x0) SlotDebugData { name: "InstantiatedModule(ripple_counter.bit_reg_1: sw_reg).sw_reg::clk", ty: Clock }, + src: StatePartIndex(31), // (0x0) SlotDebugData { name: "InstantiatedModule(ripple_counter: ripple_counter).ripple_counter::bit_reg_1.clk", ty: Clock }, + }, + // at: module-XXXXXXXXXX.rs:1:1 + 26: Const { + dest: StatePartIndex(28), // (0x0) SlotDebugData { name: "", ty: Bool }, + value: 0x0, + }, + 27: Copy { + dest: StatePartIndex(29), // (0x0) SlotDebugData { name: "", ty: SyncReset }, + src: StatePartIndex(28), // (0x0) SlotDebugData { name: "", ty: Bool }, + }, + 28: Copy { + dest: StatePartIndex(26), // (0x1) SlotDebugData { name: ".clk", ty: Clock }, + src: StatePartIndex(0), // (0x1) SlotDebugData { name: "InstantiatedModule(ripple_counter: ripple_counter).ripple_counter::clk", ty: Clock }, + }, + 29: Copy { + dest: StatePartIndex(27), // (0x0) SlotDebugData { name: ".rst", ty: SyncReset }, + src: StatePartIndex(29), // (0x0) SlotDebugData { name: "", ty: SyncReset }, + }, + // at: module-XXXXXXXXXX.rs:6:1 + 30: IsNonZeroDestIsSmall { + dest: StatePartIndex(2), // (0x1 1) SlotDebugData { name: "", ty: Bool }, + src: StatePartIndex(26), // (0x1) SlotDebugData { name: ".clk", ty: Clock }, + }, + 31: AndSmall { + dest: StatePartIndex(1), // (0x0 0) SlotDebugData { name: "", ty: Bool }, + lhs: StatePartIndex(2), // (0x1 1) SlotDebugData { name: "", ty: Bool }, + rhs: StatePartIndex(0), // (0x0 0) SlotDebugData { name: "", ty: Bool }, + }, + // at: module-XXXXXXXXXX.rs:1:1 + 32: Copy { + dest: StatePartIndex(38), // (0x0) SlotDebugData { name: ".clk", ty: Clock }, + src: StatePartIndex(40), // (0x0) SlotDebugData { name: "", ty: Clock }, + }, + 33: Copy { + dest: StatePartIndex(39), // (0x0) SlotDebugData { name: ".rst", ty: SyncReset }, + src: StatePartIndex(29), // (0x0) SlotDebugData { name: "", ty: SyncReset }, + }, + // at: module-XXXXXXXXXX.rs:6:1 + 34: IsNonZeroDestIsSmall { + dest: StatePartIndex(5), // (0x0 0) SlotDebugData { name: "", ty: Bool }, + src: StatePartIndex(38), // (0x0) SlotDebugData { name: ".clk", ty: Clock }, + }, + 35: AndSmall { + dest: StatePartIndex(4), // (0x0 0) SlotDebugData { name: "", ty: Bool }, + lhs: StatePartIndex(5), // (0x0 0) SlotDebugData { name: "", ty: Bool }, + rhs: StatePartIndex(3), // (0x1 1) SlotDebugData { name: "", ty: Bool }, + }, + // at: module-XXXXXXXXXX.rs:1:1 + 36: Copy { + dest: StatePartIndex(49), // (0x0) SlotDebugData { name: ".clk", ty: Clock }, + src: StatePartIndex(51), // (0x0) SlotDebugData { name: "", ty: Clock }, + }, + 37: Copy { + dest: StatePartIndex(50), // (0x0) SlotDebugData { name: ".rst", ty: SyncReset }, + src: StatePartIndex(29), // (0x0) SlotDebugData { name: "", ty: SyncReset }, + }, + // at: module-XXXXXXXXXX.rs:6:1 + 38: IsNonZeroDestIsSmall { + dest: StatePartIndex(8), // (0x0 0) SlotDebugData { name: "", ty: Bool }, + src: StatePartIndex(49), // (0x0) SlotDebugData { name: ".clk", ty: Clock }, + }, + 39: AndSmall { + dest: StatePartIndex(7), // (0x0 0) SlotDebugData { name: "", ty: Bool }, + lhs: StatePartIndex(8), // (0x0 0) SlotDebugData { name: "", ty: Bool }, + rhs: StatePartIndex(6), // (0x1 1) SlotDebugData { name: "", ty: Bool }, + }, + // at: module-XXXXXXXXXX.rs:1:1 + 40: Copy { + dest: StatePartIndex(21), // (0x0) SlotDebugData { name: "", ty: UInt<1> }, + src: StatePartIndex(7), // (0x0) SlotDebugData { name: "InstantiatedModule(ripple_counter: ripple_counter).ripple_counter::bits[5]", ty: Bool }, + }, + 41: Shl { + dest: StatePartIndex(22), // (0x0) SlotDebugData { name: "", ty: UInt<6> }, + lhs: StatePartIndex(21), // (0x0) SlotDebugData { name: "", ty: UInt<1> }, + rhs: 5, + }, + 42: Copy { + dest: StatePartIndex(18), // (0x0) SlotDebugData { name: "", ty: UInt<1> }, + src: StatePartIndex(6), // (0x0) SlotDebugData { name: "InstantiatedModule(ripple_counter: ripple_counter).ripple_counter::bits[4]", ty: Bool }, + }, + 43: Shl { + dest: StatePartIndex(19), // (0x0) SlotDebugData { name: "", ty: UInt<5> }, + lhs: StatePartIndex(18), // (0x0) SlotDebugData { name: "", ty: UInt<1> }, + rhs: 4, + }, + 44: Copy { + dest: StatePartIndex(15), // (0x0) SlotDebugData { name: "", ty: UInt<1> }, + src: StatePartIndex(5), // (0x0) SlotDebugData { name: "InstantiatedModule(ripple_counter: ripple_counter).ripple_counter::bits[3]", ty: Bool }, + }, + 45: Shl { + dest: StatePartIndex(16), // (0x0) SlotDebugData { name: "", ty: UInt<4> }, + lhs: StatePartIndex(15), // (0x0) SlotDebugData { name: "", ty: UInt<1> }, + rhs: 3, + }, + 46: Copy { + dest: StatePartIndex(12), // (0x0) SlotDebugData { name: "", ty: UInt<1> }, + src: StatePartIndex(4), // (0x0) SlotDebugData { name: "InstantiatedModule(ripple_counter: ripple_counter).ripple_counter::bits[2]", ty: Bool }, + }, + 47: Shl { + dest: StatePartIndex(13), // (0x0) SlotDebugData { name: "", ty: UInt<3> }, + lhs: StatePartIndex(12), // (0x0) SlotDebugData { name: "", ty: UInt<1> }, + rhs: 2, + }, + 48: Copy { + dest: StatePartIndex(9), // (0x0) SlotDebugData { name: "", ty: UInt<1> }, + src: StatePartIndex(3), // (0x0) SlotDebugData { name: "InstantiatedModule(ripple_counter: ripple_counter).ripple_counter::bits[1]", ty: Bool }, + }, + 49: Shl { + dest: StatePartIndex(10), // (0x0) SlotDebugData { name: "", ty: UInt<2> }, + lhs: StatePartIndex(9), // (0x0) SlotDebugData { name: "", ty: UInt<1> }, + rhs: 1, + }, + 50: Copy { + dest: StatePartIndex(8), // (0x0) SlotDebugData { name: "", ty: UInt<1> }, + src: StatePartIndex(2), // (0x0) SlotDebugData { name: "InstantiatedModule(ripple_counter: ripple_counter).ripple_counter::bits[0]", ty: Bool }, + }, + 51: Or { + dest: StatePartIndex(11), // (0x0) SlotDebugData { name: "", ty: UInt<2> }, + lhs: StatePartIndex(8), // (0x0) SlotDebugData { name: "", ty: UInt<1> }, + rhs: StatePartIndex(10), // (0x0) SlotDebugData { name: "", ty: UInt<2> }, + }, + 52: Or { + dest: StatePartIndex(14), // (0x0) SlotDebugData { name: "", ty: UInt<3> }, + lhs: StatePartIndex(11), // (0x0) SlotDebugData { name: "", ty: UInt<2> }, + rhs: StatePartIndex(13), // (0x0) SlotDebugData { name: "", ty: UInt<3> }, + }, + 53: Or { + dest: StatePartIndex(17), // (0x0) SlotDebugData { name: "", ty: UInt<4> }, + lhs: StatePartIndex(14), // (0x0) SlotDebugData { name: "", ty: UInt<3> }, + rhs: StatePartIndex(16), // (0x0) SlotDebugData { name: "", ty: UInt<4> }, + }, + 54: Or { + dest: StatePartIndex(20), // (0x0) SlotDebugData { name: "", ty: UInt<5> }, + lhs: StatePartIndex(17), // (0x0) SlotDebugData { name: "", ty: UInt<4> }, + rhs: StatePartIndex(19), // (0x0) SlotDebugData { name: "", ty: UInt<5> }, + }, + 55: Or { + dest: StatePartIndex(23), // (0x0) SlotDebugData { name: "", ty: UInt<6> }, + lhs: StatePartIndex(20), // (0x0) SlotDebugData { name: "", ty: UInt<5> }, + rhs: StatePartIndex(22), // (0x0) SlotDebugData { name: "", ty: UInt<6> }, + }, + // at: module-XXXXXXXXXX.rs:5:1 + 56: Copy { + dest: StatePartIndex(1), // (0x0) SlotDebugData { name: "InstantiatedModule(ripple_counter: ripple_counter).ripple_counter::o", ty: UInt<6> }, + src: StatePartIndex(23), // (0x0) SlotDebugData { name: "", ty: UInt<6> }, + }, + // at: module-XXXXXXXXXX.rs:6:1 + 57: BranchIfSmallZero { + target: 59, + value: StatePartIndex(1), // (0x0 0) SlotDebugData { name: "", ty: Bool }, + }, + 58: Copy { + dest: StatePartIndex(24), // (0x0) SlotDebugData { name: "InstantiatedModule(ripple_counter: ripple_counter).ripple_counter::bit_reg_0", ty: Bool }, + src: StatePartIndex(25), // (0x1) SlotDebugData { name: "InstantiatedModule(ripple_counter: ripple_counter).ripple_counter::bit_reg_0$next", ty: Bool }, + }, + 59: BranchIfSmallZero { + target: 61, + value: StatePartIndex(4), // (0x0 0) SlotDebugData { name: "", ty: Bool }, + }, + 60: Copy { + dest: StatePartIndex(36), // (0x0) SlotDebugData { name: "InstantiatedModule(ripple_counter: ripple_counter).ripple_counter::bit_reg_2", ty: Bool }, + src: StatePartIndex(37), // (0x1) SlotDebugData { name: "InstantiatedModule(ripple_counter: ripple_counter).ripple_counter::bit_reg_2$next", ty: Bool }, + }, + 61: BranchIfSmallZero { + target: 63, + value: StatePartIndex(7), // (0x0 0) SlotDebugData { name: "", ty: Bool }, + }, + 62: Copy { + dest: StatePartIndex(47), // (0x0) SlotDebugData { name: "InstantiatedModule(ripple_counter: ripple_counter).ripple_counter::bit_reg_4", ty: Bool }, + src: StatePartIndex(48), // (0x1) SlotDebugData { name: "InstantiatedModule(ripple_counter: ripple_counter).ripple_counter::bit_reg_4$next", ty: Bool }, + }, + 63: XorSmallImmediate { + dest: StatePartIndex(0), // (0x0 0) SlotDebugData { name: "", ty: Bool }, + lhs: StatePartIndex(2), // (0x1 1) SlotDebugData { name: "", ty: Bool }, + rhs: 0x1, + }, + 64: XorSmallImmediate { + dest: StatePartIndex(3), // (0x1 1) SlotDebugData { name: "", ty: Bool }, + lhs: StatePartIndex(5), // (0x0 0) SlotDebugData { name: "", ty: Bool }, + rhs: 0x1, + }, + 65: XorSmallImmediate { + dest: StatePartIndex(6), // (0x1 1) SlotDebugData { name: "", ty: Bool }, + lhs: StatePartIndex(8), // (0x0 0) SlotDebugData { name: "", ty: Bool }, + rhs: 0x1, + }, + // at: module-XXXXXXXXXX.rs:1:1 + 66: Return, + ], + .. + }, + pc: 66, + memory_write_log: [], + memories: StatePart { + value: [], + }, + small_slots: StatePart { + value: [ + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + ], + }, + big_slots: StatePart { + value: [ + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + ], + }, + }, + io: Instance { + name: ::ripple_counter, + instantiated: Module { + name: ripple_counter, + .. + }, + }, + main_module: SimulationModuleState { + base_targets: [ + Instance { + name: ::ripple_counter, + instantiated: Module { + name: ripple_counter, + .. + }, + }.clk, + Instance { + name: ::ripple_counter, + instantiated: Module { + name: ripple_counter, + .. + }, + }.o, + ], + uninitialized_ios: {}, + io_targets: { + Instance { + name: ::ripple_counter, + instantiated: Module { + name: ripple_counter, + .. + }, + }.clk, + Instance { + name: ::ripple_counter, + instantiated: Module { + name: ripple_counter, + .. + }, + }.o, + }, + did_initial_settle: true, + }, + extern_modules: [ + SimulationExternModuleState { + module_state: SimulationModuleState { + base_targets: [ + ModuleIO { + name: sw_reg::clk, + is_input: true, + ty: Clock, + .. + }, + ModuleIO { + name: sw_reg::o, + is_input: false, + ty: Bool, + .. + }, + ], + uninitialized_ios: {}, + io_targets: { + ModuleIO { + name: sw_reg::clk, + is_input: true, + ty: Clock, + .. + }, + ModuleIO { + name: sw_reg::o, + is_input: false, + ty: Bool, + .. + }, + }, + did_initial_settle: true, + }, + sim: ExternModuleSimulation { + generator: SimGeneratorFn { + args: ( + ModuleIO { + name: sw_reg::clk, + is_input: true, + ty: Clock, + .. + }, + ModuleIO { + name: sw_reg::o, + is_input: false, + ty: Bool, + .. + }, + ), + f: ..., + }, + source_location: SourceLocation( + module-XXXXXXXXXX-2.rs:4:1, + ), + }, + running_generator: Some( + ..., + ), + wait_targets: { + Change { + key: CompiledValue { + layout: CompiledTypeLayout { + ty: Clock, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(ripple_counter.bit_reg_1: sw_reg).sw_reg::clk", + ty: Clock, + }, + ], + .. + }, + }, + body: Scalar, + }, + range: TypeIndexRange { + small_slots: StatePartIndexRange { start: 3, len: 0 }, + big_slots: StatePartIndexRange { start: 33, len: 1 }, + }, + write: None, + }, + value: SimValue { + ty: Clock, + bits: 0x0, + }, + }, + }, + }, + SimulationExternModuleState { + module_state: SimulationModuleState { + base_targets: [ + ModuleIO { + name: sw_reg::clk, + is_input: true, + ty: Clock, + .. + }, + ModuleIO { + name: sw_reg::o, + is_input: false, + ty: Bool, + .. + }, + ], + uninitialized_ios: {}, + io_targets: { + ModuleIO { + name: sw_reg::clk, + is_input: true, + ty: Clock, + .. + }, + ModuleIO { + name: sw_reg::o, + is_input: false, + ty: Bool, + .. + }, + }, + did_initial_settle: true, + }, + sim: ExternModuleSimulation { + generator: SimGeneratorFn { + args: ( + ModuleIO { + name: sw_reg::clk, + is_input: true, + ty: Clock, + .. + }, + ModuleIO { + name: sw_reg::o, + is_input: false, + ty: Bool, + .. + }, + ), + f: ..., + }, + source_location: SourceLocation( + module-XXXXXXXXXX-2.rs:4:1, + ), + }, + running_generator: Some( + ..., + ), + wait_targets: { + Change { + key: CompiledValue { + layout: CompiledTypeLayout { + ty: Clock, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(ripple_counter.bit_reg_3: sw_reg).sw_reg::clk", + ty: Clock, + }, + ], + .. + }, + }, + body: Scalar, + }, + range: TypeIndexRange { + small_slots: StatePartIndexRange { start: 6, len: 0 }, + big_slots: StatePartIndexRange { start: 44, len: 1 }, + }, + write: None, + }, + value: SimValue { + ty: Clock, + bits: 0x0, + }, + }, + }, + }, + SimulationExternModuleState { + module_state: SimulationModuleState { + base_targets: [ + ModuleIO { + name: sw_reg::clk, + is_input: true, + ty: Clock, + .. + }, + ModuleIO { + name: sw_reg::o, + is_input: false, + ty: Bool, + .. + }, + ], + uninitialized_ios: {}, + io_targets: { + ModuleIO { + name: sw_reg::clk, + is_input: true, + ty: Clock, + .. + }, + ModuleIO { + name: sw_reg::o, + is_input: false, + ty: Bool, + .. + }, + }, + did_initial_settle: true, + }, + sim: ExternModuleSimulation { + generator: SimGeneratorFn { + args: ( + ModuleIO { + name: sw_reg::clk, + is_input: true, + ty: Clock, + .. + }, + ModuleIO { + name: sw_reg::o, + is_input: false, + ty: Bool, + .. + }, + ), + f: ..., + }, + source_location: SourceLocation( + module-XXXXXXXXXX-2.rs:4:1, + ), + }, + running_generator: Some( + ..., + ), + wait_targets: { + Change { + key: CompiledValue { + layout: CompiledTypeLayout { + ty: Clock, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(ripple_counter.bit_reg_5: sw_reg).sw_reg::clk", + ty: Clock, + }, + ], + .. + }, + }, + body: Scalar, + }, + range: TypeIndexRange { + small_slots: StatePartIndexRange { start: 9, len: 0 }, + big_slots: StatePartIndexRange { start: 55, len: 1 }, + }, + write: None, + }, + value: SimValue { + ty: Clock, + bits: 0x0, + }, + }, + }, + }, + ], + state_ready_to_run: false, + trace_decls: TraceModule { + name: "ripple_counter", + children: [ + TraceModuleIO { + name: "clk", + child: TraceClock { + location: TraceScalarId(0), + name: "clk", + flow: Source, + }, + ty: Clock, + flow: Source, + }, + TraceModuleIO { + name: "o", + child: TraceUInt { + location: TraceScalarId(1), + name: "o", + ty: UInt<6>, + flow: Sink, + }, + ty: UInt<6>, + flow: Sink, + }, + TraceWire { + name: "bits", + child: TraceArray { + name: "bits", + elements: [ + TraceBool { + location: TraceScalarId(2), + name: "[0]", + flow: Duplex, + }, + TraceBool { + location: TraceScalarId(3), + name: "[1]", + flow: Duplex, + }, + TraceBool { + location: TraceScalarId(4), + name: "[2]", + flow: Duplex, + }, + TraceBool { + location: TraceScalarId(5), + name: "[3]", + flow: Duplex, + }, + TraceBool { + location: TraceScalarId(6), + name: "[4]", + flow: Duplex, + }, + TraceBool { + location: TraceScalarId(7), + name: "[5]", + flow: Duplex, + }, + ], + ty: Array, + flow: Duplex, + }, + ty: Array, + }, + TraceReg { + name: "bit_reg_0", + child: TraceBool { + location: TraceScalarId(8), + name: "bit_reg_0", + flow: Duplex, + }, + ty: Bool, + }, + TraceInstance { + name: "bit_reg_1", + instance_io: TraceBundle { + name: "bit_reg_1", + fields: [ + TraceClock { + location: TraceScalarId(11), + name: "clk", + flow: Sink, + }, + TraceBool { + location: TraceScalarId(12), + name: "o", + flow: Source, + }, + ], + ty: Bundle { + #[hdl(flip)] /* offset = 0 */ + clk: Clock, + /* offset = 1 */ + o: Bool, + }, + flow: Source, + }, + module: TraceModule { + name: "sw_reg", + children: [ + TraceModuleIO { + name: "clk", + child: TraceClock { + location: TraceScalarId(9), + name: "clk", + flow: Source, + }, + ty: Clock, + flow: Source, + }, + TraceModuleIO { + name: "o", + child: TraceBool { + location: TraceScalarId(10), + name: "o", + flow: Sink, + }, + ty: Bool, + flow: Sink, + }, + ], + }, + ty: Bundle { + #[hdl(flip)] /* offset = 0 */ + clk: Clock, + /* offset = 1 */ + o: Bool, + }, + }, + TraceReg { + name: "bit_reg_2", + child: TraceBool { + location: TraceScalarId(13), + name: "bit_reg_2", + flow: Duplex, + }, + ty: Bool, + }, + TraceInstance { + name: "bit_reg_3", + instance_io: TraceBundle { + name: "bit_reg_3", + fields: [ + TraceClock { + location: TraceScalarId(16), + name: "clk", + flow: Sink, + }, + TraceBool { + location: TraceScalarId(17), + name: "o", + flow: Source, + }, + ], + ty: Bundle { + #[hdl(flip)] /* offset = 0 */ + clk: Clock, + /* offset = 1 */ + o: Bool, + }, + flow: Source, + }, + module: TraceModule { + name: "sw_reg", + children: [ + TraceModuleIO { + name: "clk", + child: TraceClock { + location: TraceScalarId(14), + name: "clk", + flow: Source, + }, + ty: Clock, + flow: Source, + }, + TraceModuleIO { + name: "o", + child: TraceBool { + location: TraceScalarId(15), + name: "o", + flow: Sink, + }, + ty: Bool, + flow: Sink, + }, + ], + }, + ty: Bundle { + #[hdl(flip)] /* offset = 0 */ + clk: Clock, + /* offset = 1 */ + o: Bool, + }, + }, + TraceReg { + name: "bit_reg_4", + child: TraceBool { + location: TraceScalarId(18), + name: "bit_reg_4", + flow: Duplex, + }, + ty: Bool, + }, + TraceInstance { + name: "bit_reg_5", + instance_io: TraceBundle { + name: "bit_reg_5", + fields: [ + TraceClock { + location: TraceScalarId(21), + name: "clk", + flow: Sink, + }, + TraceBool { + location: TraceScalarId(22), + name: "o", + flow: Source, + }, + ], + ty: Bundle { + #[hdl(flip)] /* offset = 0 */ + clk: Clock, + /* offset = 1 */ + o: Bool, + }, + flow: Source, + }, + module: TraceModule { + name: "sw_reg", + children: [ + TraceModuleIO { + name: "clk", + child: TraceClock { + location: TraceScalarId(19), + name: "clk", + flow: Source, + }, + ty: Clock, + flow: Source, + }, + TraceModuleIO { + name: "o", + child: TraceBool { + location: TraceScalarId(20), + name: "o", + flow: Sink, + }, + ty: Bool, + flow: Sink, + }, + ], + }, + ty: Bundle { + #[hdl(flip)] /* offset = 0 */ + clk: Clock, + /* offset = 1 */ + o: Bool, + }, + }, + ], + }, + traces: [ + SimTrace { + id: TraceScalarId(0), + kind: BigClock { + index: StatePartIndex(0), + }, + state: 0x1, + last_state: 0x1, + }, + SimTrace { + id: TraceScalarId(1), + kind: BigUInt { + index: StatePartIndex(1), + ty: UInt<6>, + }, + state: 0x00, + last_state: 0x00, + }, + SimTrace { + id: TraceScalarId(2), + kind: BigBool { + index: StatePartIndex(2), + }, + state: 0x0, + last_state: 0x0, + }, + SimTrace { + id: TraceScalarId(3), + kind: BigBool { + index: StatePartIndex(3), + }, + state: 0x0, + last_state: 0x0, + }, + SimTrace { + id: TraceScalarId(4), + kind: BigBool { + index: StatePartIndex(4), + }, + state: 0x0, + last_state: 0x0, + }, + SimTrace { + id: TraceScalarId(5), + kind: BigBool { + index: StatePartIndex(5), + }, + state: 0x0, + last_state: 0x0, + }, + SimTrace { + id: TraceScalarId(6), + kind: BigBool { + index: StatePartIndex(6), + }, + state: 0x0, + last_state: 0x0, + }, + SimTrace { + id: TraceScalarId(7), + kind: BigBool { + index: StatePartIndex(7), + }, + state: 0x0, + last_state: 0x0, + }, + SimTrace { + id: TraceScalarId(8), + kind: BigBool { + index: StatePartIndex(24), + }, + state: 0x0, + last_state: 0x0, + }, + SimTrace { + id: TraceScalarId(9), + kind: BigClock { + index: StatePartIndex(33), + }, + state: 0x0, + last_state: 0x0, + }, + SimTrace { + id: TraceScalarId(10), + kind: BigBool { + index: StatePartIndex(34), + }, + state: 0x0, + last_state: 0x0, + }, + SimTrace { + id: TraceScalarId(11), + kind: BigClock { + index: StatePartIndex(31), + }, + state: 0x0, + last_state: 0x0, + }, + SimTrace { + id: TraceScalarId(12), + kind: BigBool { + index: StatePartIndex(32), + }, + state: 0x0, + last_state: 0x0, + }, + SimTrace { + id: TraceScalarId(13), + kind: BigBool { + index: StatePartIndex(36), + }, + state: 0x0, + last_state: 0x0, + }, + SimTrace { + id: TraceScalarId(14), + kind: BigClock { + index: StatePartIndex(44), + }, + state: 0x0, + last_state: 0x0, + }, + SimTrace { + id: TraceScalarId(15), + kind: BigBool { + index: StatePartIndex(45), + }, + state: 0x0, + last_state: 0x0, + }, + SimTrace { + id: TraceScalarId(16), + kind: BigClock { + index: StatePartIndex(42), + }, + state: 0x0, + last_state: 0x0, + }, + SimTrace { + id: TraceScalarId(17), + kind: BigBool { + index: StatePartIndex(43), + }, + state: 0x0, + last_state: 0x0, + }, + SimTrace { + id: TraceScalarId(18), + kind: BigBool { + index: StatePartIndex(47), + }, + state: 0x0, + last_state: 0x0, + }, + SimTrace { + id: TraceScalarId(19), + kind: BigClock { + index: StatePartIndex(55), + }, + state: 0x0, + last_state: 0x0, + }, + SimTrace { + id: TraceScalarId(20), + kind: BigBool { + index: StatePartIndex(56), + }, + state: 0x0, + last_state: 0x0, + }, + SimTrace { + id: TraceScalarId(21), + kind: BigClock { + index: StatePartIndex(53), + }, + state: 0x0, + last_state: 0x0, + }, + SimTrace { + id: TraceScalarId(22), + kind: BigBool { + index: StatePartIndex(54), + }, + state: 0x0, + last_state: 0x0, + }, + ], + trace_memories: {}, + trace_writers: [ + Running( + VcdWriter { + finished_init: true, + timescale: 1 ps, + .. + }, + ), + ], + instant: 256 μs, + clocks_triggered: [ + StatePartIndex(1), + StatePartIndex(4), + StatePartIndex(7), + ], + .. +} \ No newline at end of file diff --git a/crates/fayalite/tests/sim/expected/ripple_counter.vcd b/crates/fayalite/tests/sim/expected/ripple_counter.vcd new file mode 100644 index 0000000..6f14a8e --- /dev/null +++ b/crates/fayalite/tests/sim/expected/ripple_counter.vcd @@ -0,0 +1,1753 @@ +$timescale 1 ps $end +$scope module ripple_counter $end +$var wire 1 ! clk $end +$var wire 6 " o $end +$scope struct bits $end +$var wire 1 # \[0] $end +$var wire 1 $ \[1] $end +$var wire 1 % \[2] $end +$var wire 1 & \[3] $end +$var wire 1 ' \[4] $end +$var wire 1 ( \[5] $end +$upscope $end +$var reg 1 ) bit_reg_0 $end +$scope struct bit_reg_1 $end +$var wire 1 , clk $end +$var wire 1 - o $end +$upscope $end +$scope module sw_reg $end +$var wire 1 * clk $end +$var wire 1 + o $end +$upscope $end +$var reg 1 . bit_reg_2 $end +$scope struct bit_reg_3 $end +$var wire 1 1 clk $end +$var wire 1 2 o $end +$upscope $end +$scope module sw_reg_2 $end +$var wire 1 / clk $end +$var wire 1 0 o $end +$upscope $end +$var reg 1 3 bit_reg_4 $end +$scope struct bit_reg_5 $end +$var wire 1 6 clk $end +$var wire 1 7 o $end +$upscope $end +$scope module sw_reg_3 $end +$var wire 1 4 clk $end +$var wire 1 5 o $end +$upscope $end +$upscope $end +$enddefinitions $end +$dumpvars +0! +b0 " +0# +0$ +0% +0& +0' +0( +0) +0* +0+ +0, +0- +0. +0/ +00 +01 +02 +03 +04 +05 +06 +07 +$end +#1000000 +1! +1) +b1 " +1# +1* +1, +1+ +b11 " +1$ +1- +1. +b111 " +1% +1/ +11 +10 +b1111 " +1& +12 +13 +b11111 " +1' +14 +16 +15 +b111111 " +1( +17 +#2000000 +0! +#3000000 +1! +0) +b111110 " +0# +0* +0, +#4000000 +0! +#5000000 +1! +1) +b111111 " +1# +1* +1, +0+ +b111101 " +0$ +0- +#6000000 +0! +#7000000 +1! +0) +b111100 " +0# +0* +0, +#8000000 +0! +#9000000 +1! +1) +b111101 " +1# +1* +1, +1+ +b111111 " +1$ +1- +0. +b111011 " +0% +0/ +01 +#10000000 +0! +#11000000 +1! +0) +b111010 " +0# +0* +0, +#12000000 +0! +#13000000 +1! +1) +b111011 " +1# +1* +1, +0+ +b111001 " +0$ +0- +#14000000 +0! +#15000000 +1! +0) +b111000 " +0# +0* +0, +#16000000 +0! +#17000000 +1! +1) +b111001 " +1# +1* +1, +1+ +b111011 " +1$ +1- +1. +b111111 " +1% +1/ +11 +00 +b110111 " +0& +02 +#18000000 +0! +#19000000 +1! +0) +b110110 " +0# +0* +0, +#20000000 +0! +#21000000 +1! +1) +b110111 " +1# +1* +1, +0+ +b110101 " +0$ +0- +#22000000 +0! +#23000000 +1! +0) +b110100 " +0# +0* +0, +#24000000 +0! +#25000000 +1! +1) +b110101 " +1# +1* +1, +1+ +b110111 " +1$ +1- +0. +b110011 " +0% +0/ +01 +#26000000 +0! +#27000000 +1! +0) +b110010 " +0# +0* +0, +#28000000 +0! +#29000000 +1! +1) +b110011 " +1# +1* +1, +0+ +b110001 " +0$ +0- +#30000000 +0! +#31000000 +1! +0) +b110000 " +0# +0* +0, +#32000000 +0! +#33000000 +1! +1) +b110001 " +1# +1* +1, +1+ +b110011 " +1$ +1- +1. +b110111 " +1% +1/ +11 +10 +b111111 " +1& +12 +03 +b101111 " +0' +04 +06 +#34000000 +0! +#35000000 +1! +0) +b101110 " +0# +0* +0, +#36000000 +0! +#37000000 +1! +1) +b101111 " +1# +1* +1, +0+ +b101101 " +0$ +0- +#38000000 +0! +#39000000 +1! +0) +b101100 " +0# +0* +0, +#40000000 +0! +#41000000 +1! +1) +b101101 " +1# +1* +1, +1+ +b101111 " +1$ +1- +0. +b101011 " +0% +0/ +01 +#42000000 +0! +#43000000 +1! +0) +b101010 " +0# +0* +0, +#44000000 +0! +#45000000 +1! +1) +b101011 " +1# +1* +1, +0+ +b101001 " +0$ +0- +#46000000 +0! +#47000000 +1! +0) +b101000 " +0# +0* +0, +#48000000 +0! +#49000000 +1! +1) +b101001 " +1# +1* +1, +1+ +b101011 " +1$ +1- +1. +b101111 " +1% +1/ +11 +00 +b100111 " +0& +02 +#50000000 +0! +#51000000 +1! +0) +b100110 " +0# +0* +0, +#52000000 +0! +#53000000 +1! +1) +b100111 " +1# +1* +1, +0+ +b100101 " +0$ +0- +#54000000 +0! +#55000000 +1! +0) +b100100 " +0# +0* +0, +#56000000 +0! +#57000000 +1! +1) +b100101 " +1# +1* +1, +1+ +b100111 " +1$ +1- +0. +b100011 " +0% +0/ +01 +#58000000 +0! +#59000000 +1! +0) +b100010 " +0# +0* +0, +#60000000 +0! +#61000000 +1! +1) +b100011 " +1# +1* +1, +0+ +b100001 " +0$ +0- +#62000000 +0! +#63000000 +1! +0) +b100000 " +0# +0* +0, +#64000000 +0! +#65000000 +1! +1) +b100001 " +1# +1* +1, +1+ +b100011 " +1$ +1- +1. +b100111 " +1% +1/ +11 +10 +b101111 " +1& +12 +13 +b111111 " +1' +14 +16 +05 +b11111 " +0( +07 +#66000000 +0! +#67000000 +1! +0) +b11110 " +0# +0* +0, +#68000000 +0! +#69000000 +1! +1) +b11111 " +1# +1* +1, +0+ +b11101 " +0$ +0- +#70000000 +0! +#71000000 +1! +0) +b11100 " +0# +0* +0, +#72000000 +0! +#73000000 +1! +1) +b11101 " +1# +1* +1, +1+ +b11111 " +1$ +1- +0. +b11011 " +0% +0/ +01 +#74000000 +0! +#75000000 +1! +0) +b11010 " +0# +0* +0, +#76000000 +0! +#77000000 +1! +1) +b11011 " +1# +1* +1, +0+ +b11001 " +0$ +0- +#78000000 +0! +#79000000 +1! +0) +b11000 " +0# +0* +0, +#80000000 +0! +#81000000 +1! +1) +b11001 " +1# +1* +1, +1+ +b11011 " +1$ +1- +1. +b11111 " +1% +1/ +11 +00 +b10111 " +0& +02 +#82000000 +0! +#83000000 +1! +0) +b10110 " +0# +0* +0, +#84000000 +0! +#85000000 +1! +1) +b10111 " +1# +1* +1, +0+ +b10101 " +0$ +0- +#86000000 +0! +#87000000 +1! +0) +b10100 " +0# +0* +0, +#88000000 +0! +#89000000 +1! +1) +b10101 " +1# +1* +1, +1+ +b10111 " +1$ +1- +0. +b10011 " +0% +0/ +01 +#90000000 +0! +#91000000 +1! +0) +b10010 " +0# +0* +0, +#92000000 +0! +#93000000 +1! +1) +b10011 " +1# +1* +1, +0+ +b10001 " +0$ +0- +#94000000 +0! +#95000000 +1! +0) +b10000 " +0# +0* +0, +#96000000 +0! +#97000000 +1! +1) +b10001 " +1# +1* +1, +1+ +b10011 " +1$ +1- +1. +b10111 " +1% +1/ +11 +10 +b11111 " +1& +12 +03 +b1111 " +0' +04 +06 +#98000000 +0! +#99000000 +1! +0) +b1110 " +0# +0* +0, +#100000000 +0! +#101000000 +1! +1) +b1111 " +1# +1* +1, +0+ +b1101 " +0$ +0- +#102000000 +0! +#103000000 +1! +0) +b1100 " +0# +0* +0, +#104000000 +0! +#105000000 +1! +1) +b1101 " +1# +1* +1, +1+ +b1111 " +1$ +1- +0. +b1011 " +0% +0/ +01 +#106000000 +0! +#107000000 +1! +0) +b1010 " +0# +0* +0, +#108000000 +0! +#109000000 +1! +1) +b1011 " +1# +1* +1, +0+ +b1001 " +0$ +0- +#110000000 +0! +#111000000 +1! +0) +b1000 " +0# +0* +0, +#112000000 +0! +#113000000 +1! +1) +b1001 " +1# +1* +1, +1+ +b1011 " +1$ +1- +1. +b1111 " +1% +1/ +11 +00 +b111 " +0& +02 +#114000000 +0! +#115000000 +1! +0) +b110 " +0# +0* +0, +#116000000 +0! +#117000000 +1! +1) +b111 " +1# +1* +1, +0+ +b101 " +0$ +0- +#118000000 +0! +#119000000 +1! +0) +b100 " +0# +0* +0, +#120000000 +0! +#121000000 +1! +1) +b101 " +1# +1* +1, +1+ +b111 " +1$ +1- +0. +b11 " +0% +0/ +01 +#122000000 +0! +#123000000 +1! +0) +b10 " +0# +0* +0, +#124000000 +0! +#125000000 +1! +1) +b11 " +1# +1* +1, +0+ +b1 " +0$ +0- +#126000000 +0! +#127000000 +1! +0) +b0 " +0# +0* +0, +#128000000 +0! +#129000000 +1! +1) +b1 " +1# +1* +1, +1+ +b11 " +1$ +1- +1. +b111 " +1% +1/ +11 +10 +b1111 " +1& +12 +13 +b11111 " +1' +14 +16 +15 +b111111 " +1( +17 +#130000000 +0! +#131000000 +1! +0) +b111110 " +0# +0* +0, +#132000000 +0! +#133000000 +1! +1) +b111111 " +1# +1* +1, +0+ +b111101 " +0$ +0- +#134000000 +0! +#135000000 +1! +0) +b111100 " +0# +0* +0, +#136000000 +0! +#137000000 +1! +1) +b111101 " +1# +1* +1, +1+ +b111111 " +1$ +1- +0. +b111011 " +0% +0/ +01 +#138000000 +0! +#139000000 +1! +0) +b111010 " +0# +0* +0, +#140000000 +0! +#141000000 +1! +1) +b111011 " +1# +1* +1, +0+ +b111001 " +0$ +0- +#142000000 +0! +#143000000 +1! +0) +b111000 " +0# +0* +0, +#144000000 +0! +#145000000 +1! +1) +b111001 " +1# +1* +1, +1+ +b111011 " +1$ +1- +1. +b111111 " +1% +1/ +11 +00 +b110111 " +0& +02 +#146000000 +0! +#147000000 +1! +0) +b110110 " +0# +0* +0, +#148000000 +0! +#149000000 +1! +1) +b110111 " +1# +1* +1, +0+ +b110101 " +0$ +0- +#150000000 +0! +#151000000 +1! +0) +b110100 " +0# +0* +0, +#152000000 +0! +#153000000 +1! +1) +b110101 " +1# +1* +1, +1+ +b110111 " +1$ +1- +0. +b110011 " +0% +0/ +01 +#154000000 +0! +#155000000 +1! +0) +b110010 " +0# +0* +0, +#156000000 +0! +#157000000 +1! +1) +b110011 " +1# +1* +1, +0+ +b110001 " +0$ +0- +#158000000 +0! +#159000000 +1! +0) +b110000 " +0# +0* +0, +#160000000 +0! +#161000000 +1! +1) +b110001 " +1# +1* +1, +1+ +b110011 " +1$ +1- +1. +b110111 " +1% +1/ +11 +10 +b111111 " +1& +12 +03 +b101111 " +0' +04 +06 +#162000000 +0! +#163000000 +1! +0) +b101110 " +0# +0* +0, +#164000000 +0! +#165000000 +1! +1) +b101111 " +1# +1* +1, +0+ +b101101 " +0$ +0- +#166000000 +0! +#167000000 +1! +0) +b101100 " +0# +0* +0, +#168000000 +0! +#169000000 +1! +1) +b101101 " +1# +1* +1, +1+ +b101111 " +1$ +1- +0. +b101011 " +0% +0/ +01 +#170000000 +0! +#171000000 +1! +0) +b101010 " +0# +0* +0, +#172000000 +0! +#173000000 +1! +1) +b101011 " +1# +1* +1, +0+ +b101001 " +0$ +0- +#174000000 +0! +#175000000 +1! +0) +b101000 " +0# +0* +0, +#176000000 +0! +#177000000 +1! +1) +b101001 " +1# +1* +1, +1+ +b101011 " +1$ +1- +1. +b101111 " +1% +1/ +11 +00 +b100111 " +0& +02 +#178000000 +0! +#179000000 +1! +0) +b100110 " +0# +0* +0, +#180000000 +0! +#181000000 +1! +1) +b100111 " +1# +1* +1, +0+ +b100101 " +0$ +0- +#182000000 +0! +#183000000 +1! +0) +b100100 " +0# +0* +0, +#184000000 +0! +#185000000 +1! +1) +b100101 " +1# +1* +1, +1+ +b100111 " +1$ +1- +0. +b100011 " +0% +0/ +01 +#186000000 +0! +#187000000 +1! +0) +b100010 " +0# +0* +0, +#188000000 +0! +#189000000 +1! +1) +b100011 " +1# +1* +1, +0+ +b100001 " +0$ +0- +#190000000 +0! +#191000000 +1! +0) +b100000 " +0# +0* +0, +#192000000 +0! +#193000000 +1! +1) +b100001 " +1# +1* +1, +1+ +b100011 " +1$ +1- +1. +b100111 " +1% +1/ +11 +10 +b101111 " +1& +12 +13 +b111111 " +1' +14 +16 +05 +b11111 " +0( +07 +#194000000 +0! +#195000000 +1! +0) +b11110 " +0# +0* +0, +#196000000 +0! +#197000000 +1! +1) +b11111 " +1# +1* +1, +0+ +b11101 " +0$ +0- +#198000000 +0! +#199000000 +1! +0) +b11100 " +0# +0* +0, +#200000000 +0! +#201000000 +1! +1) +b11101 " +1# +1* +1, +1+ +b11111 " +1$ +1- +0. +b11011 " +0% +0/ +01 +#202000000 +0! +#203000000 +1! +0) +b11010 " +0# +0* +0, +#204000000 +0! +#205000000 +1! +1) +b11011 " +1# +1* +1, +0+ +b11001 " +0$ +0- +#206000000 +0! +#207000000 +1! +0) +b11000 " +0# +0* +0, +#208000000 +0! +#209000000 +1! +1) +b11001 " +1# +1* +1, +1+ +b11011 " +1$ +1- +1. +b11111 " +1% +1/ +11 +00 +b10111 " +0& +02 +#210000000 +0! +#211000000 +1! +0) +b10110 " +0# +0* +0, +#212000000 +0! +#213000000 +1! +1) +b10111 " +1# +1* +1, +0+ +b10101 " +0$ +0- +#214000000 +0! +#215000000 +1! +0) +b10100 " +0# +0* +0, +#216000000 +0! +#217000000 +1! +1) +b10101 " +1# +1* +1, +1+ +b10111 " +1$ +1- +0. +b10011 " +0% +0/ +01 +#218000000 +0! +#219000000 +1! +0) +b10010 " +0# +0* +0, +#220000000 +0! +#221000000 +1! +1) +b10011 " +1# +1* +1, +0+ +b10001 " +0$ +0- +#222000000 +0! +#223000000 +1! +0) +b10000 " +0# +0* +0, +#224000000 +0! +#225000000 +1! +1) +b10001 " +1# +1* +1, +1+ +b10011 " +1$ +1- +1. +b10111 " +1% +1/ +11 +10 +b11111 " +1& +12 +03 +b1111 " +0' +04 +06 +#226000000 +0! +#227000000 +1! +0) +b1110 " +0# +0* +0, +#228000000 +0! +#229000000 +1! +1) +b1111 " +1# +1* +1, +0+ +b1101 " +0$ +0- +#230000000 +0! +#231000000 +1! +0) +b1100 " +0# +0* +0, +#232000000 +0! +#233000000 +1! +1) +b1101 " +1# +1* +1, +1+ +b1111 " +1$ +1- +0. +b1011 " +0% +0/ +01 +#234000000 +0! +#235000000 +1! +0) +b1010 " +0# +0* +0, +#236000000 +0! +#237000000 +1! +1) +b1011 " +1# +1* +1, +0+ +b1001 " +0$ +0- +#238000000 +0! +#239000000 +1! +0) +b1000 " +0# +0* +0, +#240000000 +0! +#241000000 +1! +1) +b1001 " +1# +1* +1, +1+ +b1011 " +1$ +1- +1. +b1111 " +1% +1/ +11 +00 +b111 " +0& +02 +#242000000 +0! +#243000000 +1! +0) +b110 " +0# +0* +0, +#244000000 +0! +#245000000 +1! +1) +b111 " +1# +1* +1, +0+ +b101 " +0$ +0- +#246000000 +0! +#247000000 +1! +0) +b100 " +0# +0* +0, +#248000000 +0! +#249000000 +1! +1) +b101 " +1# +1* +1, +1+ +b111 " +1$ +1- +0. +b11 " +0% +0/ +01 +#250000000 +0! +#251000000 +1! +0) +b10 " +0# +0* +0, +#252000000 +0! +#253000000 +1! +1) +b11 " +1# +1* +1, +0+ +b1 " +0$ +0- +#254000000 +0! +#255000000 +1! +0) +b0 " +0# +0* +0, +#256000000 From ec3a61513ba0b3a4b8db7026bac7bc6beb0a1eaa Mon Sep 17 00:00:00 2001 From: Jacob Lifshay Date: Thu, 27 Mar 2025 23:03:44 -0700 Subject: [PATCH 16/38] simulator read/write types must be passive --- crates/fayalite/src/sim.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/crates/fayalite/src/sim.rs b/crates/fayalite/src/sim.rs index 275b106..b508756 100644 --- a/crates/fayalite/src/sim.rs +++ b/crates/fayalite/src/sim.rs @@ -6698,6 +6698,11 @@ impl SimulationModuleState { mut target: Target, which_module: WhichModule, ) -> CompiledValue { + assert!( + target.canonical_ty().is_passive(), + "simulator read/write expression must have a passive type \ + (recursively contains no fields with `#[hdl(flip)]`)" + ); if let Some(&retval) = self.io_targets.get(&target) { return retval; } From e0f978fbb6b9ce19876489d0bbe5fcdae57175a3 Mon Sep 17 00:00:00 2001 From: Jacob Lifshay Date: Thu, 27 Mar 2025 23:17:28 -0700 Subject: [PATCH 17/38] silence unused `m` variable warning in #[hdl_module] with an empty body. --- crates/fayalite-proc-macros-impl/src/module.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/crates/fayalite-proc-macros-impl/src/module.rs b/crates/fayalite-proc-macros-impl/src/module.rs index 0852f58..62b7837 100644 --- a/crates/fayalite-proc-macros-impl/src/module.rs +++ b/crates/fayalite-proc-macros-impl/src/module.rs @@ -377,7 +377,7 @@ impl ModuleFn { module_kind, vis, sig, - block, + mut block, struct_generics, the_struct, } = match self.0 { @@ -439,6 +439,12 @@ impl ModuleFn { body_sig .inputs .insert(0, parse_quote! { m: &::fayalite::module::ModuleBuilder }); + block.stmts.insert( + 0, + parse_quote! { + let _ = m; + }, + ); let body_fn = ItemFn { attrs: vec![], vis: Visibility::Inherited, From 5028401a5adad671f31ab754a958a692debdb062 Mon Sep 17 00:00:00 2001 From: Jacob Lifshay Date: Thu, 27 Mar 2025 23:44:36 -0700 Subject: [PATCH 18/38] change SimValue to contain and deref to a value and not just contain bits --- .../src/hdl_bundle.rs | 171 ++++- .../fayalite-proc-macros-impl/src/hdl_enum.rs | 269 +++++++ crates/fayalite/src/array.rs | 55 ++ crates/fayalite/src/bundle.rs | 191 ++++- crates/fayalite/src/clock.rs | 17 + crates/fayalite/src/enum_.rs | 327 ++++++++- crates/fayalite/src/expr.rs | 1 + crates/fayalite/src/int.rs | 68 +- crates/fayalite/src/lib.rs | 2 + crates/fayalite/src/phantom_const.rs | 23 + crates/fayalite/src/reset.rs | 17 + crates/fayalite/src/sim.rs | 674 +----------------- crates/fayalite/src/sim/value.rs | 665 +++++++++++++++++ crates/fayalite/src/ty.rs | 52 +- crates/fayalite/src/util.rs | 1 + crates/fayalite/src/util/alternating_cell.rs | 122 ++++ crates/fayalite/tests/sim.rs | 214 +++--- .../tests/sim/expected/extern_module2.txt | 4 +- .../tests/sim/expected/ripple_counter.txt | 12 +- 19 files changed, 2065 insertions(+), 820 deletions(-) create mode 100644 crates/fayalite/src/sim/value.rs create mode 100644 crates/fayalite/src/util/alternating_cell.rs diff --git a/crates/fayalite-proc-macros-impl/src/hdl_bundle.rs b/crates/fayalite-proc-macros-impl/src/hdl_bundle.rs index b0fe498..a083def 100644 --- a/crates/fayalite-proc-macros-impl/src/hdl_bundle.rs +++ b/crates/fayalite-proc-macros-impl/src/hdl_bundle.rs @@ -30,7 +30,9 @@ pub(crate) struct ParsedBundle { pub(crate) field_flips: Vec>>, pub(crate) mask_type_ident: Ident, pub(crate) mask_type_match_variant_ident: Ident, + pub(crate) mask_type_sim_value_ident: Ident, pub(crate) match_variant_ident: Ident, + pub(crate) sim_value_ident: Ident, pub(crate) builder_ident: Ident, pub(crate) mask_type_builder_ident: Ident, } @@ -125,7 +127,9 @@ impl ParsedBundle { field_flips, mask_type_ident: format_ident!("__{}__MaskType", ident), mask_type_match_variant_ident: format_ident!("__{}__MaskType__MatchVariant", ident), + mask_type_sim_value_ident: format_ident!("__{}__MaskType__SimValue", ident), match_variant_ident: format_ident!("__{}__MatchVariant", ident), + sim_value_ident: format_ident!("__{}__SimValue", ident), mask_type_builder_ident: format_ident!("__{}__MaskType__Builder", ident), builder_ident: format_ident!("__{}__Builder", ident), ident, @@ -427,7 +431,9 @@ impl ToTokens for ParsedBundle { field_flips, mask_type_ident, mask_type_match_variant_ident, + mask_type_sim_value_ident, match_variant_ident, + sim_value_ident, builder_ident, mask_type_builder_ident, } = self; @@ -523,7 +529,7 @@ impl ToTokens for ParsedBundle { semi_token: None, } .to_tokens(tokens); - let mut mask_type_match_variant_fields = mask_type_fields; + let mut mask_type_match_variant_fields = mask_type_fields.clone(); for Field { ty, .. } in &mut mask_type_match_variant_fields.named { *ty = parse_quote_spanned! {span=> ::fayalite::expr::Expr<#ty> @@ -565,6 +571,58 @@ impl ToTokens for ParsedBundle { semi_token: None, } .to_tokens(tokens); + let mut mask_type_sim_value_fields = mask_type_fields; + for Field { ty, .. } in &mut mask_type_sim_value_fields.named { + *ty = parse_quote_spanned! {span=> + ::fayalite::sim::value::SimValue<#ty> + }; + } + ItemStruct { + attrs: vec![ + parse_quote_spanned! {span=> + #[::fayalite::__std::prelude::v1::derive( + ::fayalite::__std::fmt::Debug, + ::fayalite::__std::clone::Clone, + )] + }, + parse_quote_spanned! {span=> + #[allow(non_camel_case_types, dead_code)] + }, + ], + vis: vis.clone(), + struct_token: *struct_token, + ident: mask_type_sim_value_ident.clone(), + generics: generics.into(), + fields: Fields::Named(mask_type_sim_value_fields), + semi_token: None, + } + .to_tokens(tokens); + let mut sim_value_fields = FieldsNamed::from(fields.clone()); + for Field { ty, .. } in &mut sim_value_fields.named { + *ty = parse_quote_spanned! {span=> + ::fayalite::sim::value::SimValue<#ty> + }; + } + ItemStruct { + attrs: vec![ + parse_quote_spanned! {span=> + #[::fayalite::__std::prelude::v1::derive( + ::fayalite::__std::fmt::Debug, + ::fayalite::__std::clone::Clone, + )] + }, + parse_quote_spanned! {span=> + #[allow(non_camel_case_types, dead_code)] + }, + ], + vis: vis.clone(), + struct_token: *struct_token, + ident: sim_value_ident.clone(), + generics: generics.into(), + fields: Fields::Named(sim_value_fields), + semi_token: None, + } + .to_tokens(tokens); let this_token = Ident::new("__this", span); let fields_token = Ident::new("__fields", span); let self_token = Token![self](span); @@ -615,6 +673,25 @@ impl ToTokens for ParsedBundle { } }, )); + let sim_value_from_bits_fields = Vec::from_iter(fields.named().into_iter().map(|field| { + let ident: &Ident = field.ident().as_ref().unwrap(); + quote_spanned! {span=> + #ident: v.field_from_bits(), + } + })); + let sim_value_clone_from_bits_fields = + Vec::from_iter(fields.named().into_iter().map(|field| { + let ident: &Ident = field.ident().as_ref().unwrap(); + quote_spanned! {span=> + v.field_clone_from_bits(&mut value.#ident); + } + })); + let sim_value_to_bits_fields = Vec::from_iter(fields.named().into_iter().map(|field| { + let ident: &Ident = field.ident().as_ref().unwrap(); + quote_spanned! {span=> + v.field_to_bits(&value.#ident); + } + })); let fields_len = fields.named().into_iter().len(); quote_spanned! {span=> #[automatically_derived] @@ -623,6 +700,7 @@ impl ToTokens for ParsedBundle { { type BaseType = ::fayalite::bundle::Bundle; type MaskType = #mask_type_ident #type_generics; + type SimValue = #mask_type_sim_value_ident #type_generics; type MatchVariant = #mask_type_match_variant_ident #type_generics; type MatchActiveScope = (); type MatchVariantAndInactiveScope = ::fayalite::ty::MatchVariantWithoutScope< @@ -660,6 +738,34 @@ impl ToTokens for ParsedBundle { fn source_location() -> ::fayalite::source_location::SourceLocation { ::fayalite::source_location::SourceLocation::caller() } + fn sim_value_from_bits( + &self, + bits: &::fayalite::__bitvec::slice::BitSlice, + ) -> ::SimValue { + #![allow(unused_mut, unused_variables)] + let mut v = ::fayalite::bundle::BundleSimValueFromBits::new(*self, bits); + #mask_type_sim_value_ident { + #(#sim_value_from_bits_fields)* + } + } + fn sim_value_clone_from_bits( + &self, + value: &mut ::SimValue, + bits: &::fayalite::__bitvec::slice::BitSlice, + ) { + #![allow(unused_mut, unused_variables)] + let mut v = ::fayalite::bundle::BundleSimValueFromBits::new(*self, bits); + #(#sim_value_clone_from_bits_fields)* + } + fn sim_value_to_bits( + &self, + value: &::SimValue, + bits: &mut ::fayalite::__bitvec::slice::BitSlice, + ) { + #![allow(unused_mut, unused_variables)] + let mut v = ::fayalite::bundle::BundleSimValueToBits::new(*self, bits); + #(#sim_value_to_bits_fields)* + } } #[automatically_derived] impl #impl_generics ::fayalite::bundle::BundleType for #mask_type_ident #type_generics @@ -696,6 +802,7 @@ impl ToTokens for ParsedBundle { { type BaseType = ::fayalite::bundle::Bundle; type MaskType = #mask_type_ident #type_generics; + type SimValue = #sim_value_ident #type_generics; type MatchVariant = #match_variant_ident #type_generics; type MatchActiveScope = (); type MatchVariantAndInactiveScope = ::fayalite::ty::MatchVariantWithoutScope< @@ -735,6 +842,34 @@ impl ToTokens for ParsedBundle { fn source_location() -> ::fayalite::source_location::SourceLocation { ::fayalite::source_location::SourceLocation::caller() } + fn sim_value_from_bits( + &self, + bits: &::fayalite::__bitvec::slice::BitSlice, + ) -> ::SimValue { + #![allow(unused_mut, unused_variables)] + let mut v = ::fayalite::bundle::BundleSimValueFromBits::new(*self, bits); + #sim_value_ident { + #(#sim_value_from_bits_fields)* + } + } + fn sim_value_clone_from_bits( + &self, + value: &mut ::SimValue, + bits: &::fayalite::__bitvec::slice::BitSlice, + ) { + #![allow(unused_mut, unused_variables)] + let mut v = ::fayalite::bundle::BundleSimValueFromBits::new(*self, bits); + #(#sim_value_clone_from_bits_fields)* + } + fn sim_value_to_bits( + &self, + value: &::SimValue, + bits: &mut ::fayalite::__bitvec::slice::BitSlice, + ) { + #![allow(unused_mut, unused_variables)] + let mut v = ::fayalite::bundle::BundleSimValueToBits::new(*self, bits); + #(#sim_value_to_bits_fields)* + } } #[automatically_derived] impl #impl_generics ::fayalite::bundle::BundleType for #target #type_generics @@ -768,23 +903,33 @@ impl ToTokens for ParsedBundle { } .to_tokens(tokens); if let Some((cmp_eq,)) = cmp_eq { - let mut where_clause = + let mut expr_where_clause = Generics::from(generics) .where_clause .unwrap_or_else(|| syn::WhereClause { where_token: Token![where](span), predicates: Punctuated::new(), }); + let mut sim_value_where_clause = expr_where_clause.clone(); + let mut fields_sim_value_eq = vec![]; let mut fields_cmp_eq = vec![]; let mut fields_cmp_ne = vec![]; for field in fields.named() { let field_ident = field.ident(); let field_ty = field.ty(); - where_clause + expr_where_clause .predicates .push(parse_quote_spanned! {cmp_eq.span=> #field_ty: ::fayalite::expr::ops::ExprPartialEq<#field_ty> }); + sim_value_where_clause + .predicates + .push(parse_quote_spanned! {cmp_eq.span=> + #field_ty: ::fayalite::sim::value::SimValuePartialEq<#field_ty> + }); + fields_sim_value_eq.push(quote_spanned! {span=> + ::fayalite::sim::value::SimValuePartialEq::sim_value_eq(&__lhs.#field_ident, &__rhs.#field_ident) + }); fields_cmp_eq.push(quote_spanned! {span=> ::fayalite::expr::ops::ExprPartialEq::cmp_eq(__lhs.#field_ident, __rhs.#field_ident) }); @@ -792,9 +937,13 @@ impl ToTokens for ParsedBundle { ::fayalite::expr::ops::ExprPartialEq::cmp_ne(__lhs.#field_ident, __rhs.#field_ident) }); } + let sim_value_eq_body; let cmp_eq_body; let cmp_ne_body; if fields_len == 0 { + sim_value_eq_body = quote_spanned! {span=> + true + }; cmp_eq_body = quote_spanned! {span=> ::fayalite::expr::ToExpr::to_expr(&true) }; @@ -802,6 +951,9 @@ impl ToTokens for ParsedBundle { ::fayalite::expr::ToExpr::to_expr(&false) }; } else { + sim_value_eq_body = quote_spanned! {span=> + #(#fields_sim_value_eq)&&* + }; cmp_eq_body = quote_spanned! {span=> #(#fields_cmp_eq)&* }; @@ -812,7 +964,7 @@ impl ToTokens for ParsedBundle { quote_spanned! {span=> #[automatically_derived] impl #impl_generics ::fayalite::expr::ops::ExprPartialEq for #target #type_generics - #where_clause + #expr_where_clause { fn cmp_eq( __lhs: ::fayalite::expr::Expr, @@ -827,6 +979,17 @@ impl ToTokens for ParsedBundle { #cmp_ne_body } } + #[automatically_derived] + impl #impl_generics ::fayalite::sim::value::SimValuePartialEq for #target #type_generics + #sim_value_where_clause + { + fn sim_value_eq( + __lhs: &::fayalite::sim::value::SimValue, + __rhs: &::fayalite::sim::value::SimValue, + ) -> bool { + #sim_value_eq_body + } + } } .to_tokens(tokens); } diff --git a/crates/fayalite-proc-macros-impl/src/hdl_enum.rs b/crates/fayalite-proc-macros-impl/src/hdl_enum.rs index 9174566..0cdf85c 100644 --- a/crates/fayalite-proc-macros-impl/src/hdl_enum.rs +++ b/crates/fayalite-proc-macros-impl/src/hdl_enum.rs @@ -129,6 +129,7 @@ pub(crate) struct ParsedEnum { pub(crate) brace_token: Brace, pub(crate) variants: Punctuated, pub(crate) match_variant_ident: Ident, + pub(crate) sim_value_ident: Ident, } impl ParsedEnum { @@ -190,6 +191,7 @@ impl ParsedEnum { brace_token, variants, match_variant_ident: format_ident!("__{}__MatchVariant", ident), + sim_value_ident: format_ident!("__{}__SimValue", ident), ident, }) } @@ -207,6 +209,7 @@ impl ToTokens for ParsedEnum { brace_token, variants, match_variant_ident, + sim_value_ident, } = self; let span = ident.span(); let ItemOptions { @@ -409,6 +412,106 @@ impl ToTokens for ParsedEnum { )), } .to_tokens(tokens); + let mut enum_attrs = attrs.clone(); + enum_attrs.push(parse_quote_spanned! {span=> + #[::fayalite::__std::prelude::v1::derive( + ::fayalite::__std::fmt::Debug, + ::fayalite::__std::clone::Clone, + )] + }); + enum_attrs.push(parse_quote_spanned! {span=> + #[allow(dead_code, non_camel_case_types)] + }); + let sim_value_has_unknown_variant = !variants.len().is_power_of_two(); + let sim_value_unknown_variant_name = sim_value_has_unknown_variant.then(|| { + let mut name = String::new(); + let unknown = "Unknown"; + loop { + let orig_len = name.len(); + name.push_str(unknown); + if variants.iter().all(|v| v.ident != name) { + break Ident::new(&name, span); + } + name.truncate(orig_len); + name.push('_'); + } + }); + let sim_value_unknown_variant = + sim_value_unknown_variant_name + .as_ref() + .map(|unknown_variant_name| { + Pair::End(parse_quote_spanned! {span=> + #unknown_variant_name(::fayalite::enum_::UnknownVariantSimValue) + }) + }); + ItemEnum { + attrs: enum_attrs, + vis: vis.clone(), + enum_token: *enum_token, + ident: sim_value_ident.clone(), + generics: generics.into(), + brace_token: *brace_token, + variants: Punctuated::from_iter( + variants + .pairs() + .map_pair_value_ref( + |ParsedVariant { + attrs, + options: _, + ident, + field, + }| Variant { + attrs: attrs.clone(), + ident: ident.clone(), + fields: match field { + Some(ParsedVariantField { + paren_token, + attrs, + options: _, + ty, + comma_token, + }) => Fields::Unnamed(FieldsUnnamed { + paren_token: *paren_token, + unnamed: Punctuated::from_iter([ + Pair::new( + Field { + attrs: attrs.clone(), + vis: Visibility::Inherited, + mutability: FieldMutability::None, + ident: None, + colon_token: None, + ty: parse_quote_spanned! {span=> + ::fayalite::sim::value::SimValue<#ty> + }, + }, + Some(comma_token.unwrap_or(Token![,](ident.span()))), + ), + Pair::new( + Field { + attrs: vec![], + vis: Visibility::Inherited, + mutability: FieldMutability::None, + ident: None, + colon_token: None, + ty: parse_quote_spanned! {span=> + ::fayalite::enum_::EnumPaddingSimValue + }, + }, + None, + ), + ]), + }), + None => Fields::Unnamed(parse_quote_spanned! {span=> + (::fayalite::enum_::EnumPaddingSimValue) + }), + }, + discriminant: None, + }, + ) + .chain(sim_value_unknown_variant), + ), + } + .to_tokens(tokens); let self_token = Token![self](span); for (index, ParsedVariant { ident, field, .. }) in variants.iter().enumerate() { if let Some(ParsedVariantField { ty, .. }) = field { @@ -534,6 +637,142 @@ impl ToTokens for ParsedEnum { } }, )); + let sim_value_from_bits_unknown_match_arm = if let Some(sim_value_unknown_variant_name) = + &sim_value_unknown_variant_name + { + quote_spanned! {span=> + _ => #sim_value_ident::#sim_value_unknown_variant_name(v.unknown_variant_from_bits()), + } + } else { + quote_spanned! {span=> + _ => ::fayalite::__std::unreachable!(), + } + }; + let sim_value_from_bits_match_arms = Vec::from_iter( + variants + .iter() + .enumerate() + .map( + |( + index, + ParsedVariant { + attrs: _, + options: _, + ident, + field, + }, + )| { + if let Some(_) = field { + quote_spanned! {span=> + #index => { + let (field, padding) = v.variant_with_field_from_bits(); + #sim_value_ident::#ident(field, padding) + } + } + } else { + quote_spanned! {span=> + #index => #sim_value_ident::#ident( + v.variant_no_field_from_bits(), + ), + } + } + }, + ) + .chain([sim_value_from_bits_unknown_match_arm]), + ); + let sim_value_clone_from_bits_unknown_match_arm = + if let Some(sim_value_unknown_variant_name) = &sim_value_unknown_variant_name { + quote_spanned! {span=> + _ => if let #sim_value_ident::#sim_value_unknown_variant_name(value) = value { + v.unknown_variant_clone_from_bits(value); + } else { + *value = #sim_value_ident::#sim_value_unknown_variant_name( + v.unknown_variant_from_bits(), + ); + }, + } + } else { + quote_spanned! {span=> + _ => ::fayalite::__std::unreachable!(), + } + }; + let sim_value_clone_from_bits_match_arms = Vec::from_iter( + variants + .iter() + .enumerate() + .map( + |( + index, + ParsedVariant { + attrs: _, + options: _, + ident, + field, + }, + )| { + if let Some(_) = field { + quote_spanned! {span=> + #index => if let #sim_value_ident::#ident(field, padding) = value { + v.variant_with_field_clone_from_bits(field, padding); + } else { + let (field, padding) = v.variant_with_field_from_bits(); + *value = #sim_value_ident::#ident(field, padding); + }, + } + } else { + quote_spanned! {span=> + #index => if let #sim_value_ident::#ident(padding) = value { + v.variant_no_field_clone_from_bits(padding); + } else { + *value = #sim_value_ident::#ident( + v.variant_no_field_from_bits(), + ); + }, + } + } + }, + ) + .chain([sim_value_clone_from_bits_unknown_match_arm]), + ); + let sim_value_to_bits_match_arms = Vec::from_iter( + variants + .iter() + .enumerate() + .map( + |( + index, + ParsedVariant { + attrs: _, + options: _, + ident, + field, + }, + )| { + if let Some(_) = field { + quote_spanned! {span=> + #sim_value_ident::#ident(field, padding) => { + v.variant_with_field_to_bits(#index, field, padding); + } + } + } else { + quote_spanned! {span=> + #sim_value_ident::#ident(padding) => { + v.variant_no_field_to_bits(#index, padding); + } + } + } + }, + ) + .chain(sim_value_unknown_variant_name.as_ref().map( + |sim_value_unknown_variant_name| { + quote_spanned! {span=> + #sim_value_ident::#sim_value_unknown_variant_name(value) => { + v.unknown_variant_to_bits(value); + } + } + }, + )), + ); let variants_len = variants.len(); quote_spanned! {span=> #[automatically_derived] @@ -542,6 +781,7 @@ impl ToTokens for ParsedEnum { { type BaseType = ::fayalite::enum_::Enum; type MaskType = ::fayalite::int::Bool; + type SimValue = #sim_value_ident #type_generics; type MatchVariant = #match_variant_ident #type_generics; type MatchActiveScope = ::fayalite::module::Scope; type MatchVariantAndInactiveScope = ::fayalite::enum_::EnumMatchVariantAndInactiveScope; @@ -574,6 +814,35 @@ impl ToTokens for ParsedEnum { fn source_location() -> ::fayalite::source_location::SourceLocation { ::fayalite::source_location::SourceLocation::caller() } + fn sim_value_from_bits( + &self, + bits: &::fayalite::__bitvec::slice::BitSlice, + ) -> ::SimValue { + let v = ::fayalite::enum_::EnumSimValueFromBits::new(*self, bits); + match v.discriminant() { + #(#sim_value_from_bits_match_arms)* + } + } + fn sim_value_clone_from_bits( + &self, + value: &mut ::SimValue, + bits: &::fayalite::__bitvec::slice::BitSlice, + ) { + let v = ::fayalite::enum_::EnumSimValueFromBits::new(*self, bits); + match v.discriminant() { + #(#sim_value_clone_from_bits_match_arms)* + } + } + fn sim_value_to_bits( + &self, + value: &::SimValue, + bits: &mut ::fayalite::__bitvec::slice::BitSlice, + ) { + let v = ::fayalite::enum_::EnumSimValueToBits::new(*self, bits); + match value { + #(#sim_value_to_bits_match_arms)* + } + } } #[automatically_derived] impl #impl_generics ::fayalite::enum_::EnumType for #target #type_generics diff --git a/crates/fayalite/src/array.rs b/crates/fayalite/src/array.rs index 0d9b63f..a2df6cf 100644 --- a/crates/fayalite/src/array.rs +++ b/crates/fayalite/src/array.rs @@ -1,6 +1,8 @@ // SPDX-License-Identifier: LGPL-3.0-or-later // See Notices.txt for copyright information +use bitvec::slice::BitSlice; + use crate::{ expr::{ ops::{ArrayLiteral, ExprFromIterator, ExprIntoIterator, ExprPartialEq}, @@ -9,6 +11,7 @@ use crate::{ int::{Bool, DynSize, KnownSize, Size, SizeType, DYN_SIZE}, intern::{Intern, Interned, LazyInterned}, module::transform::visit::{Fold, Folder, Visit, Visitor}, + sim::value::{SimValue, SimValuePartialEq}, source_location::SourceLocation, ty::{ CanonicalType, MatchVariantWithoutScope, StaticType, Type, TypeProperties, TypeWithDeref, @@ -142,6 +145,7 @@ impl, Len: Size, State: Visitor + ?Sized> Visit impl Type for ArrayType { type BaseType = Array; type MaskType = ArrayType; + type SimValue = Len::ArraySimValue; type MatchVariant = Len::ArrayMatch; type MatchActiveScope = (); type MatchVariantAndInactiveScope = MatchVariantWithoutScope>; @@ -178,9 +182,48 @@ impl Type for ArrayType { Len::from_usize(array.len()), ) } + fn source_location() -> SourceLocation { SourceLocation::builtin() } + + fn sim_value_from_bits(&self, bits: &BitSlice) -> Self::SimValue { + assert_eq!(bits.len(), self.type_properties.bit_width); + let element = self.element(); + let element_bit_width = element.canonical().bit_width(); + TryFrom::try_from(Vec::from_iter((0..self.len()).map(|i| { + SimValue::from_bitslice(element, &bits[i * element_bit_width..][..element_bit_width]) + }))) + .ok() + .expect("used correct length") + } + + fn sim_value_clone_from_bits(&self, value: &mut Self::SimValue, bits: &BitSlice) { + assert_eq!(bits.len(), self.type_properties.bit_width); + let element_ty = self.element(); + let element_bit_width = element_ty.canonical().bit_width(); + let value: &mut [SimValue] = value.as_mut(); + assert_eq!(self.len(), value.len()); + for (i, element_value) in value.iter_mut().enumerate() { + assert_eq!(SimValue::ty(element_value), element_ty); + SimValue::bits_mut(element_value) + .bits_mut() + .copy_from_bitslice(&bits[i * element_bit_width..][..element_bit_width]); + } + } + + fn sim_value_to_bits(&self, value: &Self::SimValue, bits: &mut BitSlice) { + assert_eq!(bits.len(), self.type_properties.bit_width); + let element_ty = self.element(); + let element_bit_width = element_ty.canonical().bit_width(); + let value: &[SimValue] = value.as_ref(); + assert_eq!(self.len(), value.len()); + for (i, element_value) in value.iter().enumerate() { + assert_eq!(SimValue::ty(element_value), element_ty); + bits[i * element_bit_width..][..element_bit_width] + .copy_from_bitslice(SimValue::bits(element_value).bits()); + } + } } impl TypeWithDeref for ArrayType { @@ -247,6 +290,18 @@ where } } +impl SimValuePartialEq> for ArrayType +where + Lhs: SimValuePartialEq, +{ + fn sim_value_eq(this: &SimValue, other: &SimValue>) -> bool { + AsRef::<[_]>::as_ref(&**this) + .iter() + .zip(AsRef::<[_]>::as_ref(&**other)) + .all(|(l, r)| SimValuePartialEq::sim_value_eq(l, r)) + } +} + impl ExprIntoIterator for ArrayType { type Item = T; type ExprIntoIter = ExprArrayIter; diff --git a/crates/fayalite/src/bundle.rs b/crates/fayalite/src/bundle.rs index 9807b92..06e0411 100644 --- a/crates/fayalite/src/bundle.rs +++ b/crates/fayalite/src/bundle.rs @@ -8,14 +8,14 @@ use crate::{ }, int::{Bool, DynSize}, intern::{Intern, Interned}, - sim::{SimValue, ToSimValue}, + sim::value::{SimValue, SimValuePartialEq, ToSimValue}, source_location::SourceLocation, ty::{ - impl_match_variant_as_self, CanonicalType, MatchVariantWithoutScope, StaticType, Type, - TypeProperties, TypeWithDeref, + impl_match_variant_as_self, CanonicalType, MatchVariantWithoutScope, OpaqueSimValue, + StaticType, Type, TypeProperties, TypeWithDeref, }, }; -use bitvec::vec::BitVec; +use bitvec::{slice::BitSlice, vec::BitVec}; use hashbrown::HashMap; use std::{fmt, marker::PhantomData}; @@ -216,6 +216,7 @@ impl Bundle { impl Type for Bundle { type BaseType = Bundle; type MaskType = Bundle; + type SimValue = OpaqueSimValue; impl_match_variant_as_self!(); fn mask_type(&self) -> Self::MaskType { Self::new(Interned::from_iter(self.0.fields.into_iter().map( @@ -239,6 +240,20 @@ impl Type for Bundle { fn source_location() -> SourceLocation { SourceLocation::builtin() } + fn sim_value_from_bits(&self, bits: &BitSlice) -> Self::SimValue { + assert_eq!(bits.len(), self.type_properties().bit_width); + OpaqueSimValue::from_bitslice(bits) + } + fn sim_value_clone_from_bits(&self, value: &mut Self::SimValue, bits: &BitSlice) { + assert_eq!(bits.len(), self.type_properties().bit_width); + assert_eq!(value.bit_width(), self.type_properties().bit_width); + value.bits_mut().bits_mut().copy_from_bitslice(bits); + } + fn sim_value_to_bits(&self, value: &Self::SimValue, bits: &mut BitSlice) { + assert_eq!(bits.len(), self.type_properties().bit_width); + assert_eq!(value.bit_width(), self.type_properties().bit_width); + bits.copy_from_bitslice(value.bits().bits()); + } } pub trait BundleType: Type { @@ -247,6 +262,93 @@ pub trait BundleType: Type { fn fields(&self) -> Interned<[BundleField]>; } +pub struct BundleSimValueFromBits<'a> { + fields: std::slice::Iter<'static, BundleField>, + bits: &'a BitSlice, +} + +impl<'a> BundleSimValueFromBits<'a> { + #[track_caller] + pub fn new(bundle_ty: T, bits: &'a BitSlice) -> Self { + let fields = bundle_ty.fields(); + assert_eq!( + bits.len(), + fields + .iter() + .map(|BundleField { ty, .. }| ty.bit_width()) + .sum::() + ); + Self { + fields: Interned::into_inner(fields).iter(), + bits, + } + } + #[track_caller] + fn field_ty_and_bits(&mut self) -> (T, &'a BitSlice) { + let Some(&BundleField { + name: _, + flipped: _, + ty, + }) = self.fields.next() + else { + panic!("tried to read too many fields from BundleSimValueFromBits"); + }; + let (field_bits, rest) = self.bits.split_at(ty.bit_width()); + self.bits = rest; + (T::from_canonical(ty), field_bits) + } + #[track_caller] + pub fn field_from_bits(&mut self) -> SimValue { + let (field_ty, field_bits) = self.field_ty_and_bits::(); + SimValue::from_bitslice(field_ty, field_bits) + } + #[track_caller] + pub fn field_clone_from_bits(&mut self, field_value: &mut SimValue) { + let (field_ty, field_bits) = self.field_ty_and_bits::(); + assert_eq!(field_ty, SimValue::ty(field_value)); + SimValue::bits_mut(field_value) + .bits_mut() + .copy_from_bitslice(field_bits); + } +} + +pub struct BundleSimValueToBits<'a> { + fields: std::slice::Iter<'static, BundleField>, + bits: &'a mut BitSlice, +} + +impl<'a> BundleSimValueToBits<'a> { + #[track_caller] + pub fn new(bundle_ty: T, bits: &'a mut BitSlice) -> Self { + let fields = bundle_ty.fields(); + assert_eq!( + bits.len(), + fields + .iter() + .map(|BundleField { ty, .. }| ty.bit_width()) + .sum::() + ); + Self { + fields: Interned::into_inner(fields).iter(), + bits, + } + } + #[track_caller] + pub fn field_to_bits(&mut self, field_value: &SimValue) { + let Some(&BundleField { + name: _, + flipped: _, + ty, + }) = self.fields.next() + else { + panic!("tried to read too many fields from BundleSimValueFromBits"); + }; + assert_eq!(T::from_canonical(ty), SimValue::ty(field_value)); + self.bits[..ty.bit_width()].copy_from_bitslice(SimValue::bits(field_value).bits()); + self.bits = &mut std::mem::take(&mut self.bits)[ty.bit_width()..]; + } +} + #[derive(Default)] pub struct NoBuilder; @@ -353,6 +455,7 @@ macro_rules! impl_tuples { impl<$($T: Type,)*> Type for ($($T,)*) { type BaseType = Bundle; type MaskType = ($($T::MaskType,)*); + type SimValue = ($(SimValue<$T>,)*); type MatchVariant = ($(Expr<$T>,)*); type MatchActiveScope = (); type MatchVariantAndInactiveScope = MatchVariantWithoutScope; @@ -391,6 +494,24 @@ macro_rules! impl_tuples { fn source_location() -> SourceLocation { SourceLocation::builtin() } + fn sim_value_from_bits(&self, bits: &BitSlice) -> Self::SimValue { + #![allow(unused_mut, unused_variables)] + let mut v = BundleSimValueFromBits::new(*self, bits); + $(let $var = v.field_from_bits();)* + ($($var,)*) + } + fn sim_value_clone_from_bits(&self, value: &mut Self::SimValue, bits: &BitSlice) { + #![allow(unused_mut, unused_variables)] + let mut v = BundleSimValueFromBits::new(*self, bits); + let ($($var,)*) = value; + $(v.field_clone_from_bits($var);)* + } + fn sim_value_to_bits(&self, value: &Self::SimValue, bits: &mut BitSlice) { + #![allow(unused_mut, unused_variables)] + let mut v = BundleSimValueToBits::new(*self, bits); + let ($($var,)*) = value; + $(v.field_to_bits($var);)* + } } impl<$($T: Type,)*> BundleType for ($($T,)*) { type Builder = TupleBuilder<($(Unfilled<$T>,)*)>; @@ -444,16 +565,12 @@ macro_rules! impl_tuples { impl<$($T: ToSimValue,)*> ToSimValue for ($($T,)*) { #[track_caller] fn to_sim_value(&self, ty: CanonicalType) -> SimValue { - ToSimValue::::to_sim_value(self, Bundle::from_canonical(ty)).into_canonical() + SimValue::into_canonical(ToSimValue::::to_sim_value(self, Bundle::from_canonical(ty))) } #[track_caller] fn into_sim_value(self, ty: CanonicalType) -> SimValue { - ToSimValue::::into_sim_value(self, Bundle::from_canonical(ty)).into_canonical() - } - #[track_caller] - fn box_into_sim_value(self: Box, ty: CanonicalType) -> SimValue { - ToSimValue::::box_into_sim_value(self, Bundle::from_canonical(ty)).into_canonical() + SimValue::into_canonical(ToSimValue::::into_sim_value(self, Bundle::from_canonical(ty))) } } impl<$($T: ToSimValue,)*> ToSimValue for ($($T,)*) { @@ -474,24 +591,12 @@ macro_rules! impl_tuples { let [$($ty_var,)*] = *ty.fields() else { panic!("bundle has wrong number of fields"); }; - let mut bits: Option = None; + let mut bits = BitVec::new(); $(let $var = $var.into_sim_value($ty_var.ty); - assert_eq!($var.ty(), $ty_var.ty); - if !$var.bits().is_empty() { - if let Some(bits) = &mut bits { - bits.extend_from_bitslice($var.bits()); - } else { - let mut $var = $var.into_bits(); - $var.reserve(ty.type_properties().bit_width - $var.len()); - bits = Some($var); - } - } + assert_eq!(SimValue::ty(&$var), $ty_var.ty); + bits.extend_from_bitslice(SimValue::bits(&$var).bits()); )* - bits.unwrap_or_else(BitVec::new).into_sim_value(ty) - } - #[track_caller] - fn box_into_sim_value(self: Box, ty: Bundle) -> SimValue { - Self::into_sim_value(*self, ty) + bits.into_sim_value(ty) } } impl<$($T: ToSimValue<$Ty>, $Ty: Type,)*> ToSimValue<($($Ty,)*)> for ($($T,)*) { @@ -499,19 +604,15 @@ macro_rules! impl_tuples { fn to_sim_value(&self, ty: ($($Ty,)*)) -> SimValue<($($Ty,)*)> { let ($($var,)*) = self; let ($($ty_var,)*) = ty; - $(let $var = $var.to_sim_value($ty_var).into_canonical();)* - SimValue::from_canonical(ToSimValue::into_sim_value(($($var,)*), ty.canonical())) + $(let $var = $var.to_sim_value($ty_var);)* + SimValue::from_value(ty, ($($var,)*)) } #[track_caller] fn into_sim_value(self, ty: ($($Ty,)*)) -> SimValue<($($Ty,)*)> { let ($($var,)*) = self; let ($($ty_var,)*) = ty; - $(let $var = $var.into_sim_value($ty_var).into_canonical();)* - SimValue::from_canonical(ToSimValue::into_sim_value(($($var,)*), ty.canonical())) - } - #[track_caller] - fn box_into_sim_value(self: Box, ty: ($($Ty,)*)) -> SimValue<($($Ty,)*)> { - Self::into_sim_value(*self, ty) + $(let $var = $var.into_sim_value($ty_var);)* + SimValue::from_value(ty, ($($var,)*)) } } impl<$($Lhs: Type + ExprPartialEq<$Rhs>, $Rhs: Type,)*> ExprPartialEq<($($Rhs,)*)> for ($($Lhs,)*) { @@ -537,6 +638,15 @@ macro_rules! impl_tuples { .any_one_bits() } } + impl<$($Lhs: SimValuePartialEq<$Rhs>, $Rhs: Type,)*> SimValuePartialEq<($($Rhs,)*)> for ($($Lhs,)*) { + fn sim_value_eq(lhs: &SimValue, rhs: &SimValue<($($Rhs,)*)>) -> bool { + let ($($lhs_var,)*) = &**lhs; + let ($($rhs_var,)*) = &**rhs; + let retval = true; + $(let retval = retval && $lhs_var == $rhs_var;)* + retval + } + } }; ([$($lhs:tt)*] [$rhs_first:tt $($rhs:tt)*]) => { impl_tuples!([$($lhs)*] []); @@ -564,6 +674,7 @@ impl_tuples! { impl Type for PhantomData { type BaseType = Bundle; type MaskType = (); + type SimValue = (); type MatchVariant = PhantomData; type MatchActiveScope = (); type MatchVariantAndInactiveScope = MatchVariantWithoutScope; @@ -596,6 +707,16 @@ impl Type for PhantomData { fn source_location() -> SourceLocation { SourceLocation::builtin() } + fn sim_value_from_bits(&self, bits: &BitSlice) -> Self::SimValue { + assert!(bits.is_empty()); + () + } + fn sim_value_clone_from_bits(&self, _value: &mut Self::SimValue, bits: &BitSlice) { + assert!(bits.is_empty()); + } + fn sim_value_to_bits(&self, _value: &Self::SimValue, bits: &mut BitSlice) { + assert!(bits.is_empty()); + } } pub struct PhantomDataBuilder(PhantomData); @@ -663,6 +784,6 @@ impl ToSimValue for PhantomData { fn to_sim_value(&self, ty: CanonicalType) -> SimValue { let ty = Bundle::from_canonical(ty); assert!(ty.fields().is_empty()); - ToSimValue::into_sim_value(BitVec::new(), ty).into_canonical() + SimValue::into_canonical(ToSimValue::into_sim_value(BitVec::new(), ty)) } } diff --git a/crates/fayalite/src/clock.rs b/crates/fayalite/src/clock.rs index 711432b..f0623d4 100644 --- a/crates/fayalite/src/clock.rs +++ b/crates/fayalite/src/clock.rs @@ -8,6 +8,7 @@ use crate::{ source_location::SourceLocation, ty::{impl_match_variant_as_self, CanonicalType, StaticType, Type, TypeProperties}, }; +use bitvec::slice::BitSlice; #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, Default)] pub struct Clock; @@ -15,6 +16,7 @@ pub struct Clock; impl Type for Clock { type BaseType = Clock; type MaskType = Bool; + type SimValue = bool; impl_match_variant_as_self!(); @@ -36,6 +38,21 @@ impl Type for Clock { }; retval } + + fn sim_value_from_bits(&self, bits: &BitSlice) -> Self::SimValue { + assert_eq!(bits.len(), 1); + bits[0] + } + + fn sim_value_clone_from_bits(&self, value: &mut Self::SimValue, bits: &BitSlice) { + assert_eq!(bits.len(), 1); + *value = bits[0]; + } + + fn sim_value_to_bits(&self, value: &Self::SimValue, bits: &mut BitSlice) { + assert_eq!(bits.len(), 1); + bits.set(0, *value); + } } impl Clock { diff --git a/crates/fayalite/src/enum_.rs b/crates/fayalite/src/enum_.rs index 70c58c0..9fa38e9 100644 --- a/crates/fayalite/src/enum_.rs +++ b/crates/fayalite/src/enum_.rs @@ -7,17 +7,22 @@ use crate::{ Expr, ToExpr, }, hdl, - int::Bool, + int::{Bool, UIntValue}, intern::{Intern, Interned}, module::{ connect, enum_match_variants_helper, incomplete_wire, wire, EnumMatchVariantAndInactiveScopeImpl, EnumMatchVariantsIterImpl, Scope, }, + sim::value::{SimValue, SimValuePartialEq}, source_location::SourceLocation, - ty::{CanonicalType, MatchVariantAndInactiveScope, StaticType, Type, TypeProperties}, + ty::{ + CanonicalType, MatchVariantAndInactiveScope, OpaqueSimValue, StaticType, Type, + TypeProperties, + }, }; +use bitvec::{order::Lsb0, slice::BitSlice, view::BitView}; use hashbrown::HashMap; -use std::{convert::Infallible, fmt, iter::FusedIterator}; +use std::{convert::Infallible, fmt, iter::FusedIterator, sync::Arc}; #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] pub struct EnumVariant { @@ -152,6 +157,12 @@ impl EnumTypePropertiesBuilder { variant_count: variant_count + 1, } } + #[must_use] + pub fn variants(self, variants: impl IntoIterator) -> Self { + variants.into_iter().fold(self, |this, variant| { + this.variant(variant.ty.map(CanonicalType::type_properties)) + }) + } pub const fn finish(self) -> TypeProperties { assert!( self.variant_count != 0, @@ -325,6 +336,7 @@ impl EnumType for Enum { impl Type for Enum { type BaseType = Enum; type MaskType = Bool; + type SimValue = OpaqueSimValue; type MatchVariant = Option>; type MatchActiveScope = Scope; type MatchVariantAndInactiveScope = EnumMatchVariantAndInactiveScope; @@ -355,6 +367,296 @@ impl Type for Enum { fn source_location() -> SourceLocation { SourceLocation::builtin() } + fn sim_value_from_bits(&self, bits: &BitSlice) -> Self::SimValue { + assert_eq!(bits.len(), self.type_properties().bit_width); + OpaqueSimValue::from_bitslice(bits) + } + fn sim_value_clone_from_bits(&self, value: &mut Self::SimValue, bits: &BitSlice) { + assert_eq!(bits.len(), self.type_properties().bit_width); + assert_eq!(value.bit_width(), self.type_properties().bit_width); + value.bits_mut().bits_mut().copy_from_bitslice(bits); + } + fn sim_value_to_bits(&self, value: &Self::SimValue, bits: &mut BitSlice) { + assert_eq!(bits.len(), self.type_properties().bit_width); + assert_eq!(value.bit_width(), self.type_properties().bit_width); + bits.copy_from_bitslice(value.bits().bits()); + } +} + +#[derive(Clone, PartialEq, Eq, Hash, Debug, Default)] +pub struct EnumPaddingSimValue { + bits: Option, +} + +impl EnumPaddingSimValue { + pub fn bit_width(&self) -> Option { + self.bits.as_ref().map(UIntValue::width) + } + pub fn bits(&self) -> &Option { + &self.bits + } + pub fn bits_mut(&mut self) -> &mut Option { + &mut self.bits + } + pub fn into_bits(self) -> Option { + self.bits + } + pub fn from_bits(bits: Option) -> Self { + Self { bits } + } + pub fn from_bitslice(v: &BitSlice) -> Self { + Self { + bits: Some(UIntValue::new(Arc::new(v.to_bitvec()))), + } + } +} + +#[derive(Clone, PartialEq, Eq, Hash, Debug)] +pub struct UnknownVariantSimValue { + discriminant: usize, + body_bits: UIntValue, +} + +impl UnknownVariantSimValue { + pub fn discriminant(&self) -> usize { + self.discriminant + } + pub fn body_bits(&self) -> &UIntValue { + &self.body_bits + } + pub fn body_bits_mut(&mut self) -> &mut UIntValue { + &mut self.body_bits + } + pub fn into_body_bits(self) -> UIntValue { + self.body_bits + } + pub fn into_parts(self) -> (usize, UIntValue) { + (self.discriminant, self.body_bits) + } + pub fn new(discriminant: usize, body_bits: UIntValue) -> Self { + Self { + discriminant, + body_bits, + } + } +} + +pub struct EnumSimValueFromBits<'a> { + variants: Interned<[EnumVariant]>, + discriminant: usize, + body_bits: &'a BitSlice, +} + +impl<'a> EnumSimValueFromBits<'a> { + #[track_caller] + pub fn new(ty: T, bits: &'a BitSlice) -> Self { + let variants = ty.variants(); + let bit_width = EnumTypePropertiesBuilder::new() + .variants(variants) + .finish() + .bit_width; + assert_eq!(bit_width, bits.len()); + let (discriminant_bits, body_bits) = + bits.split_at(discriminant_bit_width_impl(variants.len())); + let mut discriminant = 0usize; + discriminant.view_bits_mut::()[..discriminant_bits.len()] + .copy_from_bitslice(discriminant_bits); + Self { + variants, + discriminant, + body_bits, + } + } + pub fn discriminant(&self) -> usize { + self.discriminant + } + #[track_caller] + #[cold] + fn usage_error(&self, clone: bool) -> ! { + let clone = if clone { "clone_" } else { "" }; + match self.variants.get(self.discriminant) { + None => { + panic!("should have called EnumSimValueFromBits::unknown_variant_{clone}from_bits"); + } + Some(EnumVariant { ty: None, .. }) => { + panic!( + "should have called EnumSimValueFromBits::variant_no_field_{clone}from_bits" + ); + } + Some(EnumVariant { ty: Some(_), .. }) => { + panic!( + "should have called EnumSimValueFromBits::variant_with_field_{clone}from_bits" + ); + } + } + } + #[track_caller] + fn known_variant(&self, clone: bool) -> (Option, &'a BitSlice, &'a BitSlice) { + let Some(EnumVariant { ty, .. }) = self.variants.get(self.discriminant) else { + self.usage_error(clone); + }; + let variant_bit_width = ty.map_or(0, CanonicalType::bit_width); + let (variant_bits, padding_bits) = self.body_bits.split_at(variant_bit_width); + (*ty, variant_bits, padding_bits) + } + #[track_caller] + pub fn unknown_variant_from_bits(self) -> UnknownVariantSimValue { + let None = self.variants.get(self.discriminant) else { + self.usage_error(false); + }; + UnknownVariantSimValue::new( + self.discriminant, + UIntValue::new(Arc::new(self.body_bits.to_bitvec())), + ) + } + #[track_caller] + pub fn unknown_variant_clone_from_bits(self, value: &mut UnknownVariantSimValue) { + let None = self.variants.get(self.discriminant) else { + self.usage_error(true); + }; + value.discriminant = self.discriminant; + assert_eq!(value.body_bits.width(), self.body_bits.len()); + value + .body_bits + .bits_mut() + .copy_from_bitslice(self.body_bits); + } + #[track_caller] + pub fn variant_no_field_from_bits(self) -> EnumPaddingSimValue { + let (None, _variant_bits, padding_bits) = self.known_variant(false) else { + self.usage_error(false); + }; + EnumPaddingSimValue::from_bitslice(padding_bits) + } + #[track_caller] + pub fn variant_with_field_from_bits(self) -> (SimValue, EnumPaddingSimValue) { + let (Some(variant_ty), variant_bits, padding_bits) = self.known_variant(false) else { + self.usage_error(false); + }; + ( + SimValue::from_bitslice(T::from_canonical(variant_ty), variant_bits), + EnumPaddingSimValue::from_bitslice(padding_bits), + ) + } + #[track_caller] + fn clone_padding_from_bits(padding: &mut EnumPaddingSimValue, padding_bits: &BitSlice) { + match padding.bits_mut() { + None => *padding = EnumPaddingSimValue::from_bitslice(padding_bits), + Some(padding) => { + assert_eq!(padding.width(), padding_bits.len()); + padding.bits_mut().copy_from_bitslice(padding_bits); + } + } + } + #[track_caller] + pub fn variant_no_field_clone_from_bits(self, padding: &mut EnumPaddingSimValue) { + let (None, _variant_bits, padding_bits) = self.known_variant(true) else { + self.usage_error(true); + }; + Self::clone_padding_from_bits(padding, padding_bits); + } + #[track_caller] + pub fn variant_with_field_clone_from_bits( + self, + value: &mut SimValue, + padding: &mut EnumPaddingSimValue, + ) { + let (Some(variant_ty), variant_bits, padding_bits) = self.known_variant(true) else { + self.usage_error(true); + }; + assert_eq!(SimValue::ty(value), T::from_canonical(variant_ty)); + SimValue::bits_mut(value) + .bits_mut() + .copy_from_bitslice(variant_bits); + Self::clone_padding_from_bits(padding, padding_bits); + } +} + +pub struct EnumSimValueToBits<'a> { + variants: Interned<[EnumVariant]>, + bit_width: usize, + discriminant_bit_width: usize, + bits: &'a mut BitSlice, +} + +impl<'a> EnumSimValueToBits<'a> { + #[track_caller] + pub fn new(ty: T, bits: &'a mut BitSlice) -> Self { + let variants = ty.variants(); + let bit_width = EnumTypePropertiesBuilder::new() + .variants(variants) + .finish() + .bit_width; + assert_eq!(bit_width, bits.len()); + Self { + variants, + bit_width, + discriminant_bit_width: discriminant_bit_width_impl(variants.len()), + bits, + } + } + #[track_caller] + fn discriminant_to_bits(&mut self, mut discriminant: usize) { + let orig_discriminant = discriminant; + let discriminant_bits = + &mut discriminant.view_bits_mut::()[..self.discriminant_bit_width]; + self.bits[..self.discriminant_bit_width].copy_from_bitslice(discriminant_bits); + discriminant_bits.fill(false); + assert!( + discriminant == 0, + "{orig_discriminant:#x} is too big to fit in enum discriminant bits", + ); + } + #[track_caller] + pub fn unknown_variant_to_bits(mut self, value: &UnknownVariantSimValue) { + self.discriminant_to_bits(value.discriminant); + let None = self.variants.get(value.discriminant) else { + panic!("can't use UnknownVariantSimValue to set known discriminant"); + }; + assert_eq!( + self.bit_width - self.discriminant_bit_width, + value.body_bits.width() + ); + self.bits[self.discriminant_bit_width..].copy_from_bitslice(value.body_bits.bits()); + } + #[track_caller] + fn known_variant( + mut self, + discriminant: usize, + padding: &EnumPaddingSimValue, + ) -> (Option, &'a mut BitSlice) { + self.discriminant_to_bits(discriminant); + let variant_ty = self.variants[discriminant].ty; + let variant_bit_width = variant_ty.map_or(0, CanonicalType::bit_width); + let padding_bits = &mut self.bits[self.discriminant_bit_width..][variant_bit_width..]; + if let Some(padding) = padding.bits() { + assert_eq!(padding.width(), padding_bits.len()); + padding_bits.copy_from_bitslice(padding.bits()); + } else { + padding_bits.fill(false); + } + let variant_bits = &mut self.bits[self.discriminant_bit_width..][..variant_bit_width]; + (variant_ty, variant_bits) + } + #[track_caller] + pub fn variant_no_field_to_bits(self, discriminant: usize, padding: &EnumPaddingSimValue) { + let (None, _variant_bits) = self.known_variant(discriminant, padding) else { + panic!("expected variant to have no field"); + }; + } + #[track_caller] + pub fn variant_with_field_to_bits( + self, + discriminant: usize, + value: &SimValue, + padding: &EnumPaddingSimValue, + ) { + let (Some(variant_ty), variant_bits) = self.known_variant(discriminant, padding) else { + panic!("expected variant to have a field"); + }; + assert_eq!(SimValue::ty(value), T::from_canonical(variant_ty)); + variant_bits.copy_from_bitslice(SimValue::bits(value).bits()); + } } #[hdl] @@ -417,6 +719,25 @@ impl, Rhs: Type> ExprPartialEq> fo } } +impl, Rhs: Type> SimValuePartialEq> for HdlOption { + fn sim_value_eq(this: &SimValue, other: &SimValue>) -> bool { + type SimValueMatch = ::SimValue; + match (&**this, &**other) { + (SimValueMatch::::HdlNone(_), SimValueMatch::>::HdlNone(_)) => { + true + } + (SimValueMatch::::HdlSome(..), SimValueMatch::>::HdlNone(_)) + | (SimValueMatch::::HdlNone(_), SimValueMatch::>::HdlSome(..)) => { + false + } + ( + SimValueMatch::::HdlSome(l, _), + SimValueMatch::>::HdlSome(r, _), + ) => l == r, + } + } +} + #[allow(non_snake_case)] pub fn HdlNone() -> Expr> { HdlOption[T::TYPE].HdlNone() diff --git a/crates/fayalite/src/expr.rs b/crates/fayalite/src/expr.rs index 016ec8e..f511c97 100644 --- a/crates/fayalite/src/expr.rs +++ b/crates/fayalite/src/expr.rs @@ -700,6 +700,7 @@ impl CastToBits for T { } pub trait CastBitsTo { + #[track_caller] fn cast_bits_to(&self, ty: T) -> Expr; } diff --git a/crates/fayalite/src/int.rs b/crates/fayalite/src/int.rs index 236f240..a956dd5 100644 --- a/crates/fayalite/src/int.rs +++ b/crates/fayalite/src/int.rs @@ -7,6 +7,7 @@ use crate::{ Expr, NotALiteralExpr, ToExpr, ToLiteralBits, }, intern::{Intern, Interned, Memoize}, + sim::value::SimValue, source_location::SourceLocation, ty::{impl_match_variant_as_self, CanonicalType, StaticType, Type, TypeProperties}, util::{interned_bit, ConstBool, ConstUsize, GenericConstBool, GenericConstUsize}, @@ -49,6 +50,15 @@ pub trait KnownSize: + IntoIterator> + TryFrom>> + Into>>; + type ArraySimValue: AsRef<[SimValue]> + + AsMut<[SimValue]> + + BorrowMut<[SimValue]> + + 'static + + Clone + + std::fmt::Debug + + IntoIterator> + + TryFrom>> + + Into>>; } macro_rules! known_widths { @@ -60,6 +70,7 @@ macro_rules! known_widths { }> { const SIZE: Self = Self; type ArrayMatch = [Expr; Self::VALUE]; + type ArraySimValue = [SimValue; Self::VALUE]; } }; ([2 $($rest:tt)*] $($bits:literal)+) => { @@ -72,6 +83,7 @@ macro_rules! known_widths { impl KnownSize for ConstUsize<{2 $(* $rest)*}> { const SIZE: Self = Self; type ArrayMatch = [Expr; Self::VALUE]; + type ArraySimValue = [SimValue; Self::VALUE]; } }; } @@ -100,6 +112,15 @@ pub trait Size: + IntoIterator> + TryFrom>> + Into>>; + type ArraySimValue: AsRef<[SimValue]> + + AsMut<[SimValue]> + + BorrowMut<[SimValue]> + + 'static + + Clone + + std::fmt::Debug + + IntoIterator> + + TryFrom>> + + Into>>; const KNOWN_VALUE: Option; type SizeType: SizeType + Copy @@ -125,6 +146,7 @@ impl SizeType for usize { impl Size for DynSize { type ArrayMatch = Box<[Expr]>; + type ArraySimValue = Box<[SimValue]>; const KNOWN_VALUE: Option = None; type SizeType = usize; @@ -147,6 +169,7 @@ impl SizeType for T { impl Size for T { type ArrayMatch = ::ArrayMatch; + type ArraySimValue = ::ArraySimValue; const KNOWN_VALUE: Option = Some(T::VALUE); @@ -287,6 +310,7 @@ macro_rules! impl_int { impl Type for $name { type BaseType = $pretty_name; type MaskType = Bool; + type SimValue = $value; impl_match_variant_as_self!(); fn mask_type(&self) -> Self::MaskType { Bool @@ -306,6 +330,20 @@ macro_rules! impl_int { fn source_location() -> SourceLocation { SourceLocation::builtin() } + fn sim_value_from_bits(&self, bits: &BitSlice) -> Self::SimValue { + assert_eq!(bits.len(), self.width()); + $value::new(Arc::new(bits.to_bitvec())) + } + fn sim_value_clone_from_bits(&self, value: &mut Self::SimValue, bits: &BitSlice) { + assert_eq!(bits.len(), self.width()); + assert_eq!(value.width(), self.width()); + value.bits_mut().copy_from_bitslice(bits); + } + fn sim_value_to_bits(&self, value: &Self::SimValue, bits: &mut BitSlice) { + assert_eq!(bits.len(), self.width()); + assert_eq!(value.width(), self.width()); + bits.copy_from_bitslice(value.bits()); + } } impl StaticType for $name { @@ -331,7 +369,7 @@ macro_rules! impl_int { } } - #[derive(Clone, PartialEq, Eq, Hash)] + #[derive(Clone, Eq, Hash)] pub struct $value { bits: Arc, _phantom: PhantomData, @@ -351,9 +389,15 @@ macro_rules! impl_int { } } - impl PartialOrd for $value { - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) + impl PartialEq<$value> for $value { + fn eq(&self, other: &$value) -> bool { + self.to_bigint() == other.to_bigint() + } + } + + impl PartialOrd<$value> for $value { + fn partial_cmp(&self, other: &$value) -> Option { + Some(self.to_bigint().cmp(&other.to_bigint())) } } @@ -401,6 +445,9 @@ macro_rules! impl_int { pub fn bits(&self) -> &Arc { &self.bits } + pub fn bits_mut(&mut self) -> &mut BitSlice { + Arc::::make_mut(&mut self.bits) + } } impl ToLiteralBits for $value { @@ -748,6 +795,7 @@ impl Bool { impl Type for Bool { type BaseType = Bool; type MaskType = Bool; + type SimValue = bool; impl_match_variant_as_self!(); fn mask_type(&self) -> Self::MaskType { Bool @@ -765,6 +813,18 @@ impl Type for Bool { fn source_location() -> SourceLocation { SourceLocation::builtin() } + fn sim_value_from_bits(&self, bits: &BitSlice) -> Self::SimValue { + assert_eq!(bits.len(), 1); + bits[0] + } + fn sim_value_clone_from_bits(&self, value: &mut Self::SimValue, bits: &BitSlice) { + assert_eq!(bits.len(), 1); + *value = bits[0]; + } + fn sim_value_to_bits(&self, value: &Self::SimValue, bits: &mut BitSlice) { + assert_eq!(bits.len(), 1); + bits.set(0, *value); + } } impl StaticType for Bool { diff --git a/crates/fayalite/src/lib.rs b/crates/fayalite/src/lib.rs index 512572d..0843589 100644 --- a/crates/fayalite/src/lib.rs +++ b/crates/fayalite/src/lib.rs @@ -8,6 +8,8 @@ extern crate self as fayalite; +#[doc(hidden)] +pub use bitvec as __bitvec; #[doc(hidden)] pub use std as __std; diff --git a/crates/fayalite/src/phantom_const.rs b/crates/fayalite/src/phantom_const.rs index dd6cff6..27bb04e 100644 --- a/crates/fayalite/src/phantom_const.rs +++ b/crates/fayalite/src/phantom_const.rs @@ -1,6 +1,7 @@ // SPDX-License-Identifier: LGPL-3.0-or-later // See Notices.txt for copyright information +use bitvec::slice::BitSlice; use serde::{de::DeserializeOwned, Deserialize, Serialize}; use crate::{ @@ -10,6 +11,7 @@ use crate::{ }, int::Bool, intern::{Intern, Interned, InternedCompare, LazyInterned, Memoize}, + sim::value::{SimValue, SimValuePartialEq}, source_location::SourceLocation, ty::{impl_match_variant_as_self, CanonicalType, StaticType, Type, TypeProperties}, }; @@ -246,6 +248,7 @@ impl PhantomConst { impl Type for PhantomConst { type BaseType = PhantomConst; type MaskType = (); + type SimValue = (); impl_match_variant_as_self!(); fn mask_type(&self) -> Self::MaskType { @@ -266,6 +269,19 @@ impl Type for PhantomConst { fn source_location() -> SourceLocation { SourceLocation::builtin() } + + fn sim_value_from_bits(&self, bits: &BitSlice) -> Self::SimValue { + assert!(bits.is_empty()); + () + } + + fn sim_value_clone_from_bits(&self, _value: &mut Self::SimValue, bits: &BitSlice) { + assert!(bits.is_empty()); + } + + fn sim_value_to_bits(&self, _value: &Self::SimValue, bits: &mut BitSlice) { + assert!(bits.is_empty()); + } } impl StaticType for PhantomConst @@ -311,3 +327,10 @@ impl ExprPartialOrd for PhantomConst { true.to_expr() } } + +impl SimValuePartialEq for PhantomConst { + fn sim_value_eq(this: &SimValue, other: &SimValue) -> bool { + assert_eq!(SimValue::ty(this), SimValue::ty(other)); + true + } +} diff --git a/crates/fayalite/src/reset.rs b/crates/fayalite/src/reset.rs index 9328365..312a8ea 100644 --- a/crates/fayalite/src/reset.rs +++ b/crates/fayalite/src/reset.rs @@ -7,6 +7,7 @@ use crate::{ source_location::SourceLocation, ty::{impl_match_variant_as_self, CanonicalType, StaticType, Type, TypeProperties}, }; +use bitvec::slice::BitSlice; mod sealed { pub trait ResetTypeSealed {} @@ -45,6 +46,7 @@ macro_rules! reset_type { impl Type for $name { type BaseType = $name; type MaskType = Bool; + type SimValue = bool; impl_match_variant_as_self!(); @@ -66,6 +68,21 @@ macro_rules! reset_type { }; retval } + + fn sim_value_from_bits(&self, bits: &BitSlice) -> Self::SimValue { + assert_eq!(bits.len(), 1); + bits[0] + } + + fn sim_value_clone_from_bits(&self, value: &mut Self::SimValue, bits: &BitSlice) { + assert_eq!(bits.len(), 1); + *value = bits[0]; + } + + fn sim_value_to_bits(&self, value: &Self::SimValue, bits: &mut BitSlice) { + assert_eq!(bits.len(), 1); + bits.set(0, *value); + } } impl $name { diff --git a/crates/fayalite/src/sim.rs b/crates/fayalite/src/sim.rs index b508756..9be4889 100644 --- a/crates/fayalite/src/sim.rs +++ b/crates/fayalite/src/sim.rs @@ -14,7 +14,7 @@ use crate::{ }, ExprEnum, Flow, ToLiteralBits, }, - int::{BoolOrIntType, IntType, SIntValue, UIntValue}, + int::{BoolOrIntType, UIntValue}, intern::{ Intern, Interned, InternedCompare, Memoize, PtrEqWithTypeId, SupportsPtrEqWithTypeId, }, @@ -38,6 +38,7 @@ use crate::{ TypeIndexRange, TypeLayout, TypeLen, TypeParts, }, time::{SimDuration, SimInstant}, + value::SimValue, }, ty::StaticType, util::{BitSliceWriteWithBase, DebugAsDisplay}, @@ -72,6 +73,7 @@ use std::{ mod interpreter; pub mod time; +pub mod value; pub mod vcd; #[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)] @@ -5904,635 +5906,6 @@ impl SimTraceKind { } } -#[derive(Clone, PartialEq, Eq, Hash)] -pub struct SimValue { - ty: T, - bits: BitVec, -} - -impl fmt::Debug for SimValue { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("SimValue") - .field("ty", &self.ty) - .field("bits", &BitSliceWriteWithBase(&self.bits)) - .finish() - } -} - -impl SimValue { - #[track_caller] - fn to_expr_impl(ty: CanonicalType, bits: &BitSlice) -> Expr { - match ty { - CanonicalType::UInt(_) => Expr::canonical(::bits_to_expr(Cow::Borrowed(bits))), - CanonicalType::SInt(_) => Expr::canonical(::bits_to_expr(Cow::Borrowed(bits))), - CanonicalType::Bool(_) => Expr::canonical(Bool::bits_to_expr(Cow::Borrowed(bits))), - CanonicalType::Array(ty) => { - let element_bit_width = ty.element().bit_width(); - Expr::::canonical( - crate::expr::ops::ArrayLiteral::new( - ty.element(), - (0..ty.len()) - .map(|array_index| { - let start = element_bit_width * array_index; - let end = start + element_bit_width; - Self::to_expr_impl(ty.element(), &bits[start..end]) - }) - .collect(), - ) - .to_expr(), - ) - } - CanonicalType::Enum(ty) => { - let discriminant_bit_width = ty.discriminant_bit_width(); - let mut variant_index = [0; mem::size_of::()]; - variant_index.view_bits_mut::()[0..discriminant_bit_width] - .clone_from_bitslice(&bits[..discriminant_bit_width]); - let variant_index = usize::from_le_bytes(variant_index); - if let Some(variant) = ty.variants().get(variant_index) { - let data_bit_width = variant.ty.map_or(0, CanonicalType::bit_width); - Expr::canonical( - crate::expr::ops::EnumLiteral::new_by_index( - ty, - variant_index, - variant.ty.map(|ty| { - Self::to_expr_impl( - ty, - &bits[discriminant_bit_width - ..discriminant_bit_width + data_bit_width], - ) - }), - ) - .to_expr(), - ) - } else { - Expr::canonical(::bits_to_expr(Cow::Borrowed(bits)).cast_bits_to(ty)) - } - } - CanonicalType::Bundle(ty) => Expr::canonical( - crate::expr::ops::BundleLiteral::new( - ty, - ty.fields() - .iter() - .zip(ty.field_offsets().iter()) - .map(|(field, &field_offset)| { - Self::to_expr_impl( - field.ty, - &bits[field_offset..field_offset + field.ty.bit_width()], - ) - }) - .collect(), - ) - .to_expr(), - ), - CanonicalType::AsyncReset(ty) => { - Expr::canonical(Bool::bits_to_expr(Cow::Borrowed(bits)).cast_to(ty)) - } - CanonicalType::SyncReset(ty) => { - Expr::canonical(Bool::bits_to_expr(Cow::Borrowed(bits)).cast_to(ty)) - } - CanonicalType::Reset(_) => panic!( - "can't convert SimValue to Expr -- \ - can't deduce whether reset value should be sync or async" - ), - CanonicalType::Clock(ty) => { - Expr::canonical(Bool::bits_to_expr(Cow::Borrowed(bits)).cast_to(ty)) - } - CanonicalType::PhantomConst(ty) => Expr::canonical(ty.to_expr()), - } - } -} - -impl ToExpr for SimValue { - type Type = T; - - #[track_caller] - fn to_expr(&self) -> Expr { - Expr::from_canonical(SimValue::to_expr_impl(self.ty.canonical(), &self.bits)) - } -} - -impl ToSimValue for SimValue { - #[track_caller] - fn to_sim_value(&self, ty: T) -> SimValue { - assert_eq!(self.ty, ty); - self.clone() - } - - #[track_caller] - fn into_sim_value(self, ty: T) -> SimValue { - assert_eq!(self.ty, ty); - self - } - - #[track_caller] - fn box_into_sim_value(self: Box, ty: T) -> SimValue { - assert_eq!(self.ty, ty); - *self - } -} - -impl ToSimValue for BitVec { - #[track_caller] - fn to_sim_value(&self, ty: T) -> SimValue { - self.clone().into_sim_value(ty) - } - - #[track_caller] - fn into_sim_value(self, ty: T) -> SimValue { - assert_eq!(ty.canonical().bit_width(), self.len()); - SimValue { ty, bits: self } - } - - #[track_caller] - fn box_into_sim_value(self: Box, ty: T) -> SimValue { - Self::into_sim_value(*self, ty) - } -} - -impl ToSimValue for bitvec::boxed::BitBox { - #[track_caller] - fn to_sim_value(&self, ty: T) -> SimValue { - self.clone().into_sim_value(ty) - } - - #[track_caller] - fn into_sim_value(self, ty: T) -> SimValue { - assert_eq!(ty.canonical().bit_width(), self.len()); - SimValue { - ty, - bits: self.into_bitvec(), - } - } - - #[track_caller] - fn box_into_sim_value(self: Box, ty: T) -> SimValue { - Self::into_sim_value(*self, ty) - } -} - -impl ToSimValue for BitSlice { - #[track_caller] - fn to_sim_value(&self, ty: T) -> SimValue { - assert_eq!(ty.canonical().bit_width(), self.len()); - SimValue { - ty, - bits: self.to_bitvec(), - } - } -} - -impl SimValue { - pub fn ty(&self) -> T { - self.ty - } - pub fn bits(&self) -> &BitSlice { - &self.bits - } - pub fn into_bits(self) -> BitVec { - self.bits - } - #[track_caller] - pub fn from_canonical(v: SimValue) -> Self { - Self { - ty: T::from_canonical(v.ty), - bits: v.bits, - } - } - pub fn into_canonical(self) -> SimValue { - SimValue { - ty: self.ty.canonical(), - bits: self.bits, - } - } - #[track_caller] - pub fn from_dyn_int(v: SimValue) -> Self - where - T: IntType, - { - Self { - ty: T::from_dyn_int(v.ty), - bits: v.bits, - } - } - pub fn into_dyn_int(self) -> SimValue - where - T: IntType, - { - SimValue { - ty: self.ty.as_dyn_int(), - bits: self.bits, - } - } - #[track_caller] - pub fn from_bundle(v: SimValue) -> Self - where - T: BundleType, - { - Self { - ty: T::from_canonical(CanonicalType::Bundle(v.ty)), - bits: v.bits, - } - } - pub fn into_bundle(self) -> SimValue - where - T: BundleType, - { - SimValue { - ty: Bundle::from_canonical(self.ty.canonical()), - bits: self.bits, - } - } - #[track_caller] - pub fn from_enum(v: SimValue) -> Self - where - T: EnumType, - { - Self { - ty: T::from_canonical(CanonicalType::Enum(v.ty)), - bits: v.bits, - } - } - pub fn into_enum(self) -> SimValue - where - T: EnumType, - { - SimValue { - ty: Enum::from_canonical(self.ty.canonical()), - bits: self.bits, - } - } -} - -pub trait ToSimValue { - #[track_caller] - fn to_sim_value(&self, ty: T) -> SimValue; - #[track_caller] - fn into_sim_value(self, ty: T) -> SimValue - where - Self: Sized, - { - self.to_sim_value(ty) - } - #[track_caller] - fn box_into_sim_value(self: Box, ty: T) -> SimValue { - self.to_sim_value(ty) - } -} - -impl, T: Type> ToSimValue for &'_ This { - #[track_caller] - fn to_sim_value(&self, ty: T) -> SimValue { - This::to_sim_value(self, ty) - } -} - -impl, T: Type> ToSimValue for &'_ mut This { - #[track_caller] - fn to_sim_value(&self, ty: T) -> SimValue { - This::to_sim_value(self, ty) - } -} - -impl, T: Type> ToSimValue for Box { - #[track_caller] - fn to_sim_value(&self, ty: T) -> SimValue { - This::to_sim_value(self, ty) - } - #[track_caller] - fn into_sim_value(self, ty: T) -> SimValue { - This::box_into_sim_value(self, ty) - } - #[track_caller] - fn box_into_sim_value(self: Box, ty: T) -> SimValue { - This::box_into_sim_value(*self, ty) - } -} - -impl + Send + Sync + 'static, T: Type> ToSimValue - for Interned -{ - #[track_caller] - fn to_sim_value(&self, ty: T) -> SimValue { - This::to_sim_value(self, ty) - } -} - -impl SimValue> { - #[track_caller] - pub fn from_array_elements< - I: IntoIterator, IntoIter: ExactSizeIterator>, - >( - elements: I, - ty: ArrayType, - ) -> Self { - let mut iter = elements.into_iter(); - assert_eq!(iter.len(), ty.len()); - let Some(first) = iter.next() else { - return SimValue { - ty, - bits: BitVec::new(), - }; - }; - let SimValue { - ty: element_ty, - mut bits, - } = first.into_sim_value(ty.element()); - assert_eq!(element_ty, ty.element()); - bits.reserve(ty.type_properties().bit_width - bits.len()); - for element in iter { - let SimValue { - ty: element_ty, - bits: element_bits, - } = element.into_sim_value(ty.element()); - assert_eq!(element_ty, ty.element()); - bits.extend_from_bitslice(&element_bits); - } - SimValue { ty, bits } - } -} - -impl, T: Type> ToSimValue> for [Element] { - #[track_caller] - fn to_sim_value(&self, ty: Array) -> SimValue> { - SimValue::from_array_elements(self, ty) - } - #[track_caller] - fn box_into_sim_value(self: Box, ty: Array) -> SimValue> { - SimValue::from_array_elements(self, ty) - } -} - -impl> ToSimValue for [Element] { - #[track_caller] - fn to_sim_value(&self, ty: CanonicalType) -> SimValue { - SimValue::from_array_elements(self, ::from_canonical(ty)).into_canonical() - } - #[track_caller] - fn box_into_sim_value(self: Box, ty: CanonicalType) -> SimValue { - SimValue::from_array_elements(self, ::from_canonical(ty)).into_canonical() - } -} - -impl, T: Type, const N: usize> ToSimValue> for [Element; N] -where - ConstUsize: KnownSize, -{ - #[track_caller] - fn to_sim_value(&self, ty: Array) -> SimValue> { - SimValue::from_array_elements(self, ty) - } - #[track_caller] - fn into_sim_value(self, ty: Array) -> SimValue> { - SimValue::from_array_elements(self, ty) - } - #[track_caller] - fn box_into_sim_value(self: Box, ty: Array) -> SimValue> { - SimValue::from_array_elements( as From>>::from(self), ty) - } -} - -impl, T: Type, const N: usize> ToSimValue> for [Element; N] -where - ConstUsize: KnownSize, -{ - #[track_caller] - fn to_sim_value(&self, ty: Array) -> SimValue> { - SimValue::from_array_elements(self, ty) - } - #[track_caller] - fn into_sim_value(self, ty: Array) -> SimValue> { - SimValue::from_array_elements(self, ty) - } - #[track_caller] - fn box_into_sim_value(self: Box, ty: Array) -> SimValue> { - SimValue::from_array_elements( as From>>::from(self), ty) - } -} - -impl, const N: usize> ToSimValue - for [Element; N] -{ - #[track_caller] - fn to_sim_value(&self, ty: CanonicalType) -> SimValue { - SimValue::from_array_elements(self, ::from_canonical(ty)).into_canonical() - } - #[track_caller] - fn into_sim_value(self, ty: CanonicalType) -> SimValue { - SimValue::from_array_elements(self, ::from_canonical(ty)).into_canonical() - } - #[track_caller] - fn box_into_sim_value(self: Box, ty: CanonicalType) -> SimValue { - SimValue::from_array_elements( - as From>>::from(self), - ::from_canonical(ty), - ) - .into_canonical() - } -} - -impl, T: Type> ToSimValue> for Vec { - #[track_caller] - fn to_sim_value(&self, ty: Array) -> SimValue> { - SimValue::from_array_elements(self, ty) - } - #[track_caller] - fn into_sim_value(self, ty: Array) -> SimValue> { - SimValue::from_array_elements(self, ty) - } - #[track_caller] - fn box_into_sim_value(self: Box, ty: Array) -> SimValue> { - SimValue::from_array_elements(*self, ty) - } -} - -impl> ToSimValue for Vec { - #[track_caller] - fn to_sim_value(&self, ty: CanonicalType) -> SimValue { - SimValue::from_array_elements(self, ::from_canonical(ty)).into_canonical() - } - #[track_caller] - fn into_sim_value(self, ty: CanonicalType) -> SimValue { - SimValue::from_array_elements(self, ::from_canonical(ty)).into_canonical() - } - #[track_caller] - fn box_into_sim_value(self: Box, ty: CanonicalType) -> SimValue { - SimValue::from_array_elements(*self, ::from_canonical(ty)).into_canonical() - } -} - -impl ToSimValue for Expr { - #[track_caller] - fn to_sim_value(&self, ty: T) -> SimValue { - assert_eq!(Expr::ty(*self), ty); - SimValue { - ty, - bits: self - .to_literal_bits() - .expect("must be a literal expression") - .to_bitvec(), - } - } -} - -macro_rules! impl_to_sim_value_for_bool_like { - ($ty:ident) => { - impl ToSimValue<$ty> for bool { - fn to_sim_value(&self, ty: $ty) -> SimValue<$ty> { - SimValue { - ty, - bits: BitVec::repeat(*self, 1), - } - } - } - }; -} - -impl_to_sim_value_for_bool_like!(Bool); -impl_to_sim_value_for_bool_like!(AsyncReset); -impl_to_sim_value_for_bool_like!(SyncReset); -impl_to_sim_value_for_bool_like!(Reset); -impl_to_sim_value_for_bool_like!(Clock); - -impl ToSimValue for bool { - #[track_caller] - fn to_sim_value(&self, ty: CanonicalType) -> SimValue { - match ty { - CanonicalType::UInt(_) - | CanonicalType::SInt(_) - | CanonicalType::Array(_) - | CanonicalType::Enum(_) - | CanonicalType::Bundle(_) - | CanonicalType::PhantomConst(_) => { - panic!("can't create SimValue from bool: expected value of type: {ty:?}"); - } - CanonicalType::Bool(_) - | CanonicalType::AsyncReset(_) - | CanonicalType::SyncReset(_) - | CanonicalType::Reset(_) - | CanonicalType::Clock(_) => SimValue { - ty, - bits: BitVec::repeat(*self, 1), - }, - } - } -} - -macro_rules! impl_to_sim_value_for_primitive_int { - ($prim:ident) => { - impl ToSimValue<<$prim as ToExpr>::Type> for $prim { - #[track_caller] - fn to_sim_value( - &self, - ty: <$prim as ToExpr>::Type, - ) -> SimValue<<$prim as ToExpr>::Type> { - SimValue { - ty, - bits: <<$prim as ToExpr>::Type as BoolOrIntType>::le_bytes_to_bits_wrapping( - &self.to_le_bytes(), - ty.width(), - ), - } - } - } - - impl ToSimValue<<<$prim as ToExpr>::Type as IntType>::Dyn> for $prim { - #[track_caller] - fn to_sim_value( - &self, - ty: <<$prim as ToExpr>::Type as IntType>::Dyn, - ) -> SimValue<<<$prim as ToExpr>::Type as IntType>::Dyn> { - SimValue { - ty, - bits: <<$prim as ToExpr>::Type as BoolOrIntType>::le_bytes_to_bits_wrapping( - &self.to_le_bytes(), - ty.width(), - ), - } - } - } - - impl ToSimValue for $prim { - #[track_caller] - fn to_sim_value(&self, ty: CanonicalType) -> SimValue { - let ty: <<$prim as ToExpr>::Type as IntType>::Dyn = Type::from_canonical(ty); - self.to_sim_value(ty).into_canonical() - } - } - }; -} - -impl_to_sim_value_for_primitive_int!(u8); -impl_to_sim_value_for_primitive_int!(u16); -impl_to_sim_value_for_primitive_int!(u32); -impl_to_sim_value_for_primitive_int!(u64); -impl_to_sim_value_for_primitive_int!(u128); -impl_to_sim_value_for_primitive_int!(usize); -impl_to_sim_value_for_primitive_int!(i8); -impl_to_sim_value_for_primitive_int!(i16); -impl_to_sim_value_for_primitive_int!(i32); -impl_to_sim_value_for_primitive_int!(i64); -impl_to_sim_value_for_primitive_int!(i128); -impl_to_sim_value_for_primitive_int!(isize); - -macro_rules! impl_to_sim_value_for_int_value { - ($IntValue:ident, $Int:ident, $IntType:ident) => { - impl ToSimValue<$IntType> for $IntValue { - fn to_sim_value(&self, ty: $IntType) -> SimValue<$IntType> { - self.bits().to_bitvec().into_sim_value(ty) - } - - fn into_sim_value(self, ty: $IntType) -> SimValue<$IntType> { - Arc::try_unwrap(self.into_bits()) - .unwrap_or_else(|v: Arc| v.to_bitvec()) - .into_sim_value(ty) - } - - fn box_into_sim_value( - self: Box, - ty: $IntType, - ) -> SimValue<$IntType> { - Self::into_sim_value(*self, ty) - } - } - - impl ToSimValue<$Int> for $IntValue { - fn to_sim_value(&self, ty: $Int) -> SimValue<$Int> { - self.bits().to_bitvec().into_sim_value(ty) - } - - fn into_sim_value(self, ty: $Int) -> SimValue<$Int> { - Arc::try_unwrap(self.into_bits()) - .unwrap_or_else(|v: Arc| v.to_bitvec()) - .into_sim_value(ty) - } - - fn box_into_sim_value(self: Box, ty: $Int) -> SimValue<$Int> { - Self::into_sim_value(*self, ty) - } - } - - impl ToSimValue for $IntValue { - #[track_caller] - fn to_sim_value(&self, ty: CanonicalType) -> SimValue { - ToSimValue::<$Int>::to_sim_value(self, $Int::from_canonical(ty)).into_canonical() - } - - #[track_caller] - fn into_sim_value(self, ty: CanonicalType) -> SimValue { - ToSimValue::<$Int>::into_sim_value(self, $Int::from_canonical(ty)).into_canonical() - } - - #[track_caller] - fn box_into_sim_value(self: Box, ty: CanonicalType) -> SimValue { - Self::into_sim_value(*self, ty) - } - } - }; -} - -impl_to_sim_value_for_int_value!(UIntValue, UInt, UIntType); -impl_to_sim_value_for_int_value!(SIntValue, SInt, SIntType); - #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] enum MaybeNeedsSettle { NeedsSettle(S), @@ -7024,19 +6397,19 @@ impl MaybeNeedsSettleFn<&'_ mut interpreter::State> for ReadBo struct ReadFn { compiled_value: CompiledValue, io: Expr, - bits: BitVec, } impl MaybeNeedsSettleFn<&'_ mut interpreter::State> for ReadFn { type Output = SimValue; fn call(self, state: &mut interpreter::State) -> Self::Output { - let Self { - compiled_value, + let Self { compiled_value, io } = self; + SimulationImpl::read_no_settle_helper( + state, io, - bits, - } = self; - SimulationImpl::read_no_settle_helper(state, io, compiled_value, bits) + compiled_value, + UIntValue::new(Arc::new(BitVec::repeat(false, Expr::ty(io).bit_width()))), + ) } } @@ -7390,7 +6763,7 @@ impl SimulationImpl { (WaitTarget::Instant(instant), None) => Some(instant), (WaitTarget::Instant(instant), Some(retval)) => Some(instant.min(retval)), (WaitTarget::Change { key, value }, retval) => { - if Self::value_changed(&mut self.state, key, &value.bits) { + if Self::value_changed(&mut self.state, key, SimValue::bits(value).bits()) { Some(self.instant) } else { retval @@ -7688,7 +7061,7 @@ impl SimulationImpl { } } #[track_caller] - fn read_write_sim_value_helper( + fn read_write_sim_value_helper( state: &mut interpreter::State, compiled_value: CompiledValue, start_bit_index: usize, @@ -7783,15 +7156,13 @@ impl SimulationImpl { state: &mut interpreter::State, io: Expr, compiled_value: CompiledValue, - mut bits: BitVec, + mut bits: UIntValue, ) -> SimValue { - bits.clear(); - bits.resize(compiled_value.layout.ty.bit_width(), false); SimulationImpl::read_write_sim_value_helper( state, compiled_value, 0, - &mut bits, + bits.bits_mut(), |_signed, bit_range, bits, value| { ::copy_bits_from_bigint_wrapping(value, &mut bits[bit_range]); }, @@ -7802,10 +7173,7 @@ impl SimulationImpl { bits[bit_range].clone_from_bitslice(bitslice); }, ); - SimValue { - ty: Expr::ty(io), - bits, - } + SimValue::from_bits(Expr::ty(io), bits) } /// doesn't modify `bits` fn value_changed( @@ -7847,11 +7215,7 @@ impl SimulationImpl { ) { let compiled_value = self.get_module(which_module).read_helper(io, which_module); let value = compiled_value - .map(|compiled_value| ReadFn { - compiled_value, - io, - bits: BitVec::new(), - }) + .map(|compiled_value| ReadFn { compiled_value, io }) .apply_no_settle(&mut self.state); let (MaybeNeedsSettle::NeedsSettle(compiled_value) | MaybeNeedsSettle::NoSettleNeeded(compiled_value)) = compiled_value; @@ -7868,12 +7232,12 @@ impl SimulationImpl { .get_module_mut(which_module) .write_helper(io, which_module); self.state_ready_to_run = true; - assert_eq!(Expr::ty(io), value.ty()); + assert_eq!(Expr::ty(io), SimValue::ty(value)); Self::read_write_sim_value_helper( &mut self.state, compiled_value, 0, - &mut value.bits(), + &mut value.bits().bits(), |signed, bit_range, bits, value| { if signed { *value = SInt::bits_to_bigint(&bits[bit_range]); @@ -8167,10 +7531,10 @@ macro_rules! impl_simulation_methods { SimValue::from_canonical($self.settle_if_needed(retval)$(.$await)?) } $(#[$track_caller])? - pub $($async)? fn write>(&mut $self, io: Expr, value: V) { + pub $($async)? fn write>(&mut $self, io: Expr, value: V) { $self.sim_impl.borrow_mut().write( Expr::canonical(io), - &value.into_sim_value(Expr::ty(io)).into_canonical(), + &SimValue::into_canonical(value.into_sim_value(Expr::ty(io))), $which_module, ); } diff --git a/crates/fayalite/src/sim/value.rs b/crates/fayalite/src/sim/value.rs new file mode 100644 index 0000000..3e45016 --- /dev/null +++ b/crates/fayalite/src/sim/value.rs @@ -0,0 +1,665 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// See Notices.txt for copyright information + +use crate::{ + array::{Array, ArrayType}, + bundle::{Bundle, BundleType}, + clock::Clock, + enum_::{Enum, EnumType}, + expr::{CastBitsTo, Expr, ToExpr}, + int::{Bool, IntType, KnownSize, SInt, SIntType, SIntValue, Size, UInt, UIntType, UIntValue}, + reset::{AsyncReset, Reset, SyncReset}, + ty::{CanonicalType, Type}, + util::{ + alternating_cell::{AlternatingCell, AlternatingCellMethods}, + ConstUsize, + }, +}; +use bitvec::{slice::BitSlice, vec::BitVec}; +use std::{ + fmt, + ops::{Deref, DerefMut}, + sync::Arc, +}; + +#[derive(Copy, Clone, Eq, PartialEq)] +enum ValidFlags { + BothValid = 0, + OnlyValueValid = 1, + OnlyBitsValid = 2, +} + +#[derive(Clone)] +struct SimValueInner { + value: T::SimValue, + bits: UIntValue, + valid_flags: ValidFlags, + ty: T, +} + +impl SimValueInner { + fn fill_bits(&mut self) { + match self.valid_flags { + ValidFlags::BothValid | ValidFlags::OnlyBitsValid => {} + ValidFlags::OnlyValueValid => { + self.ty.sim_value_to_bits(&self.value, self.bits.bits_mut()); + self.valid_flags = ValidFlags::BothValid; + } + } + } + fn into_bits(mut self) -> UIntValue { + self.fill_bits(); + self.bits + } + fn bits_mut(&mut self) -> &mut UIntValue { + self.fill_bits(); + self.valid_flags = ValidFlags::OnlyBitsValid; + &mut self.bits + } + fn fill_value(&mut self) { + match self.valid_flags { + ValidFlags::BothValid | ValidFlags::OnlyValueValid => {} + ValidFlags::OnlyBitsValid => { + self.ty + .sim_value_clone_from_bits(&mut self.value, self.bits.bits()); + self.valid_flags = ValidFlags::BothValid; + } + } + } + fn into_value(mut self) -> T::SimValue { + self.fill_value(); + self.value + } + fn value_mut(&mut self) -> &mut T::SimValue { + self.fill_value(); + self.valid_flags = ValidFlags::OnlyValueValid; + &mut self.value + } +} + +impl AlternatingCellMethods for SimValueInner { + fn unique_to_shared(&mut self) { + match self.valid_flags { + ValidFlags::BothValid => return, + ValidFlags::OnlyValueValid => { + self.ty.sim_value_to_bits(&self.value, self.bits.bits_mut()) + } + ValidFlags::OnlyBitsValid => self + .ty + .sim_value_clone_from_bits(&mut self.value, self.bits.bits()), + } + self.valid_flags = ValidFlags::BothValid; + } + + fn shared_to_unique(&mut self) {} +} + +pub struct SimValue { + inner: AlternatingCell>, +} + +impl Clone for SimValue { + fn clone(&self) -> Self { + Self { + inner: AlternatingCell::new_unique(self.inner.share().clone()), + } + } +} + +impl SimValue { + #[track_caller] + pub fn from_bits(ty: T, bits: UIntValue) -> Self { + assert_eq!(ty.canonical().bit_width(), bits.width()); + let inner = SimValueInner { + value: ty.sim_value_from_bits(bits.bits()), + bits, + valid_flags: ValidFlags::BothValid, + ty, + }; + Self { + inner: AlternatingCell::new_shared(inner), + } + } + #[track_caller] + pub fn from_bitslice(ty: T, bits: &BitSlice) -> Self { + Self::from_bits(ty, UIntValue::new(Arc::new(bits.to_bitvec()))) + } + pub fn from_value(ty: T, value: T::SimValue) -> Self { + let inner = SimValueInner { + bits: UIntValue::new_dyn(Arc::new(BitVec::repeat(false, ty.canonical().bit_width()))), + value, + valid_flags: ValidFlags::OnlyValueValid, + ty, + }; + Self { + inner: AlternatingCell::new_unique(inner), + } + } + pub fn ty(this: &Self) -> T { + this.inner.share().ty + } + pub fn into_bits(this: Self) -> UIntValue { + this.inner.into_inner().into_bits() + } + pub fn into_ty_and_bits(this: Self) -> (T, UIntValue) { + let inner = this.inner.into_inner(); + (inner.ty, inner.into_bits()) + } + pub fn bits(this: &Self) -> &UIntValue { + &this.inner.share().bits + } + pub fn bits_mut(this: &mut Self) -> &mut UIntValue { + this.inner.unique().bits_mut() + } + pub fn into_value(this: Self) -> T::SimValue { + this.inner.into_inner().into_value() + } + pub fn value(this: &Self) -> &T::SimValue { + &this.inner.share().value + } + pub fn value_mut(this: &mut Self) -> &mut T::SimValue { + this.inner.unique().value_mut() + } + #[track_caller] + pub fn from_canonical(v: SimValue) -> Self { + let (ty, bits) = SimValue::into_ty_and_bits(v); + Self::from_bits(T::from_canonical(ty), bits) + } + pub fn into_canonical(this: Self) -> SimValue { + let (ty, bits) = Self::into_ty_and_bits(this); + SimValue::from_bits(ty.canonical(), bits) + } + pub fn canonical(this: &Self) -> SimValue { + SimValue::from_bits(Self::ty(this).canonical(), Self::bits(this).clone()) + } + #[track_caller] + pub fn from_dyn_int(v: SimValue) -> Self + where + T: IntType, + { + let (ty, bits) = SimValue::into_ty_and_bits(v); + SimValue::from_bits(T::from_dyn_int(ty), bits) + } + pub fn into_dyn_int(this: Self) -> SimValue + where + T: IntType, + { + let (ty, bits) = Self::into_ty_and_bits(this); + SimValue::from_bits(ty.as_dyn_int(), bits) + } + pub fn to_dyn_int(this: &Self) -> SimValue + where + T: IntType, + { + SimValue::from_bits(Self::ty(this).as_dyn_int(), Self::bits(&this).clone()) + } + #[track_caller] + pub fn from_bundle(v: SimValue) -> Self + where + T: BundleType, + { + let (ty, bits) = SimValue::into_ty_and_bits(v); + SimValue::from_bits(T::from_canonical(CanonicalType::Bundle(ty)), bits) + } + pub fn into_bundle(this: Self) -> SimValue + where + T: BundleType, + { + let (ty, bits) = Self::into_ty_and_bits(this); + SimValue::from_bits(Bundle::from_canonical(ty.canonical()), bits) + } + pub fn to_bundle(this: &Self) -> SimValue + where + T: BundleType, + { + SimValue::from_bits( + Bundle::from_canonical(Self::ty(this).canonical()), + Self::bits(&this).clone(), + ) + } + #[track_caller] + pub fn from_enum(v: SimValue) -> Self + where + T: EnumType, + { + let (ty, bits) = SimValue::into_ty_and_bits(v); + SimValue::from_bits(T::from_canonical(CanonicalType::Enum(ty)), bits) + } + pub fn into_enum(this: Self) -> SimValue + where + T: EnumType, + { + let (ty, bits) = Self::into_ty_and_bits(this); + SimValue::from_bits(Enum::from_canonical(ty.canonical()), bits) + } + pub fn to_enum(this: &Self) -> SimValue + where + T: EnumType, + { + SimValue::from_bits( + Enum::from_canonical(Self::ty(this).canonical()), + Self::bits(&this).clone(), + ) + } +} + +impl Deref for SimValue { + type Target = T::SimValue; + + fn deref(&self) -> &Self::Target { + Self::value(self) + } +} + +impl DerefMut for SimValue { + fn deref_mut(&mut self) -> &mut Self::Target { + Self::value_mut(self) + } +} + +impl fmt::Debug for SimValue { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let inner = self.inner.share(); + f.debug_struct("SimValue") + .field("ty", &inner.ty) + .field("value", &inner.value) + .finish() + } +} + +impl ToExpr for SimValue { + type Type = T; + + #[track_caller] + fn to_expr(&self) -> Expr { + let inner = self.inner.share(); + inner.bits.cast_bits_to(inner.ty) + } +} + +pub trait SimValuePartialEq: Type { + #[track_caller] + fn sim_value_eq(this: &SimValue, other: &SimValue) -> bool; +} + +impl, U: Type> PartialEq> for SimValue { + #[track_caller] + fn eq(&self, other: &SimValue) -> bool { + T::sim_value_eq(self, other) + } +} + +impl SimValuePartialEq> for UIntType { + fn sim_value_eq(this: &SimValue, other: &SimValue>) -> bool { + **this == **other + } +} + +impl SimValuePartialEq> for SIntType { + fn sim_value_eq(this: &SimValue, other: &SimValue>) -> bool { + **this == **other + } +} + +impl SimValuePartialEq for Bool { + fn sim_value_eq(this: &SimValue, other: &SimValue) -> bool { + **this == **other + } +} + +pub trait ToSimValue { + #[track_caller] + fn to_sim_value(&self, ty: T) -> SimValue; + #[track_caller] + fn into_sim_value(self, ty: T) -> SimValue + where + Self: Sized, + { + self.to_sim_value(ty) + } + #[track_caller] + fn arc_into_sim_value(self: Arc, ty: T) -> SimValue { + self.to_sim_value(ty) + } + #[track_caller] + fn arc_to_sim_value(self: &Arc, ty: T) -> SimValue { + self.to_sim_value(ty) + } +} + +impl ToSimValue for SimValue { + #[track_caller] + fn to_sim_value(&self, ty: T) -> SimValue { + assert_eq!(SimValue::ty(self), ty); + self.clone() + } + + #[track_caller] + fn into_sim_value(self, ty: T) -> SimValue { + assert_eq!(SimValue::ty(&self), ty); + self + } +} + +impl ToSimValue for BitVec { + #[track_caller] + fn to_sim_value(&self, ty: T) -> SimValue { + self.clone().into_sim_value(ty) + } + + #[track_caller] + fn into_sim_value(self, ty: T) -> SimValue { + Arc::new(self).arc_into_sim_value(ty) + } + + #[track_caller] + fn arc_into_sim_value(self: Arc, ty: T) -> SimValue { + SimValue::from_bits(ty, UIntValue::new_dyn(self)) + } + + #[track_caller] + fn arc_to_sim_value(self: &Arc, ty: T) -> SimValue { + SimValue::from_bits(ty, UIntValue::new_dyn(self.clone())) + } +} + +impl ToSimValue for bitvec::boxed::BitBox { + #[track_caller] + fn to_sim_value(&self, ty: T) -> SimValue { + self.clone().into_sim_value(ty) + } + + #[track_caller] + fn into_sim_value(self, ty: T) -> SimValue { + self.into_bitvec().into_sim_value(ty) + } +} + +impl ToSimValue for BitSlice { + #[track_caller] + fn to_sim_value(&self, ty: T) -> SimValue { + self.to_bitvec().into_sim_value(ty) + } +} + +impl, T: Type> ToSimValue for &'_ This { + fn to_sim_value(&self, ty: T) -> SimValue { + This::to_sim_value(self, ty) + } +} + +impl, T: Type> ToSimValue for &'_ mut This { + fn to_sim_value(&self, ty: T) -> SimValue { + This::to_sim_value(self, ty) + } +} + +impl, T: Type> ToSimValue for Arc { + fn to_sim_value(&self, ty: T) -> SimValue { + This::arc_to_sim_value(self, ty) + } + fn into_sim_value(self, ty: T) -> SimValue { + This::arc_into_sim_value(self, ty) + } +} + +impl + Send + Sync + 'static, T: Type> ToSimValue + for crate::intern::Interned +{ + fn to_sim_value(&self, ty: T) -> SimValue { + This::to_sim_value(self, ty) + } +} + +impl, T: Type> ToSimValue for Box { + fn to_sim_value(&self, ty: T) -> SimValue { + This::to_sim_value(self, ty) + } + fn into_sim_value(self, ty: T) -> SimValue { + This::into_sim_value(*self, ty) + } +} + +impl SimValue> { + #[track_caller] + pub fn from_array_elements>>( + ty: ArrayType, + elements: I, + ) -> Self { + let element_ty = ty.element(); + let elements = Vec::from_iter( + elements + .into_iter() + .map(|element| element.into_sim_value(element_ty)), + ); + assert_eq!(elements.len(), ty.len()); + SimValue::from_value(ty, elements.try_into().ok().expect("already checked len")) + } +} + +impl, T: Type> ToSimValue> for [Element] { + #[track_caller] + fn to_sim_value(&self, ty: Array) -> SimValue> { + SimValue::from_array_elements(ty, self) + } +} + +impl> ToSimValue for [Element] { + #[track_caller] + fn to_sim_value(&self, ty: CanonicalType) -> SimValue { + SimValue::into_canonical(SimValue::from_array_elements( + ::from_canonical(ty), + self, + )) + } +} + +impl, T: Type, const N: usize> ToSimValue> for [Element; N] +where + ConstUsize: KnownSize, +{ + #[track_caller] + fn to_sim_value(&self, ty: Array) -> SimValue> { + SimValue::from_array_elements(ty, self) + } + #[track_caller] + fn into_sim_value(self, ty: Array) -> SimValue> { + SimValue::from_array_elements(ty, self) + } +} + +impl, T: Type, const N: usize> ToSimValue> for [Element; N] { + #[track_caller] + fn to_sim_value(&self, ty: Array) -> SimValue> { + SimValue::from_array_elements(ty, self) + } + #[track_caller] + fn into_sim_value(self, ty: Array) -> SimValue> { + SimValue::from_array_elements(ty, self) + } +} + +impl, const N: usize> ToSimValue + for [Element; N] +{ + #[track_caller] + fn to_sim_value(&self, ty: CanonicalType) -> SimValue { + SimValue::into_canonical(SimValue::from_array_elements( + ::from_canonical(ty), + self, + )) + } + #[track_caller] + fn into_sim_value(self, ty: CanonicalType) -> SimValue { + SimValue::into_canonical(SimValue::from_array_elements( + ::from_canonical(ty), + self, + )) + } +} + +impl, T: Type> ToSimValue> for Vec { + #[track_caller] + fn to_sim_value(&self, ty: Array) -> SimValue> { + SimValue::from_array_elements(ty, self) + } + #[track_caller] + fn into_sim_value(self, ty: Array) -> SimValue> { + SimValue::from_array_elements(ty, self) + } +} + +impl> ToSimValue for Vec { + #[track_caller] + fn to_sim_value(&self, ty: CanonicalType) -> SimValue { + SimValue::into_canonical(SimValue::from_array_elements( + ::from_canonical(ty), + self, + )) + } + #[track_caller] + fn into_sim_value(self, ty: CanonicalType) -> SimValue { + SimValue::into_canonical(SimValue::from_array_elements( + ::from_canonical(ty), + self, + )) + } +} + +impl ToSimValue for Expr { + #[track_caller] + fn to_sim_value(&self, ty: T) -> SimValue { + assert_eq!(Expr::ty(*self), ty); + SimValue::from_bitslice( + ty, + &crate::expr::ToLiteralBits::to_literal_bits(self) + .expect("must be a literal expression"), + ) + } +} + +macro_rules! impl_to_sim_value_for_bool_like { + ($ty:ident) => { + impl ToSimValue<$ty> for bool { + fn to_sim_value(&self, ty: $ty) -> SimValue<$ty> { + SimValue::from_value(ty, *self) + } + } + }; +} + +impl_to_sim_value_for_bool_like!(Bool); +impl_to_sim_value_for_bool_like!(AsyncReset); +impl_to_sim_value_for_bool_like!(SyncReset); +impl_to_sim_value_for_bool_like!(Reset); +impl_to_sim_value_for_bool_like!(Clock); + +impl ToSimValue for bool { + #[track_caller] + fn to_sim_value(&self, ty: CanonicalType) -> SimValue { + match ty { + CanonicalType::UInt(_) + | CanonicalType::SInt(_) + | CanonicalType::Array(_) + | CanonicalType::Enum(_) + | CanonicalType::Bundle(_) + | CanonicalType::PhantomConst(_) => { + panic!("can't create SimValue from bool: expected value of type: {ty:?}"); + } + CanonicalType::Bool(_) + | CanonicalType::AsyncReset(_) + | CanonicalType::SyncReset(_) + | CanonicalType::Reset(_) + | CanonicalType::Clock(_) => { + SimValue::from_bits(ty, UIntValue::new(Arc::new(BitVec::repeat(*self, 1)))) + } + } + } +} + +macro_rules! impl_to_sim_value_for_primitive_int { + ($prim:ident) => { + impl ToSimValue<<$prim as ToExpr>::Type> for $prim { + #[track_caller] + fn to_sim_value( + &self, + ty: <$prim as ToExpr>::Type, + ) -> SimValue<<$prim as ToExpr>::Type> { + SimValue::from_value(ty, (*self).into()) + } + } + + impl ToSimValue<<<$prim as ToExpr>::Type as IntType>::Dyn> for $prim { + #[track_caller] + fn to_sim_value( + &self, + ty: <<$prim as ToExpr>::Type as IntType>::Dyn, + ) -> SimValue<<<$prim as ToExpr>::Type as IntType>::Dyn> { + SimValue::from_value( + ty, + <<$prim as ToExpr>::Type as Type>::SimValue::from(*self).as_dyn_int(), + ) + } + } + + impl ToSimValue for $prim { + #[track_caller] + fn to_sim_value(&self, ty: CanonicalType) -> SimValue { + let ty: <<$prim as ToExpr>::Type as IntType>::Dyn = Type::from_canonical(ty); + SimValue::into_canonical(self.to_sim_value(ty)) + } + } + }; +} + +impl_to_sim_value_for_primitive_int!(u8); +impl_to_sim_value_for_primitive_int!(u16); +impl_to_sim_value_for_primitive_int!(u32); +impl_to_sim_value_for_primitive_int!(u64); +impl_to_sim_value_for_primitive_int!(u128); +impl_to_sim_value_for_primitive_int!(usize); +impl_to_sim_value_for_primitive_int!(i8); +impl_to_sim_value_for_primitive_int!(i16); +impl_to_sim_value_for_primitive_int!(i32); +impl_to_sim_value_for_primitive_int!(i64); +impl_to_sim_value_for_primitive_int!(i128); +impl_to_sim_value_for_primitive_int!(isize); + +macro_rules! impl_to_sim_value_for_int_value { + ($IntValue:ident, $Int:ident, $IntType:ident) => { + impl ToSimValue<$IntType> for $IntValue { + fn to_sim_value(&self, ty: $IntType) -> SimValue<$IntType> { + self.bits().to_sim_value(ty) + } + + fn into_sim_value(self, ty: $IntType) -> SimValue<$IntType> { + self.into_bits().into_sim_value(ty) + } + } + + impl ToSimValue<$Int> for $IntValue { + fn to_sim_value(&self, ty: $Int) -> SimValue<$Int> { + self.bits().to_sim_value(ty) + } + + fn into_sim_value(self, ty: $Int) -> SimValue<$Int> { + self.into_bits().into_sim_value(ty) + } + } + + impl ToSimValue for $IntValue { + #[track_caller] + fn to_sim_value(&self, ty: CanonicalType) -> SimValue { + SimValue::into_canonical(self.to_sim_value($Int::from_canonical(ty))) + } + + #[track_caller] + fn into_sim_value(self, ty: CanonicalType) -> SimValue { + SimValue::into_canonical(self.into_sim_value($Int::from_canonical(ty))) + } + } + }; +} + +impl_to_sim_value_for_int_value!(UIntValue, UInt, UIntType); +impl_to_sim_value_for_int_value!(SIntValue, SInt, SIntType); diff --git a/crates/fayalite/src/ty.rs b/crates/fayalite/src/ty.rs index 2786782..cd26c9b 100644 --- a/crates/fayalite/src/ty.rs +++ b/crates/fayalite/src/ty.rs @@ -7,14 +7,15 @@ use crate::{ clock::Clock, enum_::Enum, expr::Expr, - int::{Bool, SInt, UInt}, + int::{Bool, SInt, UInt, UIntValue}, intern::{Intern, Interned}, phantom_const::PhantomConst, reset::{AsyncReset, Reset, SyncReset}, source_location::SourceLocation, util::ConstUsize, }; -use std::{fmt, hash::Hash, iter::FusedIterator, ops::Index}; +use bitvec::slice::BitSlice; +use std::{fmt, hash::Hash, iter::FusedIterator, ops::Index, sync::Arc}; #[derive(Copy, Clone, Hash, PartialEq, Eq, Debug)] #[non_exhaustive] @@ -268,6 +269,7 @@ pub trait Type: { type BaseType: BaseType; type MaskType: Type; + type SimValue: fmt::Debug + Clone + 'static; type MatchVariant: 'static + Send + Sync; type MatchActiveScope; type MatchVariantAndInactiveScope: MatchVariantAndInactiveScope< @@ -285,6 +287,9 @@ pub trait Type: fn canonical(&self) -> CanonicalType; fn from_canonical(canonical_type: CanonicalType) -> Self; fn source_location() -> SourceLocation; + fn sim_value_from_bits(&self, bits: &BitSlice) -> Self::SimValue; + fn sim_value_clone_from_bits(&self, value: &mut Self::SimValue, bits: &BitSlice); + fn sim_value_to_bits(&self, value: &Self::SimValue, bits: &mut BitSlice); } pub trait BaseType: Type + sealed::BaseTypeSealed + Into {} @@ -314,6 +319,7 @@ pub trait TypeWithDeref: Type { impl Type for CanonicalType { type BaseType = CanonicalType; type MaskType = CanonicalType; + type SimValue = OpaqueSimValue; impl_match_variant_as_self!(); fn mask_type(&self) -> Self::MaskType { match self { @@ -339,6 +345,48 @@ impl Type for CanonicalType { fn source_location() -> SourceLocation { SourceLocation::builtin() } + fn sim_value_from_bits(&self, bits: &BitSlice) -> Self::SimValue { + assert_eq!(bits.len(), self.bit_width()); + OpaqueSimValue::from_bitslice(bits) + } + fn sim_value_clone_from_bits(&self, value: &mut Self::SimValue, bits: &BitSlice) { + assert_eq!(bits.len(), self.bit_width()); + assert_eq!(value.bit_width(), self.bit_width()); + value.bits_mut().bits_mut().copy_from_bitslice(bits); + } + fn sim_value_to_bits(&self, value: &Self::SimValue, bits: &mut BitSlice) { + assert_eq!(bits.len(), self.bit_width()); + assert_eq!(value.bit_width(), self.bit_width()); + bits.copy_from_bitslice(value.bits().bits()); + } +} + +#[derive(Clone, PartialEq, Eq, Hash, Debug)] +pub struct OpaqueSimValue { + bits: UIntValue, +} + +impl OpaqueSimValue { + pub fn bit_width(&self) -> usize { + self.bits.width() + } + pub fn bits(&self) -> &UIntValue { + &self.bits + } + pub fn bits_mut(&mut self) -> &mut UIntValue { + &mut self.bits + } + pub fn into_bits(self) -> UIntValue { + self.bits + } + pub fn from_bits(bits: UIntValue) -> Self { + Self { bits } + } + pub fn from_bitslice(v: &BitSlice) -> Self { + Self { + bits: UIntValue::new(Arc::new(v.to_bitvec())), + } + } } pub trait StaticType: Type { diff --git a/crates/fayalite/src/util.rs b/crates/fayalite/src/util.rs index 66fc921..804ff19 100644 --- a/crates/fayalite/src/util.rs +++ b/crates/fayalite/src/util.rs @@ -1,6 +1,7 @@ // SPDX-License-Identifier: LGPL-3.0-or-later // See Notices.txt for copyright information +pub(crate) mod alternating_cell; mod const_bool; mod const_cmp; mod const_usize; diff --git a/crates/fayalite/src/util/alternating_cell.rs b/crates/fayalite/src/util/alternating_cell.rs new file mode 100644 index 0000000..17e06a6 --- /dev/null +++ b/crates/fayalite/src/util/alternating_cell.rs @@ -0,0 +1,122 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// See Notices.txt for copyright information + +use crate::util::DebugAsDisplay; +use std::{ + cell::{Cell, UnsafeCell}, + fmt, +}; + +pub(crate) trait AlternatingCellMethods { + fn unique_to_shared(&mut self); + fn shared_to_unique(&mut self); +} + +#[derive(Copy, Clone, Debug)] +enum State { + Unique, + Shared, + Locked, +} + +pub(crate) struct AlternatingCell { + state: Cell, + value: UnsafeCell, +} + +impl fmt::Debug for AlternatingCell { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_tuple("AlternatingCell") + .field( + self.try_share() + .as_ref() + .map(|v| -> &dyn fmt::Debug { v }) + .unwrap_or(&DebugAsDisplay("<...>")), + ) + .finish() + } +} + +impl AlternatingCell { + pub(crate) const fn new_shared(value: T) -> Self + where + T: Sized, + { + Self { + state: Cell::new(State::Shared), + value: UnsafeCell::new(value), + } + } + pub(crate) const fn new_unique(value: T) -> Self + where + T: Sized, + { + Self { + state: Cell::new(State::Unique), + value: UnsafeCell::new(value), + } + } + pub(crate) fn is_unique(&self) -> bool { + matches!(self.state.get(), State::Unique) + } + pub(crate) fn is_shared(&self) -> bool { + matches!(self.state.get(), State::Shared) + } + pub(crate) fn into_inner(self) -> T + where + T: Sized, + { + self.value.into_inner() + } + pub(crate) fn try_share(&self) -> Option<&T> + where + T: AlternatingCellMethods, + { + match self.state.get() { + State::Shared => {} + State::Unique => { + struct Locked<'a>(&'a Cell); + impl Drop for Locked<'_> { + fn drop(&mut self) { + self.0.set(State::Shared); + } + } + self.state.set(State::Locked); + let lock = Locked(&self.state); + // Safety: state is Locked, so nothing else will + // access value while calling unique_to_shared. + unsafe { &mut *self.value.get() }.unique_to_shared(); + drop(lock); + } + State::Locked => return None, + } + + // Safety: state is Shared so nothing will create any mutable + // references until the returned reference's lifetime expires. + Some(unsafe { &*self.value.get() }) + } + #[track_caller] + pub(crate) fn share(&self) -> &T + where + T: AlternatingCellMethods, + { + let Some(retval) = self.try_share() else { + panic!("`share` called recursively"); + }; + retval + } + pub(crate) fn unique(&mut self) -> &mut T + where + T: AlternatingCellMethods, + { + match self.state.get() { + State::Shared => { + self.state.set(State::Unique); + self.value.get_mut().shared_to_unique(); + } + State::Unique => {} + State::Locked => unreachable!(), + } + self.value.get_mut() + } +} diff --git a/crates/fayalite/tests/sim.rs b/crates/fayalite/tests/sim.rs index 450be54..eb5c79e 100644 --- a/crates/fayalite/tests/sim.rs +++ b/crates/fayalite/tests/sim.rs @@ -3,10 +3,16 @@ use fayalite::{ int::UIntValue, + memory::{ReadStruct, ReadWriteStruct, WriteStruct}, module::{instance_with_loc, reg_builder_with_loc}, prelude::*, reset::ResetType, - sim::{time::SimDuration, vcd::VcdWriterDecls, Simulation, ToSimValue}, + sim::{ + time::SimDuration, + value::{SimValue, ToSimValue}, + vcd::VcdWriterDecls, + Simulation, + }, ty::StaticType, util::RcWriter, }; @@ -396,32 +402,14 @@ fn test_enums() { sim.write_reset(sim.io().cd.rst, false); sim.advance_time(SimDuration::from_nanos(900)); type BOutTy = HdlOption<(UInt<1>, Bool)>; - #[derive(Debug)] + #[derive(Debug, PartialEq)] struct IO { en: bool, which_in: u8, data_in: u8, which_out: u8, data_out: u8, - b_out: Expr, - } - impl PartialEq for IO { - fn eq(&self, other: &Self) -> bool { - let Self { - en, - which_in, - data_in, - which_out, - data_out, - b_out, - } = *self; - en == other.en - && which_in == other.which_in - && data_in == other.data_in - && which_out == other.which_out - && data_out == other.data_out - && b_out.to_sim_value(BOutTy::TYPE) == other.b_out.to_sim_value(BOutTy::TYPE) - } + b_out: SimValue, } let io_cycles = [ IO { @@ -430,7 +418,7 @@ fn test_enums() { data_in: 0, which_out: 0, data_out: 0, - b_out: HdlNone(), + b_out: HdlNone().to_sim_value(StaticType::TYPE), }, IO { en: true, @@ -438,7 +426,7 @@ fn test_enums() { data_in: 0, which_out: 0, data_out: 0, - b_out: HdlNone(), + b_out: HdlNone().to_sim_value(StaticType::TYPE), }, IO { en: false, @@ -446,7 +434,7 @@ fn test_enums() { data_in: 0, which_out: 1, data_out: 0, - b_out: HdlSome((0_hdl_u1, false)), + b_out: HdlSome((0_hdl_u1, false)).to_sim_value(StaticType::TYPE), }, IO { en: true, @@ -454,7 +442,7 @@ fn test_enums() { data_in: 0xF, which_out: 1, data_out: 0, - b_out: HdlSome((0_hdl_u1, false)), + b_out: HdlSome((0_hdl_u1, false)).to_sim_value(StaticType::TYPE), }, IO { en: true, @@ -462,7 +450,7 @@ fn test_enums() { data_in: 0xF, which_out: 1, data_out: 0x3, - b_out: HdlSome((1_hdl_u1, true)), + b_out: HdlSome((1_hdl_u1, true)).to_sim_value(StaticType::TYPE), }, IO { en: true, @@ -470,7 +458,7 @@ fn test_enums() { data_in: 0xF, which_out: 1, data_out: 0x3, - b_out: HdlSome((1_hdl_u1, true)), + b_out: HdlSome((1_hdl_u1, true)).to_sim_value(StaticType::TYPE), }, IO { en: true, @@ -478,7 +466,7 @@ fn test_enums() { data_in: 0xF, which_out: 2, data_out: 0xF, - b_out: HdlNone(), + b_out: HdlNone().to_sim_value(StaticType::TYPE), }, ]; for ( @@ -510,7 +498,7 @@ fn test_enums() { .to_bigint() .try_into() .expect("known to be in range"), - b_out: sim.read(sim.io().b_out).to_expr(), + b_out: sim.read(sim.io().b_out), }; assert_eq!( expected, @@ -539,9 +527,9 @@ fn test_enums() { #[hdl_module(outline_generated)] pub fn memories() { #[hdl] - let r: fayalite::memory::ReadStruct<(UInt<8>, SInt<8>), ConstUsize<4>> = m.input(); + let r: ReadStruct<(UInt<8>, SInt<8>), ConstUsize<4>> = m.input(); #[hdl] - let w: fayalite::memory::WriteStruct<(UInt<8>, SInt<8>), ConstUsize<4>> = m.input(); + let w: WriteStruct<(UInt<8>, SInt<8>), ConstUsize<4>> = m.input(); #[hdl] let mut mem = memory_with_init([(0x01u8, 0x23i8); 16]); mem.read_latency(0); @@ -560,120 +548,131 @@ fn test_memories() { sim.add_trace_writer(VcdWriterDecls::new(writer.clone())); sim.write_clock(sim.io().r.clk, false); sim.write_clock(sim.io().w.clk, false); - #[derive(Debug, PartialEq, Eq)] + #[hdl(cmp_eq)] struct IO { - r_addr: u8, - r_en: bool, - r_data: (u8, i8), - w_addr: u8, - w_en: bool, - w_data: (u8, i8), - w_mask: (bool, bool), + r_addr: UInt<4>, + r_en: Bool, + r_data: (UInt<8>, SInt<8>), + w_addr: UInt<4>, + w_en: Bool, + w_data: (UInt<8>, SInt<8>), + w_mask: (Bool, Bool), } let io_cycles = [ + #[hdl] IO { - r_addr: 0, + r_addr: 0_hdl_u4, r_en: false, - r_data: (0, 0), - w_addr: 0, + r_data: (0u8, 0i8), + w_addr: 0_hdl_u4, w_en: false, - w_data: (0, 0), + w_data: (0u8, 0i8), w_mask: (false, false), }, + #[hdl] IO { - r_addr: 0, + r_addr: 0_hdl_u4, r_en: true, - r_data: (0x1, 0x23), - w_addr: 0, + r_data: (0x1u8, 0x23i8), + w_addr: 0_hdl_u4, w_en: true, - w_data: (0x10, 0x20), + w_data: (0x10u8, 0x20i8), w_mask: (true, true), }, + #[hdl] IO { - r_addr: 0, + r_addr: 0_hdl_u4, r_en: true, - r_data: (0x10, 0x20), - w_addr: 0, + r_data: (0x10u8, 0x20i8), + w_addr: 0_hdl_u4, w_en: true, - w_data: (0x30, 0x40), + w_data: (0x30u8, 0x40i8), w_mask: (false, true), }, + #[hdl] IO { - r_addr: 0, + r_addr: 0_hdl_u4, r_en: true, - r_data: (0x10, 0x40), - w_addr: 0, + r_data: (0x10u8, 0x40i8), + w_addr: 0_hdl_u4, w_en: true, - w_data: (0x50, 0x60), + w_data: (0x50u8, 0x60i8), w_mask: (true, false), }, + #[hdl] IO { - r_addr: 0, + r_addr: 0_hdl_u4, r_en: true, - r_data: (0x50, 0x40), - w_addr: 0, + r_data: (0x50u8, 0x40i8), + w_addr: 0_hdl_u4, w_en: true, - w_data: (0x70, -0x80), + w_data: (0x70u8, -0x80i8), w_mask: (false, false), }, + #[hdl] IO { - r_addr: 0, + r_addr: 0_hdl_u4, r_en: true, - r_data: (0x50, 0x40), - w_addr: 0, + r_data: (0x50u8, 0x40i8), + w_addr: 0_hdl_u4, w_en: false, - w_data: (0x90, 0xA0u8 as i8), + w_data: (0x90u8, 0xA0u8 as i8), w_mask: (false, false), }, + #[hdl] IO { - r_addr: 0, + r_addr: 0_hdl_u4, r_en: true, - r_data: (0x50, 0x40), - w_addr: 1, + r_data: (0x50u8, 0x40i8), + w_addr: 1_hdl_u4, w_en: true, - w_data: (0x90, 0xA0u8 as i8), + w_data: (0x90u8, 0xA0u8 as i8), w_mask: (true, true), }, + #[hdl] IO { - r_addr: 0, + r_addr: 0_hdl_u4, r_en: true, - r_data: (0x50, 0x40), - w_addr: 2, + r_data: (0x50u8, 0x40i8), + w_addr: 2_hdl_u4, w_en: true, - w_data: (0xB0, 0xC0u8 as i8), + w_data: (0xB0u8, 0xC0u8 as i8), w_mask: (true, true), }, + #[hdl] IO { - r_addr: 0, + r_addr: 0_hdl_u4, r_en: true, - r_data: (0x50, 0x40), - w_addr: 2, + r_data: (0x50u8, 0x40i8), + w_addr: 2_hdl_u4, w_en: false, - w_data: (0xD0, 0xE0u8 as i8), + w_data: (0xD0u8, 0xE0u8 as i8), w_mask: (true, true), }, + #[hdl] IO { - r_addr: 1, + r_addr: 1_hdl_u4, r_en: true, - r_data: (0x90, 0xA0u8 as i8), - w_addr: 2, + r_data: (0x90u8, 0xA0u8 as i8), + w_addr: 2_hdl_u4, w_en: false, - w_data: (0xD0, 0xE0u8 as i8), + w_data: (0xD0u8, 0xE0u8 as i8), w_mask: (true, true), }, + #[hdl] IO { - r_addr: 2, + r_addr: 2_hdl_u4, r_en: true, - r_data: (0xB0, 0xC0u8 as i8), - w_addr: 2, + r_data: (0xB0u8, 0xC0u8 as i8), + w_addr: 2_hdl_u4, w_en: false, - w_data: (0xD0, 0xE0u8 as i8), + w_data: (0xD0u8, 0xE0u8 as i8), w_mask: (true, true), }, ]; - for ( - cycle, - expected @ IO { + for (cycle, expected) in io_cycles.into_iter().enumerate() { + #[hdl] + let IO { r_addr, r_en, r_data: _, @@ -681,37 +680,26 @@ fn test_memories() { w_en, w_data, w_mask, - }, - ) in io_cycles.into_iter().enumerate() - { - sim.write_bool_or_int(sim.io().r.addr, r_addr.cast_to_static()); - sim.write_bool(sim.io().r.en, r_en); - sim.write_bool_or_int(sim.io().w.addr, w_addr.cast_to_static()); - sim.write_bool(sim.io().w.en, w_en); - sim.write_bool_or_int(sim.io().w.data.0, w_data.0); - sim.write_bool_or_int(sim.io().w.data.1, w_data.1); - sim.write_bool(sim.io().w.mask.0, w_mask.0); - sim.write_bool(sim.io().w.mask.1, w_mask.1); - let io = IO { + } = expected; + sim.write(sim.io().r.addr, r_addr); + sim.write(sim.io().r.en, r_en); + sim.write(sim.io().w.addr, w_addr); + sim.write(sim.io().w.en, w_en); + sim.write(sim.io().w.data, w_data); + sim.write(sim.io().w.mask, w_mask); + let io = (#[hdl] + IO { r_addr, r_en, - r_data: ( - sim.read_bool_or_int(sim.io().r.data.0) - .to_bigint() - .try_into() - .expect("known to be in range"), - sim.read_bool_or_int(sim.io().r.data.1) - .to_bigint() - .try_into() - .expect("known to be in range"), - ), + r_data: sim.read(sim.io().r.data), w_addr, w_en, w_data, w_mask, - }; + }) + .to_sim_value(StaticType::TYPE); assert_eq!( - expected, + expected.to_sim_value(StaticType::TYPE), io, "vcd:\n{}\ncycle: {cycle}", String::from_utf8(writer.take()).unwrap(), @@ -739,7 +727,7 @@ fn test_memories() { #[hdl_module(outline_generated)] pub fn memories2() { #[hdl] - let rw: fayalite::memory::ReadWriteStruct, ConstUsize<3>> = m.input(); + let rw: ReadWriteStruct, ConstUsize<3>> = m.input(); #[hdl] let mut mem = memory_with_init([HdlSome(true); 5]); mem.read_latency(1); @@ -1012,9 +1000,9 @@ fn test_memories2() { #[hdl_module(outline_generated)] pub fn memories3() { #[hdl] - let r: fayalite::memory::ReadStruct, 8>, ConstUsize<3>> = m.input(); + let r: ReadStruct, 8>, ConstUsize<3>> = m.input(); #[hdl] - let w: fayalite::memory::WriteStruct, 8>, ConstUsize<3>> = m.input(); + let w: WriteStruct, 8>, ConstUsize<3>> = m.input(); #[hdl] let mut mem: MemBuilder, 8>> = memory(); mem.depth(8); diff --git a/crates/fayalite/tests/sim/expected/extern_module2.txt b/crates/fayalite/tests/sim/expected/extern_module2.txt index 96710fb..ec842ff 100644 --- a/crates/fayalite/tests/sim/expected/extern_module2.txt +++ b/crates/fayalite/tests/sim/expected/extern_module2.txt @@ -222,7 +222,9 @@ Simulation { }, value: SimValue { ty: Clock, - bits: 0x1, + value: OpaqueSimValue { + bits: 0x1_u1, + }, }, }, }, diff --git a/crates/fayalite/tests/sim/expected/ripple_counter.txt b/crates/fayalite/tests/sim/expected/ripple_counter.txt index e290ace..5472950 100644 --- a/crates/fayalite/tests/sim/expected/ripple_counter.txt +++ b/crates/fayalite/tests/sim/expected/ripple_counter.txt @@ -826,7 +826,9 @@ Simulation { }, value: SimValue { ty: Clock, - bits: 0x0, + value: OpaqueSimValue { + bits: 0x0_u1, + }, }, }, }, @@ -921,7 +923,9 @@ Simulation { }, value: SimValue { ty: Clock, - bits: 0x0, + value: OpaqueSimValue { + bits: 0x0_u1, + }, }, }, }, @@ -1016,7 +1020,9 @@ Simulation { }, value: SimValue { ty: Clock, - bits: 0x0, + value: OpaqueSimValue { + bits: 0x0_u1, + }, }, }, }, From a40eaaa2da1731eb7af535dbd6b910408f3874dc Mon Sep 17 00:00:00 2001 From: Jacob Lifshay Date: Sun, 30 Mar 2025 00:55:38 -0700 Subject: [PATCH 19/38] expand SimValue support --- .../src/hdl_bundle.rs | 96 ++++ .../fayalite-proc-macros-impl/src/hdl_enum.rs | 46 ++ crates/fayalite-proc-macros-impl/src/lib.rs | 1 + .../src/module/transform_body.rs | 46 +- .../expand_aggregate_literals.rs | 111 ++++- .../src/module/transform_body/expand_match.rs | 113 +++-- crates/fayalite/src/bundle.rs | 85 ++-- crates/fayalite/src/int.rs | 9 +- crates/fayalite/src/phantom_const.rs | 32 +- crates/fayalite/src/sim.rs | 4 +- crates/fayalite/src/sim/value.rs | 455 +++++++++++++----- crates/fayalite/src/ty.rs | 12 +- crates/fayalite/tests/sim.rs | 196 ++++---- 13 files changed, 864 insertions(+), 342 deletions(-) diff --git a/crates/fayalite-proc-macros-impl/src/hdl_bundle.rs b/crates/fayalite-proc-macros-impl/src/hdl_bundle.rs index a083def..8e49ac4 100644 --- a/crates/fayalite-proc-macros-impl/src/hdl_bundle.rs +++ b/crates/fayalite-proc-macros-impl/src/hdl_bundle.rs @@ -692,6 +692,12 @@ impl ToTokens for ParsedBundle { v.field_to_bits(&value.#ident); } })); + let to_sim_value_fields = Vec::from_iter(fields.named().into_iter().map(|field| { + let ident: &Ident = field.ident().as_ref().unwrap(); + quote_spanned! {span=> + #ident: ::fayalite::sim::value::SimValue::ty(&self.#ident), + } + })); let fields_len = fields.named().into_iter().len(); quote_spanned! {span=> #[automatically_derived] @@ -797,6 +803,51 @@ impl ToTokens for ParsedBundle { } } #[automatically_derived] + impl #impl_generics ::fayalite::sim::value::ToSimValue for #mask_type_sim_value_ident #type_generics + #where_clause + { + type Type = #mask_type_ident #type_generics; + + fn to_sim_value( + &self, + ) -> ::fayalite::sim::value::SimValue< + ::Type, + > { + let ty = #mask_type_ident { + #(#to_sim_value_fields)* + }; + ::fayalite::sim::value::SimValue::from_value(ty, ::fayalite::__std::clone::Clone::clone(self)) + } + fn into_sim_value( + self, + ) -> ::fayalite::sim::value::SimValue< + ::Type, + > { + let ty = #mask_type_ident { + #(#to_sim_value_fields)* + }; + ::fayalite::sim::value::SimValue::from_value(ty, self) + } + } + #[automatically_derived] + impl #impl_generics ::fayalite::sim::value::ToSimValueWithType<#mask_type_ident #type_generics> + for #mask_type_sim_value_ident #type_generics + #where_clause + { + fn to_sim_value_with_type( + &self, + ty: #mask_type_ident #type_generics, + ) -> ::fayalite::sim::value::SimValue<#mask_type_ident #type_generics> { + ::fayalite::sim::value::SimValue::from_value(ty, ::fayalite::__std::clone::Clone::clone(self)) + } + fn into_sim_value_with_type( + self, + ty: #mask_type_ident #type_generics, + ) -> ::fayalite::sim::value::SimValue<#mask_type_ident #type_generics> { + ::fayalite::sim::value::SimValue::from_value(ty, self) + } + } + #[automatically_derived] impl #impl_generics ::fayalite::ty::Type for #target #type_generics #where_clause { @@ -900,6 +951,51 @@ impl ToTokens for ParsedBundle { ::fayalite::intern::Interned::into_inner(::fayalite::intern::Intern::intern_sized(__retval)) } } + #[automatically_derived] + impl #impl_generics ::fayalite::sim::value::ToSimValue for #sim_value_ident #type_generics + #where_clause + { + type Type = #target #type_generics; + + fn to_sim_value( + &self, + ) -> ::fayalite::sim::value::SimValue< + ::Type, + > { + let ty = #target { + #(#to_sim_value_fields)* + }; + ::fayalite::sim::value::SimValue::from_value(ty, ::fayalite::__std::clone::Clone::clone(self)) + } + fn into_sim_value( + self, + ) -> ::fayalite::sim::value::SimValue< + ::Type, + > { + let ty = #target { + #(#to_sim_value_fields)* + }; + ::fayalite::sim::value::SimValue::from_value(ty, self) + } + } + #[automatically_derived] + impl #impl_generics ::fayalite::sim::value::ToSimValueWithType<#target #type_generics> + for #sim_value_ident #type_generics + #where_clause + { + fn to_sim_value_with_type( + &self, + ty: #target #type_generics, + ) -> ::fayalite::sim::value::SimValue<#target #type_generics> { + ::fayalite::sim::value::SimValue::from_value(ty, ::fayalite::__std::clone::Clone::clone(self)) + } + fn into_sim_value_with_type( + self, + ty: #target #type_generics, + ) -> ::fayalite::sim::value::SimValue<#target #type_generics> { + ::fayalite::sim::value::SimValue::from_value(ty, self) + } + } } .to_tokens(tokens); if let Some((cmp_eq,)) = cmp_eq { diff --git a/crates/fayalite-proc-macros-impl/src/hdl_enum.rs b/crates/fayalite-proc-macros-impl/src/hdl_enum.rs index 0cdf85c..dd09a73 100644 --- a/crates/fayalite-proc-macros-impl/src/hdl_enum.rs +++ b/crates/fayalite-proc-macros-impl/src/hdl_enum.rs @@ -866,6 +866,24 @@ impl ToTokens for ParsedEnum { ][..]) } } + #[automatically_derived] + impl #impl_generics ::fayalite::sim::value::ToSimValueWithType<#target #type_generics> + for #sim_value_ident #type_generics + #where_clause + { + fn to_sim_value_with_type( + &self, + ty: #target #type_generics, + ) -> ::fayalite::sim::value::SimValue<#target #type_generics> { + ::fayalite::sim::value::SimValue::from_value(ty, ::fayalite::__std::clone::Clone::clone(self)) + } + fn into_sim_value_with_type( + self, + ty: #target #type_generics, + ) -> ::fayalite::sim::value::SimValue<#target #type_generics> { + ::fayalite::sim::value::SimValue::from_value(ty, self) + } + } } .to_tokens(tokens); if let (None, MaybeParsed::Parsed(generics)) = (no_static, &self.generics) { @@ -921,6 +939,34 @@ impl ToTokens for ParsedEnum { const MASK_TYPE_PROPERTIES: ::fayalite::ty::TypeProperties = <::fayalite::int::Bool as ::fayalite::ty::StaticType>::TYPE_PROPERTIES; } + #[automatically_derived] + impl #static_impl_generics ::fayalite::sim::value::ToSimValue + for #sim_value_ident #static_type_generics + #static_where_clause + { + type Type = #target #static_type_generics; + + fn to_sim_value( + &self, + ) -> ::fayalite::sim::value::SimValue< + ::Type, + > { + ::fayalite::sim::value::SimValue::from_value( + ::fayalite::ty::StaticType::TYPE, + ::fayalite::__std::clone::Clone::clone(self), + ) + } + fn into_sim_value( + self, + ) -> ::fayalite::sim::value::SimValue< + ::Type, + > { + ::fayalite::sim::value::SimValue::from_value( + ::fayalite::ty::StaticType::TYPE, + self, + ) + } + } } .to_tokens(tokens); } diff --git a/crates/fayalite-proc-macros-impl/src/lib.rs b/crates/fayalite-proc-macros-impl/src/lib.rs index 5fe3ae8..4f7c4f0 100644 --- a/crates/fayalite-proc-macros-impl/src/lib.rs +++ b/crates/fayalite-proc-macros-impl/src/lib.rs @@ -93,6 +93,7 @@ mod kw { custom_keyword!(output); custom_keyword!(reg_builder); custom_keyword!(reset); + custom_keyword!(sim); custom_keyword!(skip); custom_keyword!(target); custom_keyword!(wire); diff --git a/crates/fayalite-proc-macros-impl/src/module/transform_body.rs b/crates/fayalite-proc-macros-impl/src/module/transform_body.rs index c67f8dc..8f427a9 100644 --- a/crates/fayalite-proc-macros-impl/src/module/transform_body.rs +++ b/crates/fayalite-proc-macros-impl/src/module/transform_body.rs @@ -16,7 +16,7 @@ use std::{borrow::Borrow, convert::Infallible}; use syn::{ fold::{fold_expr, fold_expr_lit, fold_expr_unary, fold_local, fold_stmt, Fold}, parenthesized, - parse::{Nothing, Parse, ParseStream}, + parse::{Parse, ParseStream}, parse_quote, parse_quote_spanned, spanned::Spanned, token::Paren, @@ -27,6 +27,13 @@ use syn::{ mod expand_aggregate_literals; mod expand_match; +options! { + #[options = ExprOptions] + pub(crate) enum ExprOption { + Sim(sim), + } +} + options! { pub(crate) enum LetFnKind { Input(input), @@ -952,7 +959,7 @@ with_debug_clone_and_fold! { #[allow(dead_code)] pub(crate) struct HdlLet { pub(crate) attrs: Vec, - pub(crate) hdl_attr: HdlAttr, + pub(crate) hdl_attr: HdlAttr, pub(crate) let_token: Token![let], pub(crate) mut_token: Option, pub(crate) name: Ident, @@ -1173,7 +1180,7 @@ impl Visitor<'_> { Some(_) => {} } } - fn process_hdl_if(&mut self, hdl_attr: HdlAttr, expr_if: ExprIf) -> Expr { + fn process_hdl_if(&mut self, hdl_attr: HdlAttr, expr_if: ExprIf) -> Expr { let ExprIf { attrs, if_token, @@ -1181,10 +1188,10 @@ impl Visitor<'_> { then_branch, else_branch, } = expr_if; - self.require_normal_module_or_fn(if_token); - let else_expr = else_branch.unzip().1.map(|else_expr| match *else_expr { - Expr::If(expr_if) => self.process_hdl_if(hdl_attr.clone(), expr_if), - expr => expr, + let (else_token, else_expr) = else_branch.unzip(); + let else_expr = else_expr.map(|else_expr| match *else_expr { + Expr::If(expr_if) => Box::new(self.process_hdl_if(hdl_attr.clone(), expr_if)), + _ => else_expr, }); if let Expr::Let(ExprLet { attrs: let_attrs, @@ -1206,7 +1213,19 @@ impl Visitor<'_> { }, ); } - if let Some(else_expr) = else_expr { + let ExprOptions { sim } = hdl_attr.body; + if sim.is_some() { + ExprIf { + attrs, + if_token, + cond: parse_quote_spanned! {if_token.span=> + *::fayalite::sim::value::SimValue::<::fayalite::int::Bool>::value(&::fayalite::sim::value::ToSimValue::into_sim_value(#cond)) + }, + then_branch, + else_branch: else_token.zip(else_expr), + } + .into() + } else if let Some(else_expr) = else_expr { parse_quote_spanned! {if_token.span=> #(#attrs)* { @@ -1675,7 +1694,7 @@ impl Fold for Visitor<'_> { fn fold_local(&mut self, mut let_stmt: Local) -> Local { match self .errors - .ok(HdlAttr::::parse_and_leave_attr( + .ok(HdlAttr::::parse_and_leave_attr( &let_stmt.attrs, )) { None => return empty_let(), @@ -1694,10 +1713,11 @@ impl Fold for Visitor<'_> { subpat: None, }) = pat else { - let hdl_attr = HdlAttr::::parse_and_take_attr(&mut let_stmt.attrs) - .ok() - .flatten() - .expect("already checked above"); + let hdl_attr = + HdlAttr::::parse_and_take_attr(&mut let_stmt.attrs) + .ok() + .flatten() + .expect("already checked above"); let let_stmt = fold_local(self, let_stmt); return self.process_hdl_let_pat(hdl_attr, let_stmt); }; diff --git a/crates/fayalite-proc-macros-impl/src/module/transform_body/expand_aggregate_literals.rs b/crates/fayalite-proc-macros-impl/src/module/transform_body/expand_aggregate_literals.rs index b5a0ad3..8892bd5 100644 --- a/crates/fayalite-proc-macros-impl/src/module/transform_body/expand_aggregate_literals.rs +++ b/crates/fayalite-proc-macros-impl/src/module/transform_body/expand_aggregate_literals.rs @@ -1,45 +1,97 @@ // SPDX-License-Identifier: LGPL-3.0-or-later // See Notices.txt for copyright information -use crate::{kw, module::transform_body::Visitor, HdlAttr}; +use crate::{ + kw, + module::transform_body::{ExprOptions, Visitor}, + HdlAttr, +}; use quote::{format_ident, quote_spanned}; use syn::{ - parse::Nothing, parse_quote, parse_quote_spanned, spanned::Spanned, Expr, ExprArray, ExprPath, - ExprRepeat, ExprStruct, ExprTuple, FieldValue, TypePath, + parse_quote_spanned, spanned::Spanned, Expr, ExprArray, ExprPath, ExprRepeat, ExprStruct, + ExprTuple, FieldValue, TypePath, }; impl Visitor<'_> { pub(crate) fn process_hdl_array( &mut self, - hdl_attr: HdlAttr, + hdl_attr: HdlAttr, mut expr_array: ExprArray, ) -> Expr { - self.require_normal_module_or_fn(hdl_attr); - for elem in &mut expr_array.elems { - *elem = parse_quote_spanned! {elem.span()=> - ::fayalite::expr::ToExpr::to_expr(&(#elem)) - }; + let ExprOptions { sim } = hdl_attr.body; + let span = hdl_attr.kw.span; + if sim.is_some() { + for elem in &mut expr_array.elems { + *elem = parse_quote_spanned! {elem.span()=> + ::fayalite::sim::value::ToSimValue::to_sim_value(&(#elem)) + }; + } + parse_quote_spanned! {span=> + ::fayalite::sim::value::ToSimValue::into_sim_value(#expr_array) + } + } else { + for elem in &mut expr_array.elems { + *elem = parse_quote_spanned! {elem.span()=> + ::fayalite::expr::ToExpr::to_expr(&(#elem)) + }; + } + parse_quote_spanned! {span=> + ::fayalite::expr::ToExpr::to_expr(&#expr_array) + } } - parse_quote! {::fayalite::expr::ToExpr::to_expr(&#expr_array)} } pub(crate) fn process_hdl_repeat( &mut self, - hdl_attr: HdlAttr, + hdl_attr: HdlAttr, mut expr_repeat: ExprRepeat, ) -> Expr { - self.require_normal_module_or_fn(hdl_attr); let repeated_value = &expr_repeat.expr; - *expr_repeat.expr = parse_quote_spanned! {repeated_value.span()=> - ::fayalite::expr::ToExpr::to_expr(&(#repeated_value)) - }; - parse_quote! {::fayalite::expr::ToExpr::to_expr(&#expr_repeat)} + let ExprOptions { sim } = hdl_attr.body; + let span = hdl_attr.kw.span; + if sim.is_some() { + *expr_repeat.expr = parse_quote_spanned! {repeated_value.span()=> + ::fayalite::sim::value::ToSimValue::to_sim_value(&(#repeated_value)) + }; + parse_quote_spanned! {span=> + ::fayalite::sim::value::ToSimValue::into_sim_value(#expr_repeat) + } + } else { + *expr_repeat.expr = parse_quote_spanned! {repeated_value.span()=> + ::fayalite::expr::ToExpr::to_expr(&(#repeated_value)) + }; + parse_quote_spanned! {span=> + ::fayalite::expr::ToExpr::to_expr(&#expr_repeat) + } + } } pub(crate) fn process_hdl_struct( &mut self, - hdl_attr: HdlAttr, - expr_struct: ExprStruct, + hdl_attr: HdlAttr, + mut expr_struct: ExprStruct, ) -> Expr { - self.require_normal_module_or_fn(&hdl_attr); let name_span = expr_struct.path.segments.last().unwrap().ident.span(); + let ExprOptions { sim } = hdl_attr.body; + if sim.is_some() { + let ty_path = TypePath { + qself: expr_struct.qself.take(), + path: expr_struct.path, + }; + expr_struct.path = parse_quote_spanned! {name_span=> + __SimValue::<#ty_path> + }; + for field in &mut expr_struct.fields { + let expr = &field.expr; + field.expr = parse_quote_spanned! {field.member.span()=> + ::fayalite::sim::value::ToSimValue::to_sim_value(&(#expr)) + }; + } + return parse_quote_spanned! {name_span=> + { + type __SimValue = ::SimValue; + let value: ::fayalite::sim::value::SimValue<#ty_path> = ::fayalite::sim::value::ToSimValue::into_sim_value(#expr_struct); + value + } + }; + } let builder_ident = format_ident!("__builder", span = name_span); let empty_builder = if expr_struct.qself.is_some() || expr_struct @@ -91,12 +143,23 @@ impl Visitor<'_> { } pub(crate) fn process_hdl_tuple( &mut self, - hdl_attr: HdlAttr, - expr_tuple: ExprTuple, + hdl_attr: HdlAttr, + mut expr_tuple: ExprTuple, ) -> Expr { - self.require_normal_module_or_fn(hdl_attr); - parse_quote_spanned! {expr_tuple.span()=> - ::fayalite::expr::ToExpr::to_expr(&#expr_tuple) + let ExprOptions { sim } = hdl_attr.body; + if sim.is_some() { + for element in &mut expr_tuple.elems { + *element = parse_quote_spanned! {element.span()=> + &(#element) + }; + } + parse_quote_spanned! {expr_tuple.span()=> + ::fayalite::sim::value::ToSimValue::into_sim_value(#expr_tuple) + } + } else { + parse_quote_spanned! {expr_tuple.span()=> + ::fayalite::expr::ToExpr::to_expr(&#expr_tuple) + } } } } diff --git a/crates/fayalite-proc-macros-impl/src/module/transform_body/expand_match.rs b/crates/fayalite-proc-macros-impl/src/module/transform_body/expand_match.rs index f1ff2c2..57e919a 100644 --- a/crates/fayalite-proc-macros-impl/src/module/transform_body/expand_match.rs +++ b/crates/fayalite-proc-macros-impl/src/module/transform_body/expand_match.rs @@ -3,7 +3,9 @@ use crate::{ fold::{impl_fold, DoFold}, kw, - module::transform_body::{empty_let, with_debug_clone_and_fold, wrap_ty_with_expr, Visitor}, + module::transform_body::{ + empty_let, with_debug_clone_and_fold, wrap_ty_with_expr, ExprOptions, Visitor, + }, Errors, HdlAttr, PairsIterExt, }; use proc_macro2::{Span, TokenStream}; @@ -11,7 +13,6 @@ use quote::{format_ident, quote_spanned, ToTokens, TokenStreamExt}; use std::collections::BTreeSet; use syn::{ fold::{fold_arm, fold_expr_match, fold_local, fold_pat, Fold}, - parse::Nothing, parse_quote_spanned, punctuated::Punctuated, spanned::Spanned, @@ -981,7 +982,7 @@ impl<'a> VisitMatchPat<'a> for HdlLetPatVisitState<'a> { impl Visitor<'_> { pub(crate) fn process_hdl_let_pat( &mut self, - _hdl_attr: HdlAttr, + hdl_attr: HdlAttr, mut let_stmt: Local, ) -> Local { let span = let_stmt.let_token.span(); @@ -996,7 +997,6 @@ impl Visitor<'_> { init, semi_token, } = let_stmt; - self.require_normal_module_or_fn(let_token); let Some(syn::LocalInit { eq_token, expr, @@ -1031,29 +1031,48 @@ impl Visitor<'_> { errors: _, bindings, } = state; - let retval = parse_quote_spanned! {span=> - let (#(#bindings,)* __scope,) = { - type __MatchTy = ::MatchVariant; - let __match_expr = ::fayalite::expr::ToExpr::to_expr(&(#expr)); - ::fayalite::expr::check_match_expr(__match_expr, |__match_value, __infallible| { - #[allow(unused_variables)] - #check_let_stmt - match __infallible {} - }); - let mut __match_iter = ::fayalite::module::match_(__match_expr); - let ::fayalite::__std::option::Option::Some(__match_variant) = ::fayalite::__std::iter::Iterator::next(&mut __match_iter) else { - ::fayalite::__std::unreachable!("#[hdl] let with uninhabited type"); + let ExprOptions { sim } = hdl_attr.body; + let retval = if sim.is_some() { + parse_quote_spanned! {span=> + let (#(#bindings,)*) = { + type __MatchTy = ::SimValue; + let __match_value = ::fayalite::sim::value::ToSimValue::to_sim_value(&(#expr)); + #let_token #pat #eq_token ::fayalite::sim::value::SimValue::into_value(__match_value) #semi_token + (#(#bindings,)*) }; - let ::fayalite::__std::option::Option::None = ::fayalite::__std::iter::Iterator::next(&mut __match_iter) else { - ::fayalite::__std::unreachable!("#[hdl] let with refutable pattern"); - }; - let (__match_variant, __scope) = - ::fayalite::ty::MatchVariantAndInactiveScope::match_activate_scope( - __match_variant, + } + } else { + parse_quote_spanned! {span=> + let (#(#bindings,)* __scope,) = { + type __MatchTy = ::MatchVariant; + let __match_expr = ::fayalite::expr::ToExpr::to_expr(&(#expr)); + ::fayalite::expr::check_match_expr( + __match_expr, + |__match_value, __infallible| { + #[allow(unused_variables)] + #check_let_stmt + match __infallible {} + }, ); - #let_token #pat #eq_token __match_variant #semi_token - (#(#bindings,)* __scope,) - }; + let mut __match_iter = ::fayalite::module::match_(__match_expr); + let ::fayalite::__std::option::Option::Some(__match_variant) = + ::fayalite::__std::iter::Iterator::next(&mut __match_iter) + else { + ::fayalite::__std::unreachable!("#[hdl] let with uninhabited type"); + }; + let ::fayalite::__std::option::Option::None = + ::fayalite::__std::iter::Iterator::next(&mut __match_iter) + else { + ::fayalite::__std::unreachable!("#[hdl] let with refutable pattern"); + }; + let (__match_variant, __scope) = + ::fayalite::ty::MatchVariantAndInactiveScope::match_activate_scope( + __match_variant, + ); + #let_token #pat #eq_token __match_variant #semi_token + (#(#bindings,)* __scope,) + }; + } }; match retval { syn::Stmt::Local(retval) => retval, @@ -1062,7 +1081,7 @@ impl Visitor<'_> { } pub(crate) fn process_hdl_match( &mut self, - _hdl_attr: HdlAttr, + hdl_attr: HdlAttr, expr_match: ExprMatch, ) -> Expr { let span = expr_match.match_token.span(); @@ -1074,7 +1093,6 @@ impl Visitor<'_> { brace_token: _, arms, } = expr_match; - self.require_normal_module_or_fn(match_token); let mut state = HdlMatchParseState { match_span: span, errors: &mut self.errors, @@ -1083,24 +1101,37 @@ impl Visitor<'_> { arms.into_iter() .filter_map(|arm| MatchArm::parse(&mut state, arm).ok()), ); - let expr = quote_spanned! {span=> - { - type __MatchTy = ::MatchVariant; - let __match_expr = ::fayalite::expr::ToExpr::to_expr(&(#expr)); - ::fayalite::expr::check_match_expr(__match_expr, |__match_value, __infallible| { - #[allow(unused_variables)] - #check_match - }); - for __match_variant in ::fayalite::module::match_(__match_expr) { - let (__match_variant, __scope) = - ::fayalite::ty::MatchVariantAndInactiveScope::match_activate_scope( - __match_variant, - ); - #match_token __match_variant { + let ExprOptions { sim } = hdl_attr.body; + let expr = if sim.is_some() { + quote_spanned! {span=> + { + type __MatchTy = ::SimValue; + let __match_expr = ::fayalite::sim::value::ToSimValue::to_sim_value(&(#expr)); + #match_token *__match_expr { #(#arms)* } } } + } else { + quote_spanned! {span=> + { + type __MatchTy = ::MatchVariant; + let __match_expr = ::fayalite::expr::ToExpr::to_expr(&(#expr)); + ::fayalite::expr::check_match_expr(__match_expr, |__match_value, __infallible| { + #[allow(unused_variables)] + #check_match + }); + for __match_variant in ::fayalite::module::match_(__match_expr) { + let (__match_variant, __scope) = + ::fayalite::ty::MatchVariantAndInactiveScope::match_activate_scope( + __match_variant, + ); + #match_token __match_variant { + #(#arms)* + } + } + } + } }; syn::parse2(expr).unwrap() } diff --git a/crates/fayalite/src/bundle.rs b/crates/fayalite/src/bundle.rs index 06e0411..9279b57 100644 --- a/crates/fayalite/src/bundle.rs +++ b/crates/fayalite/src/bundle.rs @@ -8,7 +8,7 @@ use crate::{ }, int::{Bool, DynSize}, intern::{Intern, Interned}, - sim::value::{SimValue, SimValuePartialEq, ToSimValue}, + sim::value::{SimValue, SimValuePartialEq, ToSimValue, ToSimValueWithType}, source_location::SourceLocation, ty::{ impl_match_variant_as_self, CanonicalType, MatchVariantWithoutScope, OpaqueSimValue, @@ -562,29 +562,29 @@ macro_rules! impl_tuples { BundleLiteral::new(ty, field_values[..].intern()).to_expr() } } - impl<$($T: ToSimValue,)*> ToSimValue for ($($T,)*) { + impl<$($T: ToSimValueWithType,)*> ToSimValueWithType for ($($T,)*) { #[track_caller] - fn to_sim_value(&self, ty: CanonicalType) -> SimValue { - SimValue::into_canonical(ToSimValue::::to_sim_value(self, Bundle::from_canonical(ty))) + fn to_sim_value_with_type(&self, ty: CanonicalType) -> SimValue { + SimValue::into_canonical(ToSimValueWithType::::to_sim_value_with_type(self, Bundle::from_canonical(ty))) } #[track_caller] - fn into_sim_value(self, ty: CanonicalType) -> SimValue + fn into_sim_value_with_type(self, ty: CanonicalType) -> SimValue { - SimValue::into_canonical(ToSimValue::::into_sim_value(self, Bundle::from_canonical(ty))) + SimValue::into_canonical(ToSimValueWithType::::into_sim_value_with_type(self, Bundle::from_canonical(ty))) } } - impl<$($T: ToSimValue,)*> ToSimValue for ($($T,)*) { + impl<$($T: ToSimValueWithType,)*> ToSimValueWithType for ($($T,)*) { #[track_caller] - fn to_sim_value(&self, ty: Bundle) -> SimValue { + fn to_sim_value_with_type(&self, ty: Bundle) -> SimValue { let ($($var,)*) = self; let [$($ty_var,)*] = *ty.fields() else { panic!("bundle has wrong number of fields"); }; - $(let $var = $var.to_sim_value($ty_var.ty);)* - ToSimValue::into_sim_value(($($var,)*), ty) + $(let $var = $var.to_sim_value_with_type($ty_var.ty);)* + ToSimValueWithType::into_sim_value_with_type(($($var,)*), ty) } #[track_caller] - fn into_sim_value(self, ty: Bundle) -> SimValue { + fn into_sim_value_with_type(self, ty: Bundle) -> SimValue { #![allow(unused_mut)] #![allow(clippy::unused_unit)] let ($($var,)*) = self; @@ -592,29 +592,44 @@ macro_rules! impl_tuples { panic!("bundle has wrong number of fields"); }; let mut bits = BitVec::new(); - $(let $var = $var.into_sim_value($ty_var.ty); + $(let $var = $var.into_sim_value_with_type($ty_var.ty); assert_eq!(SimValue::ty(&$var), $ty_var.ty); bits.extend_from_bitslice(SimValue::bits(&$var).bits()); )* - bits.into_sim_value(ty) + bits.into_sim_value_with_type(ty) } } - impl<$($T: ToSimValue<$Ty>, $Ty: Type,)*> ToSimValue<($($Ty,)*)> for ($($T,)*) { + impl<$($T: ToSimValueWithType<$Ty>, $Ty: Type,)*> ToSimValueWithType<($($Ty,)*)> for ($($T,)*) { #[track_caller] - fn to_sim_value(&self, ty: ($($Ty,)*)) -> SimValue<($($Ty,)*)> { + fn to_sim_value_with_type(&self, ty: ($($Ty,)*)) -> SimValue<($($Ty,)*)> { let ($($var,)*) = self; let ($($ty_var,)*) = ty; - $(let $var = $var.to_sim_value($ty_var);)* + $(let $var = $var.to_sim_value_with_type($ty_var);)* SimValue::from_value(ty, ($($var,)*)) } #[track_caller] - fn into_sim_value(self, ty: ($($Ty,)*)) -> SimValue<($($Ty,)*)> { + fn into_sim_value_with_type(self, ty: ($($Ty,)*)) -> SimValue<($($Ty,)*)> { let ($($var,)*) = self; let ($($ty_var,)*) = ty; - $(let $var = $var.into_sim_value($ty_var);)* + $(let $var = $var.into_sim_value_with_type($ty_var);)* SimValue::from_value(ty, ($($var,)*)) } } + impl<$($T: ToSimValue,)*> ToSimValue for ($($T,)*) { + type Type = ($($T::Type,)*); + #[track_caller] + fn to_sim_value(&self) -> SimValue { + let ($($var,)*) = self; + $(let $var = $var.to_sim_value();)* + SimValue::from_value(($(SimValue::ty(&$var),)*), ($($var,)*)) + } + #[track_caller] + fn into_sim_value(self) -> SimValue { + let ($($var,)*) = self; + $(let $var = $var.to_sim_value();)* + SimValue::from_value(($(SimValue::ty(&$var),)*), ($($var,)*)) + } + } impl<$($Lhs: Type + ExprPartialEq<$Rhs>, $Rhs: Type,)*> ExprPartialEq<($($Rhs,)*)> for ($($Lhs,)*) { fn cmp_eq(lhs: Expr, rhs: Expr<($($Rhs,)*)>) -> Expr { let ($($lhs_var,)*) = *lhs; @@ -674,7 +689,7 @@ impl_tuples! { impl Type for PhantomData { type BaseType = Bundle; type MaskType = (); - type SimValue = (); + type SimValue = PhantomData; type MatchVariant = PhantomData; type MatchActiveScope = (); type MatchVariantAndInactiveScope = MatchVariantWithoutScope; @@ -709,7 +724,7 @@ impl Type for PhantomData { } fn sim_value_from_bits(&self, bits: &BitSlice) -> Self::SimValue { assert!(bits.is_empty()); - () + *self } fn sim_value_clone_from_bits(&self, _value: &mut Self::SimValue, bits: &BitSlice) { assert!(bits.is_empty()); @@ -764,26 +779,38 @@ impl ToExpr for PhantomData { } } -impl ToSimValue for PhantomData { +impl ToSimValue for PhantomData { + type Type = PhantomData; + #[track_caller] - fn to_sim_value(&self, ty: Self) -> SimValue { - ToSimValue::into_sim_value(BitVec::new(), ty) + fn to_sim_value(&self) -> SimValue { + SimValue::from_value(*self, *self) } } -impl ToSimValue for PhantomData { +impl ToSimValueWithType for PhantomData { #[track_caller] - fn to_sim_value(&self, ty: Bundle) -> SimValue { + fn to_sim_value_with_type(&self, ty: Self) -> SimValue { + SimValue::from_value(ty, *self) + } +} + +impl ToSimValueWithType for PhantomData { + #[track_caller] + fn to_sim_value_with_type(&self, ty: Bundle) -> SimValue { assert!(ty.fields().is_empty()); - ToSimValue::into_sim_value(BitVec::new(), ty) + ToSimValueWithType::into_sim_value_with_type(BitVec::new(), ty) } } -impl ToSimValue for PhantomData { +impl ToSimValueWithType for PhantomData { #[track_caller] - fn to_sim_value(&self, ty: CanonicalType) -> SimValue { + fn to_sim_value_with_type(&self, ty: CanonicalType) -> SimValue { let ty = Bundle::from_canonical(ty); assert!(ty.fields().is_empty()); - SimValue::into_canonical(ToSimValue::into_sim_value(BitVec::new(), ty)) + SimValue::into_canonical(ToSimValueWithType::into_sim_value_with_type( + BitVec::new(), + ty, + )) } } diff --git a/crates/fayalite/src/int.rs b/crates/fayalite/src/int.rs index a956dd5..373e150 100644 --- a/crates/fayalite/src/int.rs +++ b/crates/fayalite/src/int.rs @@ -2,12 +2,13 @@ // See Notices.txt for copyright information use crate::{ + array::ArrayType, expr::{ target::{GetTarget, Target}, Expr, NotALiteralExpr, ToExpr, ToLiteralBits, }, intern::{Intern, Interned, Memoize}, - sim::value::SimValue, + sim::value::{SimValue, ToSimValueWithType}, source_location::SourceLocation, ty::{impl_match_variant_as_self, CanonicalType, StaticType, Type, TypeProperties}, util::{interned_bit, ConstBool, ConstUsize, GenericConstBool, GenericConstUsize}, @@ -58,7 +59,8 @@ pub trait KnownSize: + std::fmt::Debug + IntoIterator> + TryFrom>> - + Into>>; + + Into>> + + ToSimValueWithType>; } macro_rules! known_widths { @@ -120,7 +122,8 @@ pub trait Size: + std::fmt::Debug + IntoIterator> + TryFrom>> - + Into>>; + + Into>> + + ToSimValueWithType>; const KNOWN_VALUE: Option; type SizeType: SizeType + Copy diff --git a/crates/fayalite/src/phantom_const.rs b/crates/fayalite/src/phantom_const.rs index 27bb04e..8422ae0 100644 --- a/crates/fayalite/src/phantom_const.rs +++ b/crates/fayalite/src/phantom_const.rs @@ -11,7 +11,7 @@ use crate::{ }, int::Bool, intern::{Intern, Interned, InternedCompare, LazyInterned, Memoize}, - sim::value::{SimValue, SimValuePartialEq}, + sim::value::{SimValue, SimValuePartialEq, ToSimValue, ToSimValueWithType}, source_location::SourceLocation, ty::{impl_match_variant_as_self, CanonicalType, StaticType, Type, TypeProperties}, }; @@ -248,7 +248,7 @@ impl PhantomConst { impl Type for PhantomConst { type BaseType = PhantomConst; type MaskType = (); - type SimValue = (); + type SimValue = PhantomConst; impl_match_variant_as_self!(); fn mask_type(&self) -> Self::MaskType { @@ -272,15 +272,17 @@ impl Type for PhantomConst { fn sim_value_from_bits(&self, bits: &BitSlice) -> Self::SimValue { assert!(bits.is_empty()); - () + *self } - fn sim_value_clone_from_bits(&self, _value: &mut Self::SimValue, bits: &BitSlice) { + fn sim_value_clone_from_bits(&self, value: &mut Self::SimValue, bits: &BitSlice) { assert!(bits.is_empty()); + assert_eq!(*value, *self); } - fn sim_value_to_bits(&self, _value: &Self::SimValue, bits: &mut BitSlice) { + fn sim_value_to_bits(&self, value: &Self::SimValue, bits: &mut BitSlice) { assert!(bits.is_empty()); + assert_eq!(*value, *self); } } @@ -334,3 +336,23 @@ impl SimValuePartialEq for PhantomConst true } } + +impl ToSimValue for PhantomConst { + type Type = PhantomConst; + + fn to_sim_value(&self) -> SimValue { + SimValue::from_value(*self, *self) + } +} + +impl ToSimValueWithType> for PhantomConst { + fn to_sim_value_with_type(&self, ty: PhantomConst) -> SimValue> { + SimValue::from_value(ty, *self) + } +} + +impl ToSimValueWithType for PhantomConst { + fn to_sim_value_with_type(&self, ty: CanonicalType) -> SimValue { + SimValue::into_canonical(SimValue::from_value(Self::from_canonical(ty), *self)) + } +} diff --git a/crates/fayalite/src/sim.rs b/crates/fayalite/src/sim.rs index 9be4889..563cd37 100644 --- a/crates/fayalite/src/sim.rs +++ b/crates/fayalite/src/sim.rs @@ -7531,10 +7531,10 @@ macro_rules! impl_simulation_methods { SimValue::from_canonical($self.settle_if_needed(retval)$(.$await)?) } $(#[$track_caller])? - pub $($async)? fn write>(&mut $self, io: Expr, value: V) { + pub $($async)? fn write>(&mut $self, io: Expr, value: V) { $self.sim_impl.borrow_mut().write( Expr::canonical(io), - &SimValue::into_canonical(value.into_sim_value(Expr::ty(io))), + &SimValue::into_canonical(value.into_sim_value_with_type(Expr::ty(io))), $which_module, ); } diff --git a/crates/fayalite/src/sim/value.rs b/crates/fayalite/src/sim/value.rs index 3e45016..d415af4 100644 --- a/crates/fayalite/src/sim/value.rs +++ b/crates/fayalite/src/sim/value.rs @@ -9,7 +9,7 @@ use crate::{ expr::{CastBitsTo, Expr, ToExpr}, int::{Bool, IntType, KnownSize, SInt, SIntType, SIntValue, Size, UInt, UIntType, UIntValue}, reset::{AsyncReset, Reset, SyncReset}, - ty::{CanonicalType, Type}, + ty::{CanonicalType, StaticType, Type}, util::{ alternating_cell::{AlternatingCell, AlternatingCellMethods}, ConstUsize, @@ -307,122 +307,223 @@ impl SimValuePartialEq for Bool { } } -pub trait ToSimValue { +pub trait ToSimValue: ToSimValueWithType<::Type> { + type Type: Type; + #[track_caller] - fn to_sim_value(&self, ty: T) -> SimValue; + fn to_sim_value(&self) -> SimValue; #[track_caller] - fn into_sim_value(self, ty: T) -> SimValue + fn into_sim_value(self) -> SimValue where Self: Sized, { - self.to_sim_value(ty) + self.to_sim_value() } #[track_caller] - fn arc_into_sim_value(self: Arc, ty: T) -> SimValue { - self.to_sim_value(ty) + fn arc_into_sim_value(self: Arc) -> SimValue { + self.to_sim_value() } #[track_caller] - fn arc_to_sim_value(self: &Arc, ty: T) -> SimValue { - self.to_sim_value(ty) + fn arc_to_sim_value(self: &Arc) -> SimValue { + self.to_sim_value() } } -impl ToSimValue for SimValue { +pub trait ToSimValueWithType { #[track_caller] - fn to_sim_value(&self, ty: T) -> SimValue { - assert_eq!(SimValue::ty(self), ty); + fn to_sim_value_with_type(&self, ty: T) -> SimValue; + #[track_caller] + fn into_sim_value_with_type(self, ty: T) -> SimValue + where + Self: Sized, + { + self.to_sim_value_with_type(ty) + } + #[track_caller] + fn arc_into_sim_value_with_type(self: Arc, ty: T) -> SimValue { + self.to_sim_value_with_type(ty) + } + #[track_caller] + fn arc_to_sim_value_with_type(self: &Arc, ty: T) -> SimValue { + self.to_sim_value_with_type(ty) + } +} + +macro_rules! forward_to_sim_value_with_type { + ([$($generics:tt)*] $ty:ty) => { + impl<$($generics)*> ToSimValueWithType<::Type> for $ty { + fn to_sim_value_with_type(&self, ty: ::Type) -> SimValue<::Type> { + let retval = Self::to_sim_value(self); + assert_eq!(SimValue::ty(&retval), ty); + retval + } + #[track_caller] + fn into_sim_value_with_type(self, ty: ::Type) -> SimValue<::Type> + where + Self: Sized, + { + let retval = Self::into_sim_value(self); + assert_eq!(SimValue::ty(&retval), ty); + retval + } + #[track_caller] + fn arc_into_sim_value_with_type(self: Arc, ty: ::Type) -> SimValue<::Type> { + let retval = Self::arc_into_sim_value(self); + assert_eq!(SimValue::ty(&retval), ty); + retval + } + #[track_caller] + fn arc_to_sim_value_with_type(self: &Arc, ty: ::Type) -> SimValue<::Type> { + let retval = Self::arc_to_sim_value(self); + assert_eq!(SimValue::ty(&retval), ty); + retval + } + } + }; +} + +impl ToSimValue for SimValue { + type Type = T; + fn to_sim_value(&self) -> SimValue { self.clone() } - #[track_caller] - fn into_sim_value(self, ty: T) -> SimValue { - assert_eq!(SimValue::ty(&self), ty); + fn into_sim_value(self) -> SimValue { self } } -impl ToSimValue for BitVec { +forward_to_sim_value_with_type!([T: Type] SimValue); + +impl ToSimValueWithType for BitVec { #[track_caller] - fn to_sim_value(&self, ty: T) -> SimValue { - self.clone().into_sim_value(ty) + fn to_sim_value_with_type(&self, ty: T) -> SimValue { + self.clone().into_sim_value_with_type(ty) } #[track_caller] - fn into_sim_value(self, ty: T) -> SimValue { - Arc::new(self).arc_into_sim_value(ty) + fn into_sim_value_with_type(self, ty: T) -> SimValue { + Arc::new(self).arc_into_sim_value_with_type(ty) } #[track_caller] - fn arc_into_sim_value(self: Arc, ty: T) -> SimValue { + fn arc_into_sim_value_with_type(self: Arc, ty: T) -> SimValue { SimValue::from_bits(ty, UIntValue::new_dyn(self)) } #[track_caller] - fn arc_to_sim_value(self: &Arc, ty: T) -> SimValue { + fn arc_to_sim_value_with_type(self: &Arc, ty: T) -> SimValue { SimValue::from_bits(ty, UIntValue::new_dyn(self.clone())) } } -impl ToSimValue for bitvec::boxed::BitBox { +impl ToSimValueWithType for bitvec::boxed::BitBox { #[track_caller] - fn to_sim_value(&self, ty: T) -> SimValue { - self.clone().into_sim_value(ty) + fn to_sim_value_with_type(&self, ty: T) -> SimValue { + self.clone().into_sim_value_with_type(ty) } #[track_caller] - fn into_sim_value(self, ty: T) -> SimValue { - self.into_bitvec().into_sim_value(ty) + fn into_sim_value_with_type(self, ty: T) -> SimValue { + self.into_bitvec().into_sim_value_with_type(ty) } } -impl ToSimValue for BitSlice { +impl ToSimValueWithType for BitSlice { #[track_caller] - fn to_sim_value(&self, ty: T) -> SimValue { - self.to_bitvec().into_sim_value(ty) + fn to_sim_value_with_type(&self, ty: T) -> SimValue { + self.to_bitvec().into_sim_value_with_type(ty) } } -impl, T: Type> ToSimValue for &'_ This { - fn to_sim_value(&self, ty: T) -> SimValue { - This::to_sim_value(self, ty) +impl ToSimValue for &'_ This { + type Type = This::Type; + + fn to_sim_value(&self) -> SimValue { + This::to_sim_value(self) } } -impl, T: Type> ToSimValue for &'_ mut This { - fn to_sim_value(&self, ty: T) -> SimValue { - This::to_sim_value(self, ty) +impl, T: Type> ToSimValueWithType for &'_ This { + fn to_sim_value_with_type(&self, ty: T) -> SimValue { + This::to_sim_value_with_type(self, ty) } } -impl, T: Type> ToSimValue for Arc { - fn to_sim_value(&self, ty: T) -> SimValue { - This::arc_to_sim_value(self, ty) - } - fn into_sim_value(self, ty: T) -> SimValue { - This::arc_into_sim_value(self, ty) +impl ToSimValue for &'_ mut This { + type Type = This::Type; + + fn to_sim_value(&self) -> SimValue { + This::to_sim_value(self) } } -impl + Send + Sync + 'static, T: Type> ToSimValue +impl, T: Type> ToSimValueWithType for &'_ mut This { + fn to_sim_value_with_type(&self, ty: T) -> SimValue { + This::to_sim_value_with_type(self, ty) + } +} + +impl ToSimValue for Arc { + type Type = This::Type; + + fn to_sim_value(&self) -> SimValue { + This::arc_to_sim_value(self) + } + fn into_sim_value(self) -> SimValue { + This::arc_into_sim_value(self) + } +} + +impl, T: Type> ToSimValueWithType for Arc { + fn to_sim_value_with_type(&self, ty: T) -> SimValue { + This::arc_to_sim_value_with_type(self, ty) + } + fn into_sim_value_with_type(self, ty: T) -> SimValue { + This::arc_into_sim_value_with_type(self, ty) + } +} + +impl ToSimValue for crate::intern::Interned { - fn to_sim_value(&self, ty: T) -> SimValue { - This::to_sim_value(self, ty) + type Type = This::Type; + fn to_sim_value(&self) -> SimValue { + This::to_sim_value(self) } } -impl, T: Type> ToSimValue for Box { - fn to_sim_value(&self, ty: T) -> SimValue { - This::to_sim_value(self, ty) +impl + Send + Sync + 'static, T: Type> ToSimValueWithType + for crate::intern::Interned +{ + fn to_sim_value_with_type(&self, ty: T) -> SimValue { + This::to_sim_value_with_type(self, ty) } - fn into_sim_value(self, ty: T) -> SimValue { - This::into_sim_value(*self, ty) +} + +impl ToSimValue for Box { + type Type = This::Type; + + fn to_sim_value(&self) -> SimValue { + This::to_sim_value(self) + } + fn into_sim_value(self) -> SimValue { + This::into_sim_value(*self) + } +} + +impl, T: Type> ToSimValueWithType for Box { + fn to_sim_value_with_type(&self, ty: T) -> SimValue { + This::to_sim_value_with_type(self, ty) + } + fn into_sim_value_with_type(self, ty: T) -> SimValue { + This::into_sim_value_with_type(*self, ty) } } impl SimValue> { #[track_caller] - pub fn from_array_elements>>( + pub fn from_array_elements>>( ty: ArrayType, elements: I, ) -> Self { @@ -430,23 +531,32 @@ impl SimValue> { let elements = Vec::from_iter( elements .into_iter() - .map(|element| element.into_sim_value(element_ty)), + .map(|element| element.into_sim_value_with_type(element_ty)), ); assert_eq!(elements.len(), ty.len()); SimValue::from_value(ty, elements.try_into().ok().expect("already checked len")) } } -impl, T: Type> ToSimValue> for [Element] { +impl, T: Type> ToSimValueWithType> for [Element] { #[track_caller] - fn to_sim_value(&self, ty: Array) -> SimValue> { + fn to_sim_value_with_type(&self, ty: Array) -> SimValue> { SimValue::from_array_elements(ty, self) } } -impl> ToSimValue for [Element] { +impl> ToSimValue for [Element] { + type Type = Array; + #[track_caller] - fn to_sim_value(&self, ty: CanonicalType) -> SimValue { + fn to_sim_value(&self) -> SimValue { + SimValue::from_array_elements(ArrayType::new_dyn(StaticType::TYPE, self.len()), self) + } +} + +impl> ToSimValueWithType for [Element] { + #[track_caller] + fn to_sim_value_with_type(&self, ty: CanonicalType) -> SimValue { SimValue::into_canonical(SimValue::from_array_elements( ::from_canonical(ty), self, @@ -454,71 +564,61 @@ impl> ToSimValue for [Element] } } -impl, T: Type, const N: usize> ToSimValue> for [Element; N] +impl, T: Type, const N: usize> ToSimValueWithType> + for [Element; N] where ConstUsize: KnownSize, { #[track_caller] - fn to_sim_value(&self, ty: Array) -> SimValue> { + fn to_sim_value_with_type(&self, ty: Array) -> SimValue> { SimValue::from_array_elements(ty, self) } #[track_caller] - fn into_sim_value(self, ty: Array) -> SimValue> { + fn into_sim_value_with_type(self, ty: Array) -> SimValue> { SimValue::from_array_elements(ty, self) } } -impl, T: Type, const N: usize> ToSimValue> for [Element; N] { - #[track_caller] - fn to_sim_value(&self, ty: Array) -> SimValue> { - SimValue::from_array_elements(ty, self) +impl, const N: usize> ToSimValue for [Element; N] +where + ConstUsize: KnownSize, +{ + type Type = Array; + + fn to_sim_value(&self) -> SimValue { + SimValue::from_array_elements(StaticType::TYPE, self) } - #[track_caller] - fn into_sim_value(self, ty: Array) -> SimValue> { - SimValue::from_array_elements(ty, self) + + fn into_sim_value(self) -> SimValue { + SimValue::from_array_elements(StaticType::TYPE, self) } } -impl, const N: usize> ToSimValue +impl, T: Type, const N: usize> ToSimValueWithType> for [Element; N] { #[track_caller] - fn to_sim_value(&self, ty: CanonicalType) -> SimValue { - SimValue::into_canonical(SimValue::from_array_elements( - ::from_canonical(ty), - self, - )) - } - #[track_caller] - fn into_sim_value(self, ty: CanonicalType) -> SimValue { - SimValue::into_canonical(SimValue::from_array_elements( - ::from_canonical(ty), - self, - )) - } -} - -impl, T: Type> ToSimValue> for Vec { - #[track_caller] - fn to_sim_value(&self, ty: Array) -> SimValue> { + fn to_sim_value_with_type(&self, ty: Array) -> SimValue> { SimValue::from_array_elements(ty, self) } #[track_caller] - fn into_sim_value(self, ty: Array) -> SimValue> { + fn into_sim_value_with_type(self, ty: Array) -> SimValue> { SimValue::from_array_elements(ty, self) } } -impl> ToSimValue for Vec { +impl, const N: usize> ToSimValueWithType + for [Element; N] +{ #[track_caller] - fn to_sim_value(&self, ty: CanonicalType) -> SimValue { + fn to_sim_value_with_type(&self, ty: CanonicalType) -> SimValue { SimValue::into_canonical(SimValue::from_array_elements( ::from_canonical(ty), self, )) } #[track_caller] - fn into_sim_value(self, ty: CanonicalType) -> SimValue { + fn into_sim_value_with_type(self, ty: CanonicalType) -> SimValue { SimValue::into_canonical(SimValue::from_array_elements( ::from_canonical(ty), self, @@ -526,37 +626,131 @@ impl> ToSimValue for Vec ToSimValue for Expr { +impl, T: Type> ToSimValueWithType> for Vec { #[track_caller] - fn to_sim_value(&self, ty: T) -> SimValue { - assert_eq!(Expr::ty(*self), ty); + fn to_sim_value_with_type(&self, ty: Array) -> SimValue> { + SimValue::from_array_elements(ty, self) + } + #[track_caller] + fn into_sim_value_with_type(self, ty: Array) -> SimValue> { + SimValue::from_array_elements(ty, self) + } +} + +impl> ToSimValue for Vec { + type Type = Array; + + fn to_sim_value(&self) -> SimValue { + SimValue::from_array_elements(ArrayType::new_dyn(StaticType::TYPE, self.len()), self) + } + + fn into_sim_value(self) -> SimValue { + SimValue::from_array_elements(ArrayType::new_dyn(StaticType::TYPE, self.len()), self) + } +} + +impl> ToSimValueWithType + for Vec +{ + #[track_caller] + fn to_sim_value_with_type(&self, ty: CanonicalType) -> SimValue { + SimValue::into_canonical(SimValue::from_array_elements( + ::from_canonical(ty), + self, + )) + } + #[track_caller] + fn into_sim_value_with_type(self, ty: CanonicalType) -> SimValue { + SimValue::into_canonical(SimValue::from_array_elements( + ::from_canonical(ty), + self, + )) + } +} + +impl, T: Type> ToSimValueWithType> for Box<[Element]> { + #[track_caller] + fn to_sim_value_with_type(&self, ty: Array) -> SimValue> { + SimValue::from_array_elements(ty, self) + } + #[track_caller] + fn into_sim_value_with_type(self, ty: Array) -> SimValue> { + SimValue::from_array_elements(ty, self) + } +} + +impl> ToSimValue for Box<[Element]> { + type Type = Array; + + fn to_sim_value(&self) -> SimValue { + SimValue::from_array_elements(ArrayType::new_dyn(StaticType::TYPE, self.len()), self) + } + + fn into_sim_value(self) -> SimValue { + SimValue::from_array_elements(ArrayType::new_dyn(StaticType::TYPE, self.len()), self) + } +} + +impl> ToSimValueWithType + for Box<[Element]> +{ + #[track_caller] + fn to_sim_value_with_type(&self, ty: CanonicalType) -> SimValue { + SimValue::into_canonical(SimValue::from_array_elements( + ::from_canonical(ty), + self, + )) + } + #[track_caller] + fn into_sim_value_with_type(self, ty: CanonicalType) -> SimValue { + SimValue::into_canonical(SimValue::from_array_elements( + ::from_canonical(ty), + self, + )) + } +} + +impl ToSimValue for Expr { + type Type = T; + #[track_caller] + fn to_sim_value(&self) -> SimValue { SimValue::from_bitslice( - ty, + Expr::ty(*self), &crate::expr::ToLiteralBits::to_literal_bits(self) .expect("must be a literal expression"), ) } } +forward_to_sim_value_with_type!([T: Type] Expr); + macro_rules! impl_to_sim_value_for_bool_like { ($ty:ident) => { - impl ToSimValue<$ty> for bool { - fn to_sim_value(&self, ty: $ty) -> SimValue<$ty> { + impl ToSimValueWithType<$ty> for bool { + fn to_sim_value_with_type(&self, ty: $ty) -> SimValue<$ty> { SimValue::from_value(ty, *self) } } }; } +impl ToSimValue for bool { + type Type = Bool; + + fn to_sim_value(&self) -> SimValue { + SimValue::from_value(Bool, *self) + } +} + impl_to_sim_value_for_bool_like!(Bool); impl_to_sim_value_for_bool_like!(AsyncReset); impl_to_sim_value_for_bool_like!(SyncReset); impl_to_sim_value_for_bool_like!(Reset); impl_to_sim_value_for_bool_like!(Clock); -impl ToSimValue for bool { +impl ToSimValueWithType for bool { #[track_caller] - fn to_sim_value(&self, ty: CanonicalType) -> SimValue { + fn to_sim_value_with_type(&self, ty: CanonicalType) -> SimValue { match ty { CanonicalType::UInt(_) | CanonicalType::SInt(_) @@ -579,19 +773,22 @@ impl ToSimValue for bool { macro_rules! impl_to_sim_value_for_primitive_int { ($prim:ident) => { - impl ToSimValue<<$prim as ToExpr>::Type> for $prim { + impl ToSimValue for $prim { + type Type = <$prim as ToExpr>::Type; + #[track_caller] fn to_sim_value( &self, - ty: <$prim as ToExpr>::Type, - ) -> SimValue<<$prim as ToExpr>::Type> { - SimValue::from_value(ty, (*self).into()) + ) -> SimValue { + SimValue::from_value(StaticType::TYPE, (*self).into()) } } - impl ToSimValue<<<$prim as ToExpr>::Type as IntType>::Dyn> for $prim { + forward_to_sim_value_with_type!([] $prim); + + impl ToSimValueWithType<<<$prim as ToExpr>::Type as IntType>::Dyn> for $prim { #[track_caller] - fn to_sim_value( + fn to_sim_value_with_type( &self, ty: <<$prim as ToExpr>::Type as IntType>::Dyn, ) -> SimValue<<<$prim as ToExpr>::Type as IntType>::Dyn> { @@ -602,11 +799,11 @@ macro_rules! impl_to_sim_value_for_primitive_int { } } - impl ToSimValue for $prim { + impl ToSimValueWithType for $prim { #[track_caller] - fn to_sim_value(&self, ty: CanonicalType) -> SimValue { + fn to_sim_value_with_type(&self, ty: CanonicalType) -> SimValue { let ty: <<$prim as ToExpr>::Type as IntType>::Dyn = Type::from_canonical(ty); - SimValue::into_canonical(self.to_sim_value(ty)) + SimValue::into_canonical(self.to_sim_value_with_type(ty)) } } }; @@ -627,35 +824,51 @@ impl_to_sim_value_for_primitive_int!(isize); macro_rules! impl_to_sim_value_for_int_value { ($IntValue:ident, $Int:ident, $IntType:ident) => { - impl ToSimValue<$IntType> for $IntValue { - fn to_sim_value(&self, ty: $IntType) -> SimValue<$IntType> { - self.bits().to_sim_value(ty) + impl ToSimValue for $IntValue { + type Type = $IntType; + + fn to_sim_value(&self) -> SimValue { + SimValue::from_value(self.ty(), self.clone()) } - fn into_sim_value(self, ty: $IntType) -> SimValue<$IntType> { - self.into_bits().into_sim_value(ty) + fn into_sim_value(self) -> SimValue { + SimValue::from_value(self.ty(), self) } } - impl ToSimValue<$Int> for $IntValue { - fn to_sim_value(&self, ty: $Int) -> SimValue<$Int> { - self.bits().to_sim_value(ty) + impl ToSimValueWithType<$IntType> for $IntValue { + fn to_sim_value_with_type(&self, ty: $IntType) -> SimValue<$IntType> { + SimValue::from_value(ty, self.clone()) } - fn into_sim_value(self, ty: $Int) -> SimValue<$Int> { - self.into_bits().into_sim_value(ty) + fn into_sim_value_with_type(self, ty: $IntType) -> SimValue<$IntType> { + SimValue::from_value(ty, self) } } - impl ToSimValue for $IntValue { + impl ToSimValueWithType<$Int> for $IntValue { + fn to_sim_value_with_type(&self, ty: $Int) -> SimValue<$Int> { + self.bits().to_sim_value_with_type(ty) + } + + fn into_sim_value_with_type(self, ty: $Int) -> SimValue<$Int> { + self.into_bits().into_sim_value_with_type(ty) + } + } + + impl ToSimValueWithType for $IntValue { #[track_caller] - fn to_sim_value(&self, ty: CanonicalType) -> SimValue { - SimValue::into_canonical(self.to_sim_value($Int::from_canonical(ty))) + fn to_sim_value_with_type(&self, ty: CanonicalType) -> SimValue { + SimValue::into_canonical( + self.to_sim_value_with_type($IntType::::from_canonical(ty)), + ) } #[track_caller] - fn into_sim_value(self, ty: CanonicalType) -> SimValue { - SimValue::into_canonical(self.into_sim_value($Int::from_canonical(ty))) + fn into_sim_value_with_type(self, ty: CanonicalType) -> SimValue { + SimValue::into_canonical( + self.into_sim_value_with_type($IntType::::from_canonical(ty)), + ) } } }; diff --git a/crates/fayalite/src/ty.rs b/crates/fayalite/src/ty.rs index cd26c9b..23680f7 100644 --- a/crates/fayalite/src/ty.rs +++ b/crates/fayalite/src/ty.rs @@ -11,6 +11,7 @@ use crate::{ intern::{Intern, Interned}, phantom_const::PhantomConst, reset::{AsyncReset, Reset, SyncReset}, + sim::value::{SimValue, ToSimValueWithType}, source_location::SourceLocation, util::ConstUsize, }; @@ -269,7 +270,7 @@ pub trait Type: { type BaseType: BaseType; type MaskType: Type; - type SimValue: fmt::Debug + Clone + 'static; + type SimValue: fmt::Debug + Clone + 'static + ToSimValueWithType; type MatchVariant: 'static + Send + Sync; type MatchActiveScope; type MatchVariantAndInactiveScope: MatchVariantAndInactiveScope< @@ -389,6 +390,15 @@ impl OpaqueSimValue { } } +impl> ToSimValueWithType for OpaqueSimValue { + fn to_sim_value_with_type(&self, ty: T) -> SimValue { + SimValue::from_value(ty, self.clone()) + } + fn into_sim_value_with_type(self, ty: T) -> SimValue { + SimValue::from_value(ty, self) + } +} + pub trait StaticType: Type { const TYPE: Self; const MASK_TYPE: Self::MaskType; diff --git a/crates/fayalite/tests/sim.rs b/crates/fayalite/tests/sim.rs index eb5c79e..398fe18 100644 --- a/crates/fayalite/tests/sim.rs +++ b/crates/fayalite/tests/sim.rs @@ -7,13 +7,7 @@ use fayalite::{ module::{instance_with_loc, reg_builder_with_loc}, prelude::*, reset::ResetType, - sim::{ - time::SimDuration, - value::{SimValue, ToSimValue}, - vcd::VcdWriterDecls, - Simulation, - }, - ty::StaticType, + sim::{time::SimDuration, vcd::VcdWriterDecls, Simulation}, util::RcWriter, }; use std::num::NonZeroUsize; @@ -391,113 +385,110 @@ fn test_enums() { let mut sim = Simulation::new(enums()); let mut writer = RcWriter::default(); sim.add_trace_writer(VcdWriterDecls::new(writer.clone())); - sim.write_clock(sim.io().cd.clk, false); - sim.write_reset(sim.io().cd.rst, true); - sim.write_bool(sim.io().en, false); - sim.write_bool_or_int(sim.io().which_in, 0_hdl_u2); - sim.write_bool_or_int(sim.io().data_in, 0_hdl_u4); + sim.write(sim.io().cd.clk, false); + sim.write(sim.io().cd.rst, true); + sim.write(sim.io().en, false); + sim.write(sim.io().which_in, 0_hdl_u2); + sim.write(sim.io().data_in, 0_hdl_u4); sim.advance_time(SimDuration::from_micros(1)); - sim.write_clock(sim.io().cd.clk, true); + sim.write(sim.io().cd.clk, true); sim.advance_time(SimDuration::from_nanos(100)); - sim.write_reset(sim.io().cd.rst, false); + sim.write(sim.io().cd.rst, false); sim.advance_time(SimDuration::from_nanos(900)); - type BOutTy = HdlOption<(UInt<1>, Bool)>; - #[derive(Debug, PartialEq)] + #[hdl(cmp_eq)] struct IO { - en: bool, - which_in: u8, - data_in: u8, - which_out: u8, - data_out: u8, - b_out: SimValue, + en: Bool, + which_in: UInt<2>, + data_in: UInt<4>, + which_out: UInt<2>, + data_out: UInt<4>, + b_out: HdlOption<(UInt<1>, Bool)>, } let io_cycles = [ + #[hdl(sim)] IO { en: false, - which_in: 0, - data_in: 0, - which_out: 0, - data_out: 0, - b_out: HdlNone().to_sim_value(StaticType::TYPE), + which_in: 0_hdl_u2, + data_in: 0_hdl_u4, + which_out: 0_hdl_u2, + data_out: 0_hdl_u4, + b_out: HdlNone(), }, + #[hdl(sim)] IO { en: true, - which_in: 1, - data_in: 0, - which_out: 0, - data_out: 0, - b_out: HdlNone().to_sim_value(StaticType::TYPE), + which_in: 1_hdl_u2, + data_in: 0_hdl_u4, + which_out: 0_hdl_u2, + data_out: 0_hdl_u4, + b_out: HdlNone(), }, + #[hdl(sim)] IO { en: false, - which_in: 0, - data_in: 0, - which_out: 1, - data_out: 0, - b_out: HdlSome((0_hdl_u1, false)).to_sim_value(StaticType::TYPE), + which_in: 0_hdl_u2, + data_in: 0_hdl_u4, + which_out: 1_hdl_u2, + data_out: 0_hdl_u4, + b_out: HdlSome((0_hdl_u1, false)), }, + #[hdl(sim)] IO { en: true, - which_in: 1, - data_in: 0xF, - which_out: 1, - data_out: 0, - b_out: HdlSome((0_hdl_u1, false)).to_sim_value(StaticType::TYPE), + which_in: 1_hdl_u2, + data_in: 0xF_hdl_u4, + which_out: 1_hdl_u2, + data_out: 0_hdl_u4, + b_out: HdlSome((0_hdl_u1, false)), }, + #[hdl(sim)] IO { en: true, - which_in: 1, - data_in: 0xF, - which_out: 1, - data_out: 0x3, - b_out: HdlSome((1_hdl_u1, true)).to_sim_value(StaticType::TYPE), + which_in: 1_hdl_u2, + data_in: 0xF_hdl_u4, + which_out: 1_hdl_u2, + data_out: 0x3_hdl_u4, + b_out: HdlSome((1_hdl_u1, true)), }, + #[hdl(sim)] IO { en: true, - which_in: 2, - data_in: 0xF, - which_out: 1, - data_out: 0x3, - b_out: HdlSome((1_hdl_u1, true)).to_sim_value(StaticType::TYPE), + which_in: 2_hdl_u2, + data_in: 0xF_hdl_u4, + which_out: 1_hdl_u2, + data_out: 0x3_hdl_u4, + b_out: HdlSome((1_hdl_u1, true)), }, + #[hdl(sim)] IO { en: true, - which_in: 2, - data_in: 0xF, - which_out: 2, - data_out: 0xF, - b_out: HdlNone().to_sim_value(StaticType::TYPE), + which_in: 2_hdl_u2, + data_in: 0xF_hdl_u4, + which_out: 2_hdl_u2, + data_out: 0xF_hdl_u4, + b_out: HdlNone(), }, ]; - for ( - cycle, - expected @ IO { + for (cycle, expected) in io_cycles.into_iter().enumerate() { + #[hdl(sim)] + let IO { en, which_in, data_in, which_out: _, data_out: _, b_out: _, - }, - ) in io_cycles.into_iter().enumerate() - { - sim.write_bool(sim.io().en, en); - sim.write_bool_or_int(sim.io().which_in, which_in.cast_to_static()); - sim.write_bool_or_int(sim.io().data_in, data_in.cast_to_static()); - let io = IO { + } = expected; + sim.write(sim.io().en, &en); + sim.write(sim.io().which_in, &which_in); + sim.write(sim.io().data_in, &data_in); + let io = #[hdl(sim)] + IO { en, which_in, data_in, - which_out: sim - .read_bool_or_int(sim.io().which_out) - .to_bigint() - .try_into() - .expect("known to be in range"), - data_out: sim - .read_bool_or_int(sim.io().data_out) - .to_bigint() - .try_into() - .expect("known to be in range"), + which_out: sim.read(sim.io().which_out), + data_out: sim.read(sim.io().data_out), b_out: sim.read(sim.io().b_out), }; assert_eq!( @@ -559,7 +550,7 @@ fn test_memories() { w_mask: (Bool, Bool), } let io_cycles = [ - #[hdl] + #[hdl(sim)] IO { r_addr: 0_hdl_u4, r_en: false, @@ -569,7 +560,7 @@ fn test_memories() { w_data: (0u8, 0i8), w_mask: (false, false), }, - #[hdl] + #[hdl(sim)] IO { r_addr: 0_hdl_u4, r_en: true, @@ -579,7 +570,7 @@ fn test_memories() { w_data: (0x10u8, 0x20i8), w_mask: (true, true), }, - #[hdl] + #[hdl(sim)] IO { r_addr: 0_hdl_u4, r_en: true, @@ -589,7 +580,7 @@ fn test_memories() { w_data: (0x30u8, 0x40i8), w_mask: (false, true), }, - #[hdl] + #[hdl(sim)] IO { r_addr: 0_hdl_u4, r_en: true, @@ -599,7 +590,7 @@ fn test_memories() { w_data: (0x50u8, 0x60i8), w_mask: (true, false), }, - #[hdl] + #[hdl(sim)] IO { r_addr: 0_hdl_u4, r_en: true, @@ -609,7 +600,7 @@ fn test_memories() { w_data: (0x70u8, -0x80i8), w_mask: (false, false), }, - #[hdl] + #[hdl(sim)] IO { r_addr: 0_hdl_u4, r_en: true, @@ -619,7 +610,7 @@ fn test_memories() { w_data: (0x90u8, 0xA0u8 as i8), w_mask: (false, false), }, - #[hdl] + #[hdl(sim)] IO { r_addr: 0_hdl_u4, r_en: true, @@ -629,7 +620,7 @@ fn test_memories() { w_data: (0x90u8, 0xA0u8 as i8), w_mask: (true, true), }, - #[hdl] + #[hdl(sim)] IO { r_addr: 0_hdl_u4, r_en: true, @@ -639,7 +630,7 @@ fn test_memories() { w_data: (0xB0u8, 0xC0u8 as i8), w_mask: (true, true), }, - #[hdl] + #[hdl(sim)] IO { r_addr: 0_hdl_u4, r_en: true, @@ -649,7 +640,7 @@ fn test_memories() { w_data: (0xD0u8, 0xE0u8 as i8), w_mask: (true, true), }, - #[hdl] + #[hdl(sim)] IO { r_addr: 1_hdl_u4, r_en: true, @@ -659,7 +650,7 @@ fn test_memories() { w_data: (0xD0u8, 0xE0u8 as i8), w_mask: (true, true), }, - #[hdl] + #[hdl(sim)] IO { r_addr: 2_hdl_u4, r_en: true, @@ -671,7 +662,7 @@ fn test_memories() { }, ]; for (cycle, expected) in io_cycles.into_iter().enumerate() { - #[hdl] + #[hdl(sim)] let IO { r_addr, r_en, @@ -681,13 +672,13 @@ fn test_memories() { w_data, w_mask, } = expected; - sim.write(sim.io().r.addr, r_addr); - sim.write(sim.io().r.en, r_en); - sim.write(sim.io().w.addr, w_addr); - sim.write(sim.io().w.en, w_en); - sim.write(sim.io().w.data, w_data); - sim.write(sim.io().w.mask, w_mask); - let io = (#[hdl] + sim.write(sim.io().r.addr, &r_addr); + sim.write(sim.io().r.en, &r_en); + sim.write(sim.io().w.addr, &w_addr); + sim.write(sim.io().w.en, &w_en); + sim.write(sim.io().w.data, &w_data); + sim.write(sim.io().w.mask, &w_mask); + let io = #[hdl(sim)] IO { r_addr, r_en, @@ -696,20 +687,19 @@ fn test_memories() { w_en, w_data, w_mask, - }) - .to_sim_value(StaticType::TYPE); + }; assert_eq!( - expected.to_sim_value(StaticType::TYPE), + expected, io, "vcd:\n{}\ncycle: {cycle}", String::from_utf8(writer.take()).unwrap(), ); sim.advance_time(SimDuration::from_micros(1)); - sim.write_clock(sim.io().r.clk, true); - sim.write_clock(sim.io().w.clk, true); + sim.write(sim.io().r.clk, true); + sim.write(sim.io().w.clk, true); sim.advance_time(SimDuration::from_micros(1)); - sim.write_clock(sim.io().r.clk, false); - sim.write_clock(sim.io().w.clk, false); + sim.write(sim.io().r.clk, false); + sim.write(sim.io().w.clk, false); } sim.flush_traces().unwrap(); let vcd = String::from_utf8(writer.take()).unwrap(); From 9092e45447136e332a346c82d4e2bb6bb16dac0f Mon Sep 17 00:00:00 2001 From: Jacob Lifshay Date: Sun, 30 Mar 2025 01:25:07 -0700 Subject: [PATCH 20/38] fix #[hdl(sim)] match on enums --- .../src/module/transform_body/expand_match.rs | 46 +++++++++++++++++-- crates/fayalite/tests/sim.rs | 6 +++ 2 files changed, 47 insertions(+), 5 deletions(-) diff --git a/crates/fayalite-proc-macros-impl/src/module/transform_body/expand_match.rs b/crates/fayalite-proc-macros-impl/src/module/transform_body/expand_match.rs index 57e919a..a2e0375 100644 --- a/crates/fayalite-proc-macros-impl/src/module/transform_body/expand_match.rs +++ b/crates/fayalite-proc-macros-impl/src/module/transform_body/expand_match.rs @@ -83,7 +83,14 @@ visit_trait! { } } fn visit_match_pat_enum_variant(state: _, v: &MatchPatEnumVariant) { - let MatchPatEnumVariant {match_span:_, variant_path: _, enum_path: _, variant_name: _, field } = v; + let MatchPatEnumVariant { + match_span:_, + sim:_, + variant_path: _, + enum_path: _, + variant_name: _, + field, + } = v; if let Some((_, v)) = field { state.visit_match_pat_simple(v); } @@ -293,6 +300,7 @@ impl ToTokens for MatchPatTuple { with_debug_clone_and_fold! { struct MatchPatEnumVariant<> { match_span: Span, + sim: Option<(kw::sim,)>, variant_path: Path, enum_path: Path, variant_name: Ident, @@ -304,6 +312,7 @@ impl ToTokens for MatchPatEnumVariant { fn to_tokens(&self, tokens: &mut TokenStream) { let Self { match_span, + sim, variant_path: _, enum_path, variant_name, @@ -313,7 +322,28 @@ impl ToTokens for MatchPatEnumVariant { __MatchTy::<#enum_path>::#variant_name } .to_tokens(tokens); - if let Some((paren_token, field)) = field { + if sim.is_some() { + if let Some((paren_token, field)) = field { + paren_token.surround(tokens, |tokens| { + field.to_tokens(tokens); + match field { + MatchPatSimple::Paren(_) + | MatchPatSimple::Or(_) + | MatchPatSimple::Binding(_) + | MatchPatSimple::Wild(_) => quote_spanned! {*match_span=> + , _ + } + .to_tokens(tokens), + MatchPatSimple::Rest(_) => {} + } + }); + } else { + quote_spanned! {*match_span=> + (_) + } + .to_tokens(tokens); + } + } else if let Some((paren_token, field)) = field { paren_token.surround(tokens, |tokens| field.to_tokens(tokens)); } } @@ -448,6 +478,7 @@ trait ParseMatchPat: Sized { state, MatchPatEnumVariant { match_span: state.match_span, + sim: state.sim, variant_path, enum_path, variant_name, @@ -494,6 +525,7 @@ trait ParseMatchPat: Sized { state, MatchPatEnumVariant { match_span: state.match_span, + sim: state.sim, variant_path, enum_path, variant_name, @@ -578,6 +610,7 @@ trait ParseMatchPat: Sized { state, MatchPatEnumVariant { match_span: state.match_span, + sim: state.sim, variant_path, enum_path, variant_name, @@ -940,6 +973,7 @@ impl Fold for RewriteAsCheckMatch { } struct HdlMatchParseState<'a> { + sim: Option<(kw::sim,)>, match_span: Span, errors: &'a mut Errors, } @@ -986,6 +1020,7 @@ impl Visitor<'_> { mut let_stmt: Local, ) -> Local { let span = let_stmt.let_token.span(); + let ExprOptions { sim } = hdl_attr.body; if let Pat::Type(pat) = &mut let_stmt.pat { *pat.ty = wrap_ty_with_expr((*pat.ty).clone()); } @@ -1015,6 +1050,7 @@ impl Visitor<'_> { } let Ok(pat) = MatchPat::parse( &mut HdlMatchParseState { + sim, match_span: span, errors: &mut self.errors, }, @@ -1031,7 +1067,6 @@ impl Visitor<'_> { errors: _, bindings, } = state; - let ExprOptions { sim } = hdl_attr.body; let retval = if sim.is_some() { parse_quote_spanned! {span=> let (#(#bindings,)*) = { @@ -1093,7 +1128,9 @@ impl Visitor<'_> { brace_token: _, arms, } = expr_match; + let ExprOptions { sim } = hdl_attr.body; let mut state = HdlMatchParseState { + sim, match_span: span, errors: &mut self.errors, }; @@ -1101,13 +1138,12 @@ impl Visitor<'_> { arms.into_iter() .filter_map(|arm| MatchArm::parse(&mut state, arm).ok()), ); - let ExprOptions { sim } = hdl_attr.body; let expr = if sim.is_some() { quote_spanned! {span=> { type __MatchTy = ::SimValue; let __match_expr = ::fayalite::sim::value::ToSimValue::to_sim_value(&(#expr)); - #match_token *__match_expr { + #match_token ::fayalite::sim::value::SimValue::into_value(__match_expr) { #(#arms)* } } diff --git a/crates/fayalite/tests/sim.rs b/crates/fayalite/tests/sim.rs index 398fe18..71e53ea 100644 --- a/crates/fayalite/tests/sim.rs +++ b/crates/fayalite/tests/sim.rs @@ -497,6 +497,12 @@ fn test_enums() { "vcd:\n{}\ncycle: {cycle}", String::from_utf8(writer.take()).unwrap(), ); + // make sure matching on SimValue works + #[hdl(sim)] + match io.b_out { + HdlNone => println!("io.b_out is HdlNone"), + HdlSome(v) => println!("io.b_out is HdlSome(({:?}, {:?}))", *v.0, *v.1), + } sim.write_clock(sim.io().cd.clk, false); sim.advance_time(SimDuration::from_micros(1)); sim.write_clock(sim.io().cd.clk, true); From c4b6a0fee6501cf0dde3fb06422af3a96eca5fe3 Mon Sep 17 00:00:00 2001 From: Jacob Lifshay Date: Tue, 1 Apr 2025 22:05:42 -0700 Subject: [PATCH 21/38] add support for #[hdl(sim)] enum_ty.Variant(value) and #[hdl(sim)] EnumTy::Variant(value) and non-sim variants too --- crates/fayalite-proc-macros-impl/src/fold.rs | 1 + .../fayalite-proc-macros-impl/src/hdl_enum.rs | 74 ++ .../src/module/transform_body.rs | 2 + .../expand_aggregate_literals.rs | 115 ++- .../src/module/transform_body/expand_match.rs | 10 +- crates/fayalite/src/enum_.rs | 25 + crates/fayalite/tests/sim.rs | 72 +- crates/fayalite/tests/sim/expected/enums.txt | 846 +++++++++++------- crates/fayalite/tests/sim/expected/enums.vcd | 42 +- 9 files changed, 817 insertions(+), 370 deletions(-) diff --git a/crates/fayalite-proc-macros-impl/src/fold.rs b/crates/fayalite-proc-macros-impl/src/fold.rs index 49cc8c1..22e7b82 100644 --- a/crates/fayalite-proc-macros-impl/src/fold.rs +++ b/crates/fayalite-proc-macros-impl/src/fold.rs @@ -220,6 +220,7 @@ forward_fold!(syn::ExprArray => fold_expr_array); forward_fold!(syn::ExprCall => fold_expr_call); forward_fold!(syn::ExprIf => fold_expr_if); forward_fold!(syn::ExprMatch => fold_expr_match); +forward_fold!(syn::ExprMethodCall => fold_expr_method_call); forward_fold!(syn::ExprPath => fold_expr_path); forward_fold!(syn::ExprRepeat => fold_expr_repeat); forward_fold!(syn::ExprStruct => fold_expr_struct); diff --git a/crates/fayalite-proc-macros-impl/src/hdl_enum.rs b/crates/fayalite-proc-macros-impl/src/hdl_enum.rs index dd09a73..6fb2a56 100644 --- a/crates/fayalite-proc-macros-impl/src/hdl_enum.rs +++ b/crates/fayalite-proc-macros-impl/src/hdl_enum.rs @@ -130,6 +130,8 @@ pub(crate) struct ParsedEnum { pub(crate) variants: Punctuated, pub(crate) match_variant_ident: Ident, pub(crate) sim_value_ident: Ident, + pub(crate) sim_builder_ident: Ident, + pub(crate) sim_builder_ty_field_ident: Ident, } impl ParsedEnum { @@ -192,6 +194,8 @@ impl ParsedEnum { variants, match_variant_ident: format_ident!("__{}__MatchVariant", ident), sim_value_ident: format_ident!("__{}__SimValue", ident), + sim_builder_ident: format_ident!("__{}__SimBuilder", ident), + sim_builder_ty_field_ident: format_ident!("__ty", span = ident.span()), ident, }) } @@ -210,6 +214,8 @@ impl ToTokens for ParsedEnum { variants, match_variant_ident, sim_value_ident, + sim_builder_ident, + sim_builder_ty_field_ident, } = self; let span = ident.span(); let ItemOptions { @@ -412,6 +418,33 @@ impl ToTokens for ParsedEnum { )), } .to_tokens(tokens); + let mut struct_attrs = attrs.clone(); + struct_attrs.push(parse_quote_spanned! {span=> + #[allow(dead_code, non_camel_case_types)] + }); + ItemStruct { + attrs: struct_attrs, + vis: vis.clone(), + struct_token: Token![struct](enum_token.span), + ident: sim_builder_ident.clone(), + generics: generics.into(), + fields: FieldsNamed { + brace_token: *brace_token, + named: Punctuated::from_iter([Field { + attrs: vec![], + vis: Visibility::Inherited, + mutability: FieldMutability::None, + ident: Some(sim_builder_ty_field_ident.clone()), + colon_token: Some(Token![:](span)), + ty: parse_quote_spanned! {span=> + #target #type_generics + }, + }]), + } + .into(), + semi_token: None, + } + .to_tokens(tokens); let mut enum_attrs = attrs.clone(); enum_attrs.push(parse_quote_spanned! {span=> #[::fayalite::__std::prelude::v1::derive( @@ -538,6 +571,25 @@ impl ToTokens for ParsedEnum { ) } } + #[automatically_derived] + impl #impl_generics #sim_builder_ident #type_generics + #where_clause + { + #[allow(non_snake_case, dead_code)] + #vis fn #ident<__V: ::fayalite::sim::value::ToSimValueWithType<#ty>>( + #self_token, + v: __V, + ) -> ::fayalite::sim::value::SimValue<#target #type_generics> { + let v = ::fayalite::sim::value::ToSimValueWithType::into_sim_value_with_type( + v, + #self_token.#sim_builder_ty_field_ident.#ident, + ); + ::fayalite::sim::value::SimValue::from_value( + #self_token.#sim_builder_ty_field_ident, + #sim_value_ident::#ident(v, ::fayalite::enum_::EnumPaddingSimValue::new()), + ) + } + } } } else { quote_spanned! {span=> @@ -556,6 +608,18 @@ impl ToTokens for ParsedEnum { ) } } + #[automatically_derived] + impl #impl_generics #sim_builder_ident #type_generics + #where_clause + { + #[allow(non_snake_case, dead_code)] + #vis fn #ident(#self_token) -> ::fayalite::sim::value::SimValue<#target #type_generics> { + ::fayalite::sim::value::SimValue::from_value( + #self_token.#sim_builder_ty_field_ident, + #sim_value_ident::#ident(::fayalite::enum_::EnumPaddingSimValue::new()), + ) + } + } } } .to_tokens(tokens); @@ -848,6 +912,7 @@ impl ToTokens for ParsedEnum { impl #impl_generics ::fayalite::enum_::EnumType for #target #type_generics #where_clause { + type SimBuilder = #sim_builder_ident #type_generics; fn match_activate_scope( v: ::MatchVariantAndInactiveScope, ) -> (::MatchVariant, ::MatchActiveScope) { @@ -884,6 +949,15 @@ impl ToTokens for ParsedEnum { ::fayalite::sim::value::SimValue::from_value(ty, self) } } + #[automatically_derived] + impl #impl_generics ::fayalite::__std::convert::From<#target #type_generics> + for #sim_builder_ident #type_generics + #where_clause + { + fn from(#sim_builder_ty_field_ident: #target #type_generics) -> Self { + Self { #sim_builder_ty_field_ident } + } + } } .to_tokens(tokens); if let (None, MaybeParsed::Parsed(generics)) = (no_static, &self.generics) { diff --git a/crates/fayalite-proc-macros-impl/src/module/transform_body.rs b/crates/fayalite-proc-macros-impl/src/module/transform_body.rs index 8f427a9..a0f8eb0 100644 --- a/crates/fayalite-proc-macros-impl/src/module/transform_body.rs +++ b/crates/fayalite-proc-macros-impl/src/module/transform_body.rs @@ -1687,6 +1687,8 @@ impl Fold for Visitor<'_> { Repeat => process_hdl_repeat, Struct => process_hdl_struct, Tuple => process_hdl_tuple, + MethodCall => process_hdl_method_call, + Call => process_hdl_call, } } } diff --git a/crates/fayalite-proc-macros-impl/src/module/transform_body/expand_aggregate_literals.rs b/crates/fayalite-proc-macros-impl/src/module/transform_body/expand_aggregate_literals.rs index 8892bd5..61f6c75 100644 --- a/crates/fayalite-proc-macros-impl/src/module/transform_body/expand_aggregate_literals.rs +++ b/crates/fayalite-proc-macros-impl/src/module/transform_body/expand_aggregate_literals.rs @@ -1,14 +1,20 @@ // SPDX-License-Identifier: LGPL-3.0-or-later // See Notices.txt for copyright information + use crate::{ kw, - module::transform_body::{ExprOptions, Visitor}, + module::transform_body::{ + expand_match::{parse_enum_path, EnumPath}, + ExprOptions, Visitor, + }, HdlAttr, }; use quote::{format_ident, quote_spanned}; +use std::mem; use syn::{ - parse_quote_spanned, spanned::Spanned, Expr, ExprArray, ExprPath, ExprRepeat, ExprStruct, - ExprTuple, FieldValue, TypePath, + parse_quote_spanned, punctuated::Punctuated, spanned::Spanned, token::Paren, Expr, ExprArray, + ExprCall, ExprGroup, ExprMethodCall, ExprParen, ExprPath, ExprRepeat, ExprStruct, ExprTuple, + FieldValue, Token, TypePath, }; impl Visitor<'_> { @@ -162,4 +168,107 @@ impl Visitor<'_> { } } } + pub(crate) fn process_hdl_call( + &mut self, + hdl_attr: HdlAttr, + mut expr_call: ExprCall, + ) -> Expr { + let span = hdl_attr.kw.span; + let mut func = &mut *expr_call.func; + let EnumPath { + variant_path: _, + enum_path, + variant_name, + } = loop { + match func { + Expr::Group(ExprGroup { expr, .. }) | Expr::Paren(ExprParen { expr, .. }) => { + func = &mut **expr; + } + Expr::Path(_) => { + let Expr::Path(ExprPath { attrs, qself, path }) = + mem::replace(func, Expr::PLACEHOLDER) + else { + unreachable!(); + }; + match parse_enum_path(TypePath { qself, path }) { + Ok(path) => break path, + Err(path) => { + self.errors.error(&path, "unsupported enum variant path"); + let TypePath { qself, path } = path; + *func = ExprPath { attrs, qself, path }.into(); + return expr_call.into(); + } + } + } + _ => { + self.errors.error( + &expr_call.func, + "#[hdl] function call -- function must be a possibly-parenthesized path", + ); + return expr_call.into(); + } + } + }; + self.process_hdl_method_call( + hdl_attr, + ExprMethodCall { + attrs: expr_call.attrs, + receiver: parse_quote_spanned! {span=> + <#enum_path as ::fayalite::ty::StaticType>::TYPE + }, + dot_token: Token![.](span), + method: variant_name, + turbofish: None, + paren_token: expr_call.paren_token, + args: expr_call.args, + }, + ) + } + pub(crate) fn process_hdl_method_call( + &mut self, + hdl_attr: HdlAttr, + mut expr_method_call: ExprMethodCall, + ) -> Expr { + let ExprOptions { sim } = hdl_attr.body; + let span = hdl_attr.kw.span; + // remove any number of groups and up to one paren + let mut receiver = &mut *expr_method_call.receiver; + let mut has_group = false; + let receiver = loop { + match receiver { + Expr::Group(ExprGroup { expr, .. }) => { + has_group = true; + receiver = expr; + } + Expr::Paren(ExprParen { expr, .. }) => break &mut **expr, + receiver @ Expr::Path(_) => break receiver, + _ => { + if !has_group { + self.errors.error( + &expr_method_call.receiver, + "#[hdl] on a method call needs parenthesized receiver", + ); + } + break &mut *expr_method_call.receiver; + } + } + }; + let func = if sim.is_some() { + parse_quote_spanned! {span=> + ::fayalite::enum_::enum_type_to_sim_builder + } + } else { + parse_quote_spanned! {span=> + ::fayalite::enum_::assert_is_enum_type + } + }; + *expr_method_call.receiver = ExprCall { + attrs: vec![], + func, + paren_token: Paren(span), + args: Punctuated::from_iter([mem::replace(receiver, Expr::PLACEHOLDER)]), + } + .into(); + expr_method_call.into() + } } diff --git a/crates/fayalite-proc-macros-impl/src/module/transform_body/expand_match.rs b/crates/fayalite-proc-macros-impl/src/module/transform_body/expand_match.rs index a2e0375..68218c1 100644 --- a/crates/fayalite-proc-macros-impl/src/module/transform_body/expand_match.rs +++ b/crates/fayalite-proc-macros-impl/src/module/transform_body/expand_match.rs @@ -380,13 +380,13 @@ impl ToTokens for MatchPatSimple { } } -struct EnumPath { - variant_path: Path, - enum_path: Path, - variant_name: Ident, +pub(crate) struct EnumPath { + pub(crate) variant_path: Path, + pub(crate) enum_path: Path, + pub(crate) variant_name: Ident, } -fn parse_enum_path(variant_path: TypePath) -> Result { +pub(crate) fn parse_enum_path(variant_path: TypePath) -> Result { let TypePath { qself: None, path: variant_path, diff --git a/crates/fayalite/src/enum_.rs b/crates/fayalite/src/enum_.rs index 9fa38e9..e37b7a5 100644 --- a/crates/fayalite/src/enum_.rs +++ b/crates/fayalite/src/enum_.rs @@ -259,6 +259,7 @@ pub trait EnumType: MatchVariantsIter = EnumMatchVariantsIter, > { + type SimBuilder: From; fn variants(&self) -> Interned<[EnumVariant]>; fn match_activate_scope( v: Self::MatchVariantAndInactiveScope, @@ -321,7 +322,18 @@ impl DoubleEndedIterator for EnumMatchVariantsIter { } } +pub struct NoBuilder { + _ty: Enum, +} + +impl From for NoBuilder { + fn from(_ty: Enum) -> Self { + Self { _ty } + } +} + impl EnumType for Enum { + type SimBuilder = NoBuilder; fn match_activate_scope( v: Self::MatchVariantAndInactiveScope, ) -> (Self::MatchVariant, Self::MatchActiveScope) { @@ -389,6 +401,9 @@ pub struct EnumPaddingSimValue { } impl EnumPaddingSimValue { + pub const fn new() -> Self { + Self { bits: None } + } pub fn bit_width(&self) -> Option { self.bits.as_ref().map(UIntValue::width) } @@ -659,6 +674,16 @@ impl<'a> EnumSimValueToBits<'a> { } } +#[doc(hidden)] +pub fn assert_is_enum_type(v: T) -> T { + v +} + +#[doc(hidden)] +pub fn enum_type_to_sim_builder(v: T) -> T::SimBuilder { + v.into() +} + #[hdl] pub enum HdlOption { HdlNone, diff --git a/crates/fayalite/tests/sim.rs b/crates/fayalite/tests/sim.rs index 71e53ea..6433844 100644 --- a/crates/fayalite/tests/sim.rs +++ b/crates/fayalite/tests/sim.rs @@ -317,8 +317,13 @@ pub fn enums() { let which_out: UInt<2> = m.output(); #[hdl] let data_out: UInt<4> = m.output(); + let b_out_ty = HdlOption[(UInt[1], Bool)]; #[hdl] - let b_out: HdlOption<(UInt<1>, Bool)> = m.output(); + let b_out: HdlOption<(UInt, Bool)> = m.output(HdlOption[(UInt[1], Bool)]); + #[hdl] + let b2_out: HdlOption<(UInt<1>, Bool)> = m.output(); + + connect_any(b2_out, b_out); #[hdl] struct MyStruct { @@ -358,7 +363,7 @@ pub fn enums() { } } - connect(b_out, HdlNone()); + connect(b_out, b_out_ty.HdlNone()); #[hdl] match the_reg { @@ -369,7 +374,7 @@ pub fn enums() { MyEnum::B(v) => { connect(which_out, 1_hdl_u2); connect_any(data_out, v.0 | (v.1.cast_to_static::>() << 1)); - connect(b_out, HdlSome(v)); + connect_any(b_out, HdlSome(v)); } MyEnum::C(v) => { connect(which_out, 2_hdl_u2); @@ -396,100 +401,125 @@ fn test_enums() { sim.write(sim.io().cd.rst, false); sim.advance_time(SimDuration::from_nanos(900)); #[hdl(cmp_eq)] - struct IO { + struct IO { en: Bool, which_in: UInt<2>, data_in: UInt<4>, which_out: UInt<2>, data_out: UInt<4>, - b_out: HdlOption<(UInt<1>, Bool)>, + b_out: HdlOption<(UIntType, Bool)>, + b2_out: HdlOption<(UInt<1>, Bool)>, } + let io_ty = IO[1]; let io_cycles = [ #[hdl(sim)] - IO { + IO::<_> { en: false, which_in: 0_hdl_u2, data_in: 0_hdl_u4, which_out: 0_hdl_u2, data_out: 0_hdl_u4, - b_out: HdlNone(), + b_out: #[hdl(sim)] + (io_ty.b_out).HdlNone(), + b2_out: #[hdl(sim)] + HdlNone(), }, #[hdl(sim)] - IO { + IO::<_> { en: true, which_in: 1_hdl_u2, data_in: 0_hdl_u4, which_out: 0_hdl_u2, data_out: 0_hdl_u4, - b_out: HdlNone(), + b_out: #[hdl(sim)] + (io_ty.b_out).HdlNone(), + b2_out: #[hdl(sim)] + HdlNone(), }, #[hdl(sim)] - IO { + IO::<_> { en: false, which_in: 0_hdl_u2, data_in: 0_hdl_u4, which_out: 1_hdl_u2, data_out: 0_hdl_u4, - b_out: HdlSome((0_hdl_u1, false)), + b_out: #[hdl(sim)] + (io_ty.b_out).HdlSome((0u8.cast_to(UInt[1]), false)), + b2_out: #[hdl(sim)] + HdlSome((0_hdl_u1, false)), }, #[hdl(sim)] - IO { + IO::<_> { en: true, which_in: 1_hdl_u2, data_in: 0xF_hdl_u4, which_out: 1_hdl_u2, data_out: 0_hdl_u4, - b_out: HdlSome((0_hdl_u1, false)), + b_out: #[hdl(sim)] + (io_ty.b_out).HdlSome((0u8.cast_to(UInt[1]), false)), + b2_out: #[hdl(sim)] + HdlSome((0_hdl_u1, false)), }, #[hdl(sim)] - IO { + IO::<_> { en: true, which_in: 1_hdl_u2, data_in: 0xF_hdl_u4, which_out: 1_hdl_u2, data_out: 0x3_hdl_u4, - b_out: HdlSome((1_hdl_u1, true)), + b_out: #[hdl(sim)] + (io_ty.b_out).HdlSome((1u8.cast_to(UInt[1]), true)), + b2_out: #[hdl(sim)] + HdlSome((1_hdl_u1, true)), }, #[hdl(sim)] - IO { + IO::<_> { en: true, which_in: 2_hdl_u2, data_in: 0xF_hdl_u4, which_out: 1_hdl_u2, data_out: 0x3_hdl_u4, - b_out: HdlSome((1_hdl_u1, true)), + b_out: #[hdl(sim)] + (io_ty.b_out).HdlSome((1u8.cast_to(UInt[1]), true)), + b2_out: #[hdl(sim)] + HdlSome((1_hdl_u1, true)), }, #[hdl(sim)] - IO { + IO::<_> { en: true, which_in: 2_hdl_u2, data_in: 0xF_hdl_u4, which_out: 2_hdl_u2, data_out: 0xF_hdl_u4, - b_out: HdlNone(), + b_out: #[hdl(sim)] + (io_ty.b_out).HdlNone(), + b2_out: #[hdl(sim)] + HdlNone(), }, ]; for (cycle, expected) in io_cycles.into_iter().enumerate() { #[hdl(sim)] - let IO { + let IO::<_> { en, which_in, data_in, which_out: _, data_out: _, b_out: _, + b2_out: _, } = expected; sim.write(sim.io().en, &en); sim.write(sim.io().which_in, &which_in); sim.write(sim.io().data_in, &data_in); let io = #[hdl(sim)] - IO { + IO::<_> { en, which_in, data_in, which_out: sim.read(sim.io().which_out), data_out: sim.read(sim.io().data_out), b_out: sim.read(sim.io().b_out), + b2_out: sim.read(sim.io().b2_out), }; assert_eq!( expected, diff --git a/crates/fayalite/tests/sim/expected/enums.txt b/crates/fayalite/tests/sim/expected/enums.txt index 089ea31..61ce5d5 100644 --- a/crates/fayalite/tests/sim/expected/enums.txt +++ b/crates/fayalite/tests/sim/expected/enums.txt @@ -4,7 +4,7 @@ Simulation { state_layout: StateLayout { ty: TypeLayout { small_slots: StatePartLayout { - len: 6, + len: 7, debug_data: [ SlotDebugData { name: "", @@ -13,6 +13,13 @@ Simulation { HdlSome, }, }, + SlotDebugData { + name: "", + ty: Enum { + HdlNone, + HdlSome, + }, + }, SlotDebugData { name: "", ty: Bool, @@ -41,7 +48,7 @@ Simulation { .. }, big_slots: StatePartLayout { - len: 103, + len: 111, debug_data: [ SlotDebugData { name: "InstantiatedModule(enums: enums).enums::cd.clk", @@ -106,6 +113,41 @@ Simulation { name: "", ty: Bool, }, + SlotDebugData { + name: "InstantiatedModule(enums: enums).enums::b2_out", + ty: Enum { + HdlNone, + HdlSome(Bundle {0: UInt<1>, 1: Bool}), + }, + }, + SlotDebugData { + name: ".0", + ty: UInt<1>, + }, + SlotDebugData { + name: ".1", + ty: Bool, + }, + SlotDebugData { + name: "", + ty: UInt<3>, + }, + SlotDebugData { + name: "", + ty: UInt<2>, + }, + SlotDebugData { + name: "", + ty: UInt<1>, + }, + SlotDebugData { + name: "", + ty: UInt<1>, + }, + SlotDebugData { + name: "", + ty: Bool, + }, SlotDebugData { name: "InstantiatedModule(enums: enums).enums::the_reg", ty: Enum { @@ -498,594 +540,640 @@ Simulation { insns: [ // at: module-XXXXXXXXXX.rs:1:1 0: Const { - dest: StatePartIndex(90), // (0x1) SlotDebugData { name: "", ty: UInt<1> }, + dest: StatePartIndex(98), // (0x1) SlotDebugData { name: "", ty: UInt<1> }, value: 0x1, }, 1: Const { - dest: StatePartIndex(82), // (0x0) SlotDebugData { name: "", ty: UInt<4> }, + dest: StatePartIndex(90), // (0x0) SlotDebugData { name: "", ty: UInt<4> }, value: 0x0, }, 2: Const { - dest: StatePartIndex(80), // (0x0) SlotDebugData { name: "", ty: UInt<3> }, + dest: StatePartIndex(88), // (0x0) SlotDebugData { name: "", ty: UInt<3> }, value: 0x0, }, 3: Copy { - dest: StatePartIndex(81), // (0x0) SlotDebugData { name: "", ty: Enum {HdlNone, HdlSome(Bundle {0: UInt<1>, 1: Bool})} }, - src: StatePartIndex(80), // (0x0) SlotDebugData { name: "", ty: UInt<3> }, + dest: StatePartIndex(89), // (0x0) SlotDebugData { name: "", ty: Enum {HdlNone, HdlSome(Bundle {0: UInt<1>, 1: Bool})} }, + src: StatePartIndex(88), // (0x0) SlotDebugData { name: "", ty: UInt<3> }, }, - // at: module-XXXXXXXXXX.rs:16:1 + // at: module-XXXXXXXXXX.rs:18:1 4: Copy { dest: StatePartIndex(7), // (0x0) SlotDebugData { name: "InstantiatedModule(enums: enums).enums::b_out", ty: Enum {HdlNone, HdlSome(Bundle {0: UInt<1>, 1: Bool})} }, - src: StatePartIndex(81), // (0x0) SlotDebugData { name: "", ty: Enum {HdlNone, HdlSome(Bundle {0: UInt<1>, 1: Bool})} }, + src: StatePartIndex(89), // (0x0) SlotDebugData { name: "", ty: Enum {HdlNone, HdlSome(Bundle {0: UInt<1>, 1: Bool})} }, }, // at: module-XXXXXXXXXX.rs:1:1 5: SliceInt { - dest: StatePartIndex(69), // (0x3) SlotDebugData { name: "", ty: UInt<2> }, + dest: StatePartIndex(77), // (0x3) SlotDebugData { name: "", ty: UInt<2> }, src: StatePartIndex(4), // (0xf) SlotDebugData { name: "InstantiatedModule(enums: enums).enums::data_in", ty: UInt<4> }, start: 2, len: 2, }, 6: CastToSInt { - dest: StatePartIndex(70), // (-0x1) SlotDebugData { name: "", ty: SInt<2> }, - src: StatePartIndex(69), // (0x3) SlotDebugData { name: "", ty: UInt<2> }, + dest: StatePartIndex(78), // (-0x1) SlotDebugData { name: "", ty: SInt<2> }, + src: StatePartIndex(77), // (0x3) SlotDebugData { name: "", ty: UInt<2> }, dest_width: 2, }, 7: Const { - dest: StatePartIndex(62), // (0x2) SlotDebugData { name: "", ty: UInt<2> }, + dest: StatePartIndex(70), // (0x2) SlotDebugData { name: "", ty: UInt<2> }, value: 0x2, }, 8: SliceInt { - dest: StatePartIndex(49), // (0x1) SlotDebugData { name: "", ty: UInt<1> }, + dest: StatePartIndex(57), // (0x1) SlotDebugData { name: "", ty: UInt<1> }, src: StatePartIndex(4), // (0xf) SlotDebugData { name: "InstantiatedModule(enums: enums).enums::data_in", ty: UInt<4> }, start: 1, len: 1, }, 9: Copy { - dest: StatePartIndex(50), // (0x1) SlotDebugData { name: "", ty: Bool }, - src: StatePartIndex(49), // (0x1) SlotDebugData { name: "", ty: UInt<1> }, + dest: StatePartIndex(58), // (0x1) SlotDebugData { name: "", ty: Bool }, + src: StatePartIndex(57), // (0x1) SlotDebugData { name: "", ty: UInt<1> }, }, 10: Copy { - dest: StatePartIndex(68), // (0x1) SlotDebugData { name: "", ty: UInt<1> }, - src: StatePartIndex(50), // (0x1) SlotDebugData { name: "", ty: Bool }, + dest: StatePartIndex(76), // (0x1) SlotDebugData { name: "", ty: UInt<1> }, + src: StatePartIndex(58), // (0x1) SlotDebugData { name: "", ty: Bool }, }, 11: SliceInt { - dest: StatePartIndex(46), // (0x1) SlotDebugData { name: "", ty: UInt<1> }, + dest: StatePartIndex(54), // (0x1) SlotDebugData { name: "", ty: UInt<1> }, src: StatePartIndex(4), // (0xf) SlotDebugData { name: "InstantiatedModule(enums: enums).enums::data_in", ty: UInt<4> }, start: 0, len: 1, }, 12: Copy { - dest: StatePartIndex(47), // (0x1) SlotDebugData { name: "", ty: Bool }, - src: StatePartIndex(46), // (0x1) SlotDebugData { name: "", ty: UInt<1> }, + dest: StatePartIndex(55), // (0x1) SlotDebugData { name: "", ty: Bool }, + src: StatePartIndex(54), // (0x1) SlotDebugData { name: "", ty: UInt<1> }, }, 13: Copy { - dest: StatePartIndex(48), // (0x1) SlotDebugData { name: "", ty: UInt<1> }, - src: StatePartIndex(47), // (0x1) SlotDebugData { name: "", ty: Bool }, + dest: StatePartIndex(56), // (0x1) SlotDebugData { name: "", ty: UInt<1> }, + src: StatePartIndex(55), // (0x1) SlotDebugData { name: "", ty: Bool }, }, 14: Copy { - dest: StatePartIndex(44), // (0x1) SlotDebugData { name: ".0", ty: UInt<1> }, - src: StatePartIndex(48), // (0x1) SlotDebugData { name: "", ty: UInt<1> }, + dest: StatePartIndex(52), // (0x1) SlotDebugData { name: ".0", ty: UInt<1> }, + src: StatePartIndex(56), // (0x1) SlotDebugData { name: "", ty: UInt<1> }, }, 15: Copy { - dest: StatePartIndex(45), // (0x1) SlotDebugData { name: ".1", ty: Bool }, - src: StatePartIndex(50), // (0x1) SlotDebugData { name: "", ty: Bool }, + dest: StatePartIndex(53), // (0x1) SlotDebugData { name: ".1", ty: Bool }, + src: StatePartIndex(58), // (0x1) SlotDebugData { name: "", ty: Bool }, }, 16: Copy { - dest: StatePartIndex(66), // (0x1) SlotDebugData { name: "[0]", ty: UInt<1> }, - src: StatePartIndex(48), // (0x1) SlotDebugData { name: "", ty: UInt<1> }, + dest: StatePartIndex(74), // (0x1) SlotDebugData { name: "[0]", ty: UInt<1> }, + src: StatePartIndex(56), // (0x1) SlotDebugData { name: "", ty: UInt<1> }, }, 17: Copy { - dest: StatePartIndex(67), // (0x1) SlotDebugData { name: "[1]", ty: UInt<1> }, - src: StatePartIndex(68), // (0x1) SlotDebugData { name: "", ty: UInt<1> }, + dest: StatePartIndex(75), // (0x1) SlotDebugData { name: "[1]", ty: UInt<1> }, + src: StatePartIndex(76), // (0x1) SlotDebugData { name: "", ty: UInt<1> }, }, 18: Copy { - dest: StatePartIndex(63), // (0x1) SlotDebugData { name: ".a[0]", ty: UInt<1> }, - src: StatePartIndex(66), // (0x1) SlotDebugData { name: "[0]", ty: UInt<1> }, + dest: StatePartIndex(71), // (0x1) SlotDebugData { name: ".a[0]", ty: UInt<1> }, + src: StatePartIndex(74), // (0x1) SlotDebugData { name: "[0]", ty: UInt<1> }, }, 19: Copy { - dest: StatePartIndex(64), // (0x1) SlotDebugData { name: ".a[1]", ty: UInt<1> }, - src: StatePartIndex(67), // (0x1) SlotDebugData { name: "[1]", ty: UInt<1> }, + dest: StatePartIndex(72), // (0x1) SlotDebugData { name: ".a[1]", ty: UInt<1> }, + src: StatePartIndex(75), // (0x1) SlotDebugData { name: "[1]", ty: UInt<1> }, }, 20: Copy { - dest: StatePartIndex(65), // (-0x1) SlotDebugData { name: ".b", ty: SInt<2> }, - src: StatePartIndex(70), // (-0x1) SlotDebugData { name: "", ty: SInt<2> }, + dest: StatePartIndex(73), // (-0x1) SlotDebugData { name: ".b", ty: SInt<2> }, + src: StatePartIndex(78), // (-0x1) SlotDebugData { name: "", ty: SInt<2> }, }, 21: Copy { - dest: StatePartIndex(58), // (0x2) SlotDebugData { name: ".0", ty: UInt<2> }, - src: StatePartIndex(62), // (0x2) SlotDebugData { name: "", ty: UInt<2> }, + dest: StatePartIndex(66), // (0x2) SlotDebugData { name: ".0", ty: UInt<2> }, + src: StatePartIndex(70), // (0x2) SlotDebugData { name: "", ty: UInt<2> }, }, 22: Copy { - dest: StatePartIndex(59), // (0x1) SlotDebugData { name: ".1.a[0]", ty: UInt<1> }, - src: StatePartIndex(63), // (0x1) SlotDebugData { name: ".a[0]", ty: UInt<1> }, + dest: StatePartIndex(67), // (0x1) SlotDebugData { name: ".1.a[0]", ty: UInt<1> }, + src: StatePartIndex(71), // (0x1) SlotDebugData { name: ".a[0]", ty: UInt<1> }, }, 23: Copy { - dest: StatePartIndex(60), // (0x1) SlotDebugData { name: ".1.a[1]", ty: UInt<1> }, - src: StatePartIndex(64), // (0x1) SlotDebugData { name: ".a[1]", ty: UInt<1> }, + dest: StatePartIndex(68), // (0x1) SlotDebugData { name: ".1.a[1]", ty: UInt<1> }, + src: StatePartIndex(72), // (0x1) SlotDebugData { name: ".a[1]", ty: UInt<1> }, }, 24: Copy { - dest: StatePartIndex(61), // (-0x1) SlotDebugData { name: ".1.b", ty: SInt<2> }, - src: StatePartIndex(65), // (-0x1) SlotDebugData { name: ".b", ty: SInt<2> }, + dest: StatePartIndex(69), // (-0x1) SlotDebugData { name: ".1.b", ty: SInt<2> }, + src: StatePartIndex(73), // (-0x1) SlotDebugData { name: ".b", ty: SInt<2> }, }, 25: Shl { - dest: StatePartIndex(71), // (0x2) SlotDebugData { name: "", ty: UInt<2> }, - lhs: StatePartIndex(60), // (0x1) SlotDebugData { name: ".1.a[1]", ty: UInt<1> }, + dest: StatePartIndex(79), // (0x2) SlotDebugData { name: "", ty: UInt<2> }, + lhs: StatePartIndex(68), // (0x1) SlotDebugData { name: ".1.a[1]", ty: UInt<1> }, rhs: 1, }, 26: Or { - dest: StatePartIndex(72), // (0x3) SlotDebugData { name: "", ty: UInt<2> }, - lhs: StatePartIndex(59), // (0x1) SlotDebugData { name: ".1.a[0]", ty: UInt<1> }, - rhs: StatePartIndex(71), // (0x2) SlotDebugData { name: "", ty: UInt<2> }, + dest: StatePartIndex(80), // (0x3) SlotDebugData { name: "", ty: UInt<2> }, + lhs: StatePartIndex(67), // (0x1) SlotDebugData { name: ".1.a[0]", ty: UInt<1> }, + rhs: StatePartIndex(79), // (0x2) SlotDebugData { name: "", ty: UInt<2> }, }, 27: CastToUInt { - dest: StatePartIndex(73), // (0x3) SlotDebugData { name: "", ty: UInt<2> }, - src: StatePartIndex(61), // (-0x1) SlotDebugData { name: ".1.b", ty: SInt<2> }, + dest: StatePartIndex(81), // (0x3) SlotDebugData { name: "", ty: UInt<2> }, + src: StatePartIndex(69), // (-0x1) SlotDebugData { name: ".1.b", ty: SInt<2> }, dest_width: 2, }, 28: Shl { - dest: StatePartIndex(74), // (0xc) SlotDebugData { name: "", ty: UInt<4> }, - lhs: StatePartIndex(73), // (0x3) SlotDebugData { name: "", ty: UInt<2> }, + dest: StatePartIndex(82), // (0xc) SlotDebugData { name: "", ty: UInt<4> }, + lhs: StatePartIndex(81), // (0x3) SlotDebugData { name: "", ty: UInt<2> }, rhs: 2, }, 29: Or { - dest: StatePartIndex(75), // (0xf) SlotDebugData { name: "", ty: UInt<4> }, - lhs: StatePartIndex(72), // (0x3) SlotDebugData { name: "", ty: UInt<2> }, - rhs: StatePartIndex(74), // (0xc) SlotDebugData { name: "", ty: UInt<4> }, + dest: StatePartIndex(83), // (0xf) SlotDebugData { name: "", ty: UInt<4> }, + lhs: StatePartIndex(80), // (0x3) SlotDebugData { name: "", ty: UInt<2> }, + rhs: StatePartIndex(82), // (0xc) SlotDebugData { name: "", ty: UInt<4> }, }, 30: Shl { - dest: StatePartIndex(76), // (0x3c) SlotDebugData { name: "", ty: UInt<6> }, - lhs: StatePartIndex(75), // (0xf) SlotDebugData { name: "", ty: UInt<4> }, + dest: StatePartIndex(84), // (0x3c) SlotDebugData { name: "", ty: UInt<6> }, + lhs: StatePartIndex(83), // (0xf) SlotDebugData { name: "", ty: UInt<4> }, rhs: 2, }, 31: Or { - dest: StatePartIndex(77), // (0x3e) SlotDebugData { name: "", ty: UInt<6> }, - lhs: StatePartIndex(58), // (0x2) SlotDebugData { name: ".0", ty: UInt<2> }, - rhs: StatePartIndex(76), // (0x3c) SlotDebugData { name: "", ty: UInt<6> }, + dest: StatePartIndex(85), // (0x3e) SlotDebugData { name: "", ty: UInt<6> }, + lhs: StatePartIndex(66), // (0x2) SlotDebugData { name: ".0", ty: UInt<2> }, + rhs: StatePartIndex(84), // (0x3c) SlotDebugData { name: "", ty: UInt<6> }, }, 32: CastToUInt { - dest: StatePartIndex(78), // (0x3e) SlotDebugData { name: "", ty: UInt<6> }, - src: StatePartIndex(77), // (0x3e) SlotDebugData { name: "", ty: UInt<6> }, + dest: StatePartIndex(86), // (0x3e) SlotDebugData { name: "", ty: UInt<6> }, + src: StatePartIndex(85), // (0x3e) SlotDebugData { name: "", ty: UInt<6> }, dest_width: 6, }, 33: Copy { - dest: StatePartIndex(79), // (0x3e) SlotDebugData { name: "", ty: Enum {A, B(Bundle {0: UInt<1>, 1: Bool}), C(Bundle {a: Array, 2>, b: SInt<2>})} }, - src: StatePartIndex(78), // (0x3e) SlotDebugData { name: "", ty: UInt<6> }, + dest: StatePartIndex(87), // (0x3e) SlotDebugData { name: "", ty: Enum {A, B(Bundle {0: UInt<1>, 1: Bool}), C(Bundle {a: Array, 2>, b: SInt<2>})} }, + src: StatePartIndex(86), // (0x3e) SlotDebugData { name: "", ty: UInt<6> }, }, 34: Const { - dest: StatePartIndex(39), // (0x1) SlotDebugData { name: "", ty: UInt<2> }, + dest: StatePartIndex(47), // (0x1) SlotDebugData { name: "", ty: UInt<2> }, value: 0x1, }, 35: CmpEq { - dest: StatePartIndex(40), // (0x0) SlotDebugData { name: "", ty: Bool }, + dest: StatePartIndex(48), // (0x0) SlotDebugData { name: "", ty: Bool }, lhs: StatePartIndex(3), // (0x2) SlotDebugData { name: "InstantiatedModule(enums: enums).enums::which_in", ty: UInt<2> }, - rhs: StatePartIndex(39), // (0x1) SlotDebugData { name: "", ty: UInt<2> }, + rhs: StatePartIndex(47), // (0x1) SlotDebugData { name: "", ty: UInt<2> }, }, 36: Copy { - dest: StatePartIndex(41), // (0x1) SlotDebugData { name: ".0", ty: UInt<2> }, - src: StatePartIndex(39), // (0x1) SlotDebugData { name: "", ty: UInt<2> }, + dest: StatePartIndex(49), // (0x1) SlotDebugData { name: ".0", ty: UInt<2> }, + src: StatePartIndex(47), // (0x1) SlotDebugData { name: "", ty: UInt<2> }, }, 37: Copy { - dest: StatePartIndex(42), // (0x1) SlotDebugData { name: ".1.0", ty: UInt<1> }, - src: StatePartIndex(44), // (0x1) SlotDebugData { name: ".0", ty: UInt<1> }, + dest: StatePartIndex(50), // (0x1) SlotDebugData { name: ".1.0", ty: UInt<1> }, + src: StatePartIndex(52), // (0x1) SlotDebugData { name: ".0", ty: UInt<1> }, }, 38: Copy { - dest: StatePartIndex(43), // (0x1) SlotDebugData { name: ".1.1", ty: Bool }, - src: StatePartIndex(45), // (0x1) SlotDebugData { name: ".1", ty: Bool }, + dest: StatePartIndex(51), // (0x1) SlotDebugData { name: ".1.1", ty: Bool }, + src: StatePartIndex(53), // (0x1) SlotDebugData { name: ".1", ty: Bool }, }, 39: Copy { - dest: StatePartIndex(51), // (0x1) SlotDebugData { name: "", ty: UInt<1> }, - src: StatePartIndex(43), // (0x1) SlotDebugData { name: ".1.1", ty: Bool }, + dest: StatePartIndex(59), // (0x1) SlotDebugData { name: "", ty: UInt<1> }, + src: StatePartIndex(51), // (0x1) SlotDebugData { name: ".1.1", ty: Bool }, }, 40: Shl { - dest: StatePartIndex(52), // (0x2) SlotDebugData { name: "", ty: UInt<2> }, - lhs: StatePartIndex(51), // (0x1) SlotDebugData { name: "", ty: UInt<1> }, + dest: StatePartIndex(60), // (0x2) SlotDebugData { name: "", ty: UInt<2> }, + lhs: StatePartIndex(59), // (0x1) SlotDebugData { name: "", ty: UInt<1> }, rhs: 1, }, 41: Or { - dest: StatePartIndex(53), // (0x3) SlotDebugData { name: "", ty: UInt<2> }, - lhs: StatePartIndex(42), // (0x1) SlotDebugData { name: ".1.0", ty: UInt<1> }, - rhs: StatePartIndex(52), // (0x2) SlotDebugData { name: "", ty: UInt<2> }, + dest: StatePartIndex(61), // (0x3) SlotDebugData { name: "", ty: UInt<2> }, + lhs: StatePartIndex(50), // (0x1) SlotDebugData { name: ".1.0", ty: UInt<1> }, + rhs: StatePartIndex(60), // (0x2) SlotDebugData { name: "", ty: UInt<2> }, }, 42: Shl { - dest: StatePartIndex(54), // (0xc) SlotDebugData { name: "", ty: UInt<4> }, - lhs: StatePartIndex(53), // (0x3) SlotDebugData { name: "", ty: UInt<2> }, + dest: StatePartIndex(62), // (0xc) SlotDebugData { name: "", ty: UInt<4> }, + lhs: StatePartIndex(61), // (0x3) SlotDebugData { name: "", ty: UInt<2> }, rhs: 2, }, 43: Or { - dest: StatePartIndex(55), // (0xd) SlotDebugData { name: "", ty: UInt<4> }, - lhs: StatePartIndex(41), // (0x1) SlotDebugData { name: ".0", ty: UInt<2> }, - rhs: StatePartIndex(54), // (0xc) SlotDebugData { name: "", ty: UInt<4> }, + dest: StatePartIndex(63), // (0xd) SlotDebugData { name: "", ty: UInt<4> }, + lhs: StatePartIndex(49), // (0x1) SlotDebugData { name: ".0", ty: UInt<2> }, + rhs: StatePartIndex(62), // (0xc) SlotDebugData { name: "", ty: UInt<4> }, }, 44: CastToUInt { - dest: StatePartIndex(56), // (0xd) SlotDebugData { name: "", ty: UInt<6> }, - src: StatePartIndex(55), // (0xd) SlotDebugData { name: "", ty: UInt<4> }, + dest: StatePartIndex(64), // (0xd) SlotDebugData { name: "", ty: UInt<6> }, + src: StatePartIndex(63), // (0xd) SlotDebugData { name: "", ty: UInt<4> }, dest_width: 6, }, 45: Copy { - dest: StatePartIndex(57), // (0xd) SlotDebugData { name: "", ty: Enum {A, B(Bundle {0: UInt<1>, 1: Bool}), C(Bundle {a: Array, 2>, b: SInt<2>})} }, - src: StatePartIndex(56), // (0xd) SlotDebugData { name: "", ty: UInt<6> }, + dest: StatePartIndex(65), // (0xd) SlotDebugData { name: "", ty: Enum {A, B(Bundle {0: UInt<1>, 1: Bool}), C(Bundle {a: Array, 2>, b: SInt<2>})} }, + src: StatePartIndex(64), // (0xd) SlotDebugData { name: "", ty: UInt<6> }, }, 46: Const { - dest: StatePartIndex(37), // (0x0) SlotDebugData { name: "", ty: UInt<2> }, + dest: StatePartIndex(45), // (0x0) SlotDebugData { name: "", ty: UInt<2> }, value: 0x0, }, 47: CmpEq { - dest: StatePartIndex(38), // (0x0) SlotDebugData { name: "", ty: Bool }, + dest: StatePartIndex(46), // (0x0) SlotDebugData { name: "", ty: Bool }, lhs: StatePartIndex(3), // (0x2) SlotDebugData { name: "InstantiatedModule(enums: enums).enums::which_in", ty: UInt<2> }, - rhs: StatePartIndex(37), // (0x0) SlotDebugData { name: "", ty: UInt<2> }, + rhs: StatePartIndex(45), // (0x0) SlotDebugData { name: "", ty: UInt<2> }, }, 48: Copy { - dest: StatePartIndex(21), // (0x3e) SlotDebugData { name: "", ty: UInt<6> }, - src: StatePartIndex(15), // (0x3e) SlotDebugData { name: "InstantiatedModule(enums: enums).enums::the_reg", ty: Enum {A, B(Bundle {0: UInt<1>, 1: Bool}), C(Bundle {a: Array, 2>, b: SInt<2>})} }, + dest: StatePartIndex(29), // (0x3e) SlotDebugData { name: "", ty: UInt<6> }, + src: StatePartIndex(23), // (0x3e) SlotDebugData { name: "InstantiatedModule(enums: enums).enums::the_reg", ty: Enum {A, B(Bundle {0: UInt<1>, 1: Bool}), C(Bundle {a: Array, 2>, b: SInt<2>})} }, }, 49: SliceInt { - dest: StatePartIndex(22), // (0x3) SlotDebugData { name: "", ty: UInt<2> }, - src: StatePartIndex(21), // (0x3e) SlotDebugData { name: "", ty: UInt<6> }, + dest: StatePartIndex(30), // (0x3) SlotDebugData { name: "", ty: UInt<2> }, + src: StatePartIndex(29), // (0x3e) SlotDebugData { name: "", ty: UInt<6> }, start: 2, len: 2, }, 50: SliceInt { - dest: StatePartIndex(23), // (0x1) SlotDebugData { name: "", ty: UInt<1> }, - src: StatePartIndex(22), // (0x3) SlotDebugData { name: "", ty: UInt<2> }, + dest: StatePartIndex(31), // (0x1) SlotDebugData { name: "", ty: UInt<1> }, + src: StatePartIndex(30), // (0x3) SlotDebugData { name: "", ty: UInt<2> }, start: 0, len: 1, }, 51: SliceInt { - dest: StatePartIndex(24), // (0x1) SlotDebugData { name: "", ty: UInt<1> }, - src: StatePartIndex(22), // (0x3) SlotDebugData { name: "", ty: UInt<2> }, + dest: StatePartIndex(32), // (0x1) SlotDebugData { name: "", ty: UInt<1> }, + src: StatePartIndex(30), // (0x3) SlotDebugData { name: "", ty: UInt<2> }, start: 1, len: 1, }, 52: Copy { - dest: StatePartIndex(25), // (0x1) SlotDebugData { name: "", ty: Bool }, - src: StatePartIndex(24), // (0x1) SlotDebugData { name: "", ty: UInt<1> }, + dest: StatePartIndex(33), // (0x1) SlotDebugData { name: "", ty: Bool }, + src: StatePartIndex(32), // (0x1) SlotDebugData { name: "", ty: UInt<1> }, }, 53: Copy { - dest: StatePartIndex(19), // (0x1) SlotDebugData { name: ".0", ty: UInt<1> }, - src: StatePartIndex(23), // (0x1) SlotDebugData { name: "", ty: UInt<1> }, + dest: StatePartIndex(27), // (0x1) SlotDebugData { name: ".0", ty: UInt<1> }, + src: StatePartIndex(31), // (0x1) SlotDebugData { name: "", ty: UInt<1> }, }, 54: Copy { - dest: StatePartIndex(20), // (0x1) SlotDebugData { name: ".1", ty: Bool }, - src: StatePartIndex(25), // (0x1) SlotDebugData { name: "", ty: Bool }, + dest: StatePartIndex(28), // (0x1) SlotDebugData { name: ".1", ty: Bool }, + src: StatePartIndex(33), // (0x1) SlotDebugData { name: "", ty: Bool }, }, 55: Copy { - dest: StatePartIndex(83), // (0x1) SlotDebugData { name: "", ty: UInt<1> }, - src: StatePartIndex(20), // (0x1) SlotDebugData { name: ".1", ty: Bool }, + dest: StatePartIndex(91), // (0x1) SlotDebugData { name: "", ty: UInt<1> }, + src: StatePartIndex(28), // (0x1) SlotDebugData { name: ".1", ty: Bool }, }, 56: Shl { - dest: StatePartIndex(84), // (0x2) SlotDebugData { name: "", ty: UInt<2> }, - lhs: StatePartIndex(83), // (0x1) SlotDebugData { name: "", ty: UInt<1> }, - rhs: 1, - }, - 57: Or { - dest: StatePartIndex(85), // (0x3) SlotDebugData { name: "", ty: UInt<2> }, - lhs: StatePartIndex(19), // (0x1) SlotDebugData { name: ".0", ty: UInt<1> }, - rhs: StatePartIndex(84), // (0x2) SlotDebugData { name: "", ty: UInt<2> }, - }, - 58: CastToUInt { - dest: StatePartIndex(86), // (0x3) SlotDebugData { name: "", ty: UInt<4> }, - src: StatePartIndex(85), // (0x3) SlotDebugData { name: "", ty: UInt<2> }, - dest_width: 4, - }, - 59: Copy { - dest: StatePartIndex(87), // (0x1) SlotDebugData { name: ".0", ty: UInt<1> }, - src: StatePartIndex(90), // (0x1) SlotDebugData { name: "", ty: UInt<1> }, - }, - 60: Copy { - dest: StatePartIndex(88), // (0x1) SlotDebugData { name: ".1.0", ty: UInt<1> }, - src: StatePartIndex(19), // (0x1) SlotDebugData { name: ".0", ty: UInt<1> }, - }, - 61: Copy { - dest: StatePartIndex(89), // (0x1) SlotDebugData { name: ".1.1", ty: Bool }, - src: StatePartIndex(20), // (0x1) SlotDebugData { name: ".1", ty: Bool }, - }, - 62: Copy { - dest: StatePartIndex(91), // (0x1) SlotDebugData { name: "", ty: UInt<1> }, - src: StatePartIndex(89), // (0x1) SlotDebugData { name: ".1.1", ty: Bool }, - }, - 63: Shl { dest: StatePartIndex(92), // (0x2) SlotDebugData { name: "", ty: UInt<2> }, lhs: StatePartIndex(91), // (0x1) SlotDebugData { name: "", ty: UInt<1> }, rhs: 1, }, - 64: Or { + 57: Or { dest: StatePartIndex(93), // (0x3) SlotDebugData { name: "", ty: UInt<2> }, - lhs: StatePartIndex(88), // (0x1) SlotDebugData { name: ".1.0", ty: UInt<1> }, + lhs: StatePartIndex(27), // (0x1) SlotDebugData { name: ".0", ty: UInt<1> }, rhs: StatePartIndex(92), // (0x2) SlotDebugData { name: "", ty: UInt<2> }, }, + 58: CastToUInt { + dest: StatePartIndex(94), // (0x3) SlotDebugData { name: "", ty: UInt<4> }, + src: StatePartIndex(93), // (0x3) SlotDebugData { name: "", ty: UInt<2> }, + dest_width: 4, + }, + 59: Copy { + dest: StatePartIndex(95), // (0x1) SlotDebugData { name: ".0", ty: UInt<1> }, + src: StatePartIndex(98), // (0x1) SlotDebugData { name: "", ty: UInt<1> }, + }, + 60: Copy { + dest: StatePartIndex(96), // (0x1) SlotDebugData { name: ".1.0", ty: UInt<1> }, + src: StatePartIndex(27), // (0x1) SlotDebugData { name: ".0", ty: UInt<1> }, + }, + 61: Copy { + dest: StatePartIndex(97), // (0x1) SlotDebugData { name: ".1.1", ty: Bool }, + src: StatePartIndex(28), // (0x1) SlotDebugData { name: ".1", ty: Bool }, + }, + 62: Copy { + dest: StatePartIndex(99), // (0x1) SlotDebugData { name: "", ty: UInt<1> }, + src: StatePartIndex(97), // (0x1) SlotDebugData { name: ".1.1", ty: Bool }, + }, + 63: Shl { + dest: StatePartIndex(100), // (0x2) SlotDebugData { name: "", ty: UInt<2> }, + lhs: StatePartIndex(99), // (0x1) SlotDebugData { name: "", ty: UInt<1> }, + rhs: 1, + }, + 64: Or { + dest: StatePartIndex(101), // (0x3) SlotDebugData { name: "", ty: UInt<2> }, + lhs: StatePartIndex(96), // (0x1) SlotDebugData { name: ".1.0", ty: UInt<1> }, + rhs: StatePartIndex(100), // (0x2) SlotDebugData { name: "", ty: UInt<2> }, + }, 65: Shl { - dest: StatePartIndex(94), // (0x6) SlotDebugData { name: "", ty: UInt<3> }, - lhs: StatePartIndex(93), // (0x3) SlotDebugData { name: "", ty: UInt<2> }, + dest: StatePartIndex(102), // (0x6) SlotDebugData { name: "", ty: UInt<3> }, + lhs: StatePartIndex(101), // (0x3) SlotDebugData { name: "", ty: UInt<2> }, rhs: 1, }, 66: Or { - dest: StatePartIndex(95), // (0x7) SlotDebugData { name: "", ty: UInt<3> }, - lhs: StatePartIndex(87), // (0x1) SlotDebugData { name: ".0", ty: UInt<1> }, - rhs: StatePartIndex(94), // (0x6) SlotDebugData { name: "", ty: UInt<3> }, + dest: StatePartIndex(103), // (0x7) SlotDebugData { name: "", ty: UInt<3> }, + lhs: StatePartIndex(95), // (0x1) SlotDebugData { name: ".0", ty: UInt<1> }, + rhs: StatePartIndex(102), // (0x6) SlotDebugData { name: "", ty: UInt<3> }, }, 67: CastToUInt { - dest: StatePartIndex(96), // (0x7) SlotDebugData { name: "", ty: UInt<3> }, - src: StatePartIndex(95), // (0x7) SlotDebugData { name: "", ty: UInt<3> }, + dest: StatePartIndex(104), // (0x7) SlotDebugData { name: "", ty: UInt<3> }, + src: StatePartIndex(103), // (0x7) SlotDebugData { name: "", ty: UInt<3> }, dest_width: 3, }, 68: Copy { - dest: StatePartIndex(97), // (0x7) SlotDebugData { name: "", ty: Enum {HdlNone, HdlSome(Bundle {0: UInt<1>, 1: Bool})} }, - src: StatePartIndex(96), // (0x7) SlotDebugData { name: "", ty: UInt<3> }, + dest: StatePartIndex(105), // (0x7) SlotDebugData { name: "", ty: Enum {HdlNone, HdlSome(Bundle {0: UInt<1>, 1: Bool})} }, + src: StatePartIndex(104), // (0x7) SlotDebugData { name: "", ty: UInt<3> }, }, 69: SliceInt { - dest: StatePartIndex(31), // (0xf) SlotDebugData { name: "", ty: UInt<4> }, - src: StatePartIndex(21), // (0x3e) SlotDebugData { name: "", ty: UInt<6> }, + dest: StatePartIndex(39), // (0xf) SlotDebugData { name: "", ty: UInt<4> }, + src: StatePartIndex(29), // (0x3e) SlotDebugData { name: "", ty: UInt<6> }, start: 2, len: 4, }, 70: SliceInt { - dest: StatePartIndex(32), // (0x3) SlotDebugData { name: "", ty: UInt<2> }, - src: StatePartIndex(31), // (0xf) SlotDebugData { name: "", ty: UInt<4> }, + dest: StatePartIndex(40), // (0x3) SlotDebugData { name: "", ty: UInt<2> }, + src: StatePartIndex(39), // (0xf) SlotDebugData { name: "", ty: UInt<4> }, start: 0, len: 2, }, 71: SliceInt { - dest: StatePartIndex(33), // (0x1) SlotDebugData { name: "", ty: UInt<1> }, - src: StatePartIndex(32), // (0x3) SlotDebugData { name: "", ty: UInt<2> }, + dest: StatePartIndex(41), // (0x1) SlotDebugData { name: "", ty: UInt<1> }, + src: StatePartIndex(40), // (0x3) SlotDebugData { name: "", ty: UInt<2> }, start: 0, len: 1, }, 72: SliceInt { - dest: StatePartIndex(34), // (0x1) SlotDebugData { name: "", ty: UInt<1> }, - src: StatePartIndex(32), // (0x3) SlotDebugData { name: "", ty: UInt<2> }, + dest: StatePartIndex(42), // (0x1) SlotDebugData { name: "", ty: UInt<1> }, + src: StatePartIndex(40), // (0x3) SlotDebugData { name: "", ty: UInt<2> }, start: 1, len: 1, }, 73: Copy { - dest: StatePartIndex(29), // (0x1) SlotDebugData { name: "[0]", ty: UInt<1> }, - src: StatePartIndex(33), // (0x1) SlotDebugData { name: "", ty: UInt<1> }, + dest: StatePartIndex(37), // (0x1) SlotDebugData { name: "[0]", ty: UInt<1> }, + src: StatePartIndex(41), // (0x1) SlotDebugData { name: "", ty: UInt<1> }, }, 74: Copy { - dest: StatePartIndex(30), // (0x1) SlotDebugData { name: "[1]", ty: UInt<1> }, - src: StatePartIndex(34), // (0x1) SlotDebugData { name: "", ty: UInt<1> }, + dest: StatePartIndex(38), // (0x1) SlotDebugData { name: "[1]", ty: UInt<1> }, + src: StatePartIndex(42), // (0x1) SlotDebugData { name: "", ty: UInt<1> }, }, 75: SliceInt { - dest: StatePartIndex(35), // (0x3) SlotDebugData { name: "", ty: UInt<2> }, - src: StatePartIndex(31), // (0xf) SlotDebugData { name: "", ty: UInt<4> }, + dest: StatePartIndex(43), // (0x3) SlotDebugData { name: "", ty: UInt<2> }, + src: StatePartIndex(39), // (0xf) SlotDebugData { name: "", ty: UInt<4> }, start: 2, len: 2, }, 76: CastToSInt { - dest: StatePartIndex(36), // (-0x1) SlotDebugData { name: "", ty: SInt<2> }, - src: StatePartIndex(35), // (0x3) SlotDebugData { name: "", ty: UInt<2> }, + dest: StatePartIndex(44), // (-0x1) SlotDebugData { name: "", ty: SInt<2> }, + src: StatePartIndex(43), // (0x3) SlotDebugData { name: "", ty: UInt<2> }, dest_width: 2, }, 77: Copy { - dest: StatePartIndex(26), // (0x1) SlotDebugData { name: ".a[0]", ty: UInt<1> }, - src: StatePartIndex(29), // (0x1) SlotDebugData { name: "[0]", ty: UInt<1> }, + dest: StatePartIndex(34), // (0x1) SlotDebugData { name: ".a[0]", ty: UInt<1> }, + src: StatePartIndex(37), // (0x1) SlotDebugData { name: "[0]", ty: UInt<1> }, }, 78: Copy { - dest: StatePartIndex(27), // (0x1) SlotDebugData { name: ".a[1]", ty: UInt<1> }, - src: StatePartIndex(30), // (0x1) SlotDebugData { name: "[1]", ty: UInt<1> }, + dest: StatePartIndex(35), // (0x1) SlotDebugData { name: ".a[1]", ty: UInt<1> }, + src: StatePartIndex(38), // (0x1) SlotDebugData { name: "[1]", ty: UInt<1> }, }, 79: Copy { - dest: StatePartIndex(28), // (-0x1) SlotDebugData { name: ".b", ty: SInt<2> }, - src: StatePartIndex(36), // (-0x1) SlotDebugData { name: "", ty: SInt<2> }, + dest: StatePartIndex(36), // (-0x1) SlotDebugData { name: ".b", ty: SInt<2> }, + src: StatePartIndex(44), // (-0x1) SlotDebugData { name: "", ty: SInt<2> }, }, 80: Shl { - dest: StatePartIndex(98), // (0x2) SlotDebugData { name: "", ty: UInt<2> }, - lhs: StatePartIndex(27), // (0x1) SlotDebugData { name: ".a[1]", ty: UInt<1> }, + dest: StatePartIndex(106), // (0x2) SlotDebugData { name: "", ty: UInt<2> }, + lhs: StatePartIndex(35), // (0x1) SlotDebugData { name: ".a[1]", ty: UInt<1> }, rhs: 1, }, 81: Or { - dest: StatePartIndex(99), // (0x3) SlotDebugData { name: "", ty: UInt<2> }, - lhs: StatePartIndex(26), // (0x1) SlotDebugData { name: ".a[0]", ty: UInt<1> }, - rhs: StatePartIndex(98), // (0x2) SlotDebugData { name: "", ty: UInt<2> }, + dest: StatePartIndex(107), // (0x3) SlotDebugData { name: "", ty: UInt<2> }, + lhs: StatePartIndex(34), // (0x1) SlotDebugData { name: ".a[0]", ty: UInt<1> }, + rhs: StatePartIndex(106), // (0x2) SlotDebugData { name: "", ty: UInt<2> }, }, 82: CastToUInt { - dest: StatePartIndex(100), // (0x3) SlotDebugData { name: "", ty: UInt<2> }, - src: StatePartIndex(28), // (-0x1) SlotDebugData { name: ".b", ty: SInt<2> }, + dest: StatePartIndex(108), // (0x3) SlotDebugData { name: "", ty: UInt<2> }, + src: StatePartIndex(36), // (-0x1) SlotDebugData { name: ".b", ty: SInt<2> }, dest_width: 2, }, 83: Shl { - dest: StatePartIndex(101), // (0xc) SlotDebugData { name: "", ty: UInt<4> }, - lhs: StatePartIndex(100), // (0x3) SlotDebugData { name: "", ty: UInt<2> }, + dest: StatePartIndex(109), // (0xc) SlotDebugData { name: "", ty: UInt<4> }, + lhs: StatePartIndex(108), // (0x3) SlotDebugData { name: "", ty: UInt<2> }, rhs: 2, }, 84: Or { - dest: StatePartIndex(102), // (0xf) SlotDebugData { name: "", ty: UInt<4> }, - lhs: StatePartIndex(99), // (0x3) SlotDebugData { name: "", ty: UInt<2> }, - rhs: StatePartIndex(101), // (0xc) SlotDebugData { name: "", ty: UInt<4> }, + dest: StatePartIndex(110), // (0xf) SlotDebugData { name: "", ty: UInt<4> }, + lhs: StatePartIndex(107), // (0x3) SlotDebugData { name: "", ty: UInt<2> }, + rhs: StatePartIndex(109), // (0xc) SlotDebugData { name: "", ty: UInt<4> }, }, - // at: module-XXXXXXXXXX.rs:9:1 + // at: module-XXXXXXXXXX.rs:11:1 85: AndBigWithSmallImmediate { - dest: StatePartIndex(5), // (0x2 2) SlotDebugData { name: "", ty: Enum {A, B, C} }, - lhs: StatePartIndex(15), // (0x3e) SlotDebugData { name: "InstantiatedModule(enums: enums).enums::the_reg", ty: Enum {A, B(Bundle {0: UInt<1>, 1: Bool}), C(Bundle {a: Array, 2>, b: SInt<2>})} }, + dest: StatePartIndex(6), // (0x2 2) SlotDebugData { name: "", ty: Enum {A, B, C} }, + lhs: StatePartIndex(23), // (0x3e) SlotDebugData { name: "InstantiatedModule(enums: enums).enums::the_reg", ty: Enum {A, B(Bundle {0: UInt<1>, 1: Bool}), C(Bundle {a: Array, 2>, b: SInt<2>})} }, rhs: 0x3, }, - // at: module-XXXXXXXXXX.rs:17:1 + // at: module-XXXXXXXXXX.rs:19:1 86: BranchIfSmallNeImmediate { target: 89, - lhs: StatePartIndex(5), // (0x2 2) SlotDebugData { name: "", ty: Enum {A, B, C} }, + lhs: StatePartIndex(6), // (0x2 2) SlotDebugData { name: "", ty: Enum {A, B, C} }, rhs: 0x0, }, - // at: module-XXXXXXXXXX.rs:18:1 + // at: module-XXXXXXXXXX.rs:20:1 87: Copy { dest: StatePartIndex(5), // (0x2) SlotDebugData { name: "InstantiatedModule(enums: enums).enums::which_out", ty: UInt<2> }, - src: StatePartIndex(37), // (0x0) SlotDebugData { name: "", ty: UInt<2> }, - }, - // at: module-XXXXXXXXXX.rs:19:1 - 88: Copy { - dest: StatePartIndex(6), // (0xf) SlotDebugData { name: "InstantiatedModule(enums: enums).enums::data_out", ty: UInt<4> }, - src: StatePartIndex(82), // (0x0) SlotDebugData { name: "", ty: UInt<4> }, - }, - // at: module-XXXXXXXXXX.rs:17:1 - 89: BranchIfSmallNeImmediate { - target: 93, - lhs: StatePartIndex(5), // (0x2 2) SlotDebugData { name: "", ty: Enum {A, B, C} }, - rhs: 0x1, - }, - // at: module-XXXXXXXXXX.rs:20:1 - 90: Copy { - dest: StatePartIndex(5), // (0x2) SlotDebugData { name: "InstantiatedModule(enums: enums).enums::which_out", ty: UInt<2> }, - src: StatePartIndex(39), // (0x1) SlotDebugData { name: "", ty: UInt<2> }, + src: StatePartIndex(45), // (0x0) SlotDebugData { name: "", ty: UInt<2> }, }, // at: module-XXXXXXXXXX.rs:21:1 - 91: Copy { + 88: Copy { dest: StatePartIndex(6), // (0xf) SlotDebugData { name: "InstantiatedModule(enums: enums).enums::data_out", ty: UInt<4> }, - src: StatePartIndex(86), // (0x3) SlotDebugData { name: "", ty: UInt<4> }, + src: StatePartIndex(90), // (0x0) SlotDebugData { name: "", ty: UInt<4> }, + }, + // at: module-XXXXXXXXXX.rs:19:1 + 89: BranchIfSmallNeImmediate { + target: 93, + lhs: StatePartIndex(6), // (0x2 2) SlotDebugData { name: "", ty: Enum {A, B, C} }, + rhs: 0x1, }, // at: module-XXXXXXXXXX.rs:22:1 - 92: Copy { - dest: StatePartIndex(7), // (0x0) SlotDebugData { name: "InstantiatedModule(enums: enums).enums::b_out", ty: Enum {HdlNone, HdlSome(Bundle {0: UInt<1>, 1: Bool})} }, - src: StatePartIndex(97), // (0x7) SlotDebugData { name: "", ty: Enum {HdlNone, HdlSome(Bundle {0: UInt<1>, 1: Bool})} }, - }, - // at: module-XXXXXXXXXX.rs:17:1 - 93: BranchIfSmallNeImmediate { - target: 96, - lhs: StatePartIndex(5), // (0x2 2) SlotDebugData { name: "", ty: Enum {A, B, C} }, - rhs: 0x2, + 90: Copy { + dest: StatePartIndex(5), // (0x2) SlotDebugData { name: "InstantiatedModule(enums: enums).enums::which_out", ty: UInt<2> }, + src: StatePartIndex(47), // (0x1) SlotDebugData { name: "", ty: UInt<2> }, }, // at: module-XXXXXXXXXX.rs:23:1 - 94: Copy { - dest: StatePartIndex(5), // (0x2) SlotDebugData { name: "InstantiatedModule(enums: enums).enums::which_out", ty: UInt<2> }, - src: StatePartIndex(62), // (0x2) SlotDebugData { name: "", ty: UInt<2> }, + 91: Copy { + dest: StatePartIndex(6), // (0xf) SlotDebugData { name: "InstantiatedModule(enums: enums).enums::data_out", ty: UInt<4> }, + src: StatePartIndex(94), // (0x3) SlotDebugData { name: "", ty: UInt<4> }, }, // at: module-XXXXXXXXXX.rs:24:1 + 92: Copy { + dest: StatePartIndex(7), // (0x0) SlotDebugData { name: "InstantiatedModule(enums: enums).enums::b_out", ty: Enum {HdlNone, HdlSome(Bundle {0: UInt<1>, 1: Bool})} }, + src: StatePartIndex(105), // (0x7) SlotDebugData { name: "", ty: Enum {HdlNone, HdlSome(Bundle {0: UInt<1>, 1: Bool})} }, + }, + // at: module-XXXXXXXXXX.rs:19:1 + 93: BranchIfSmallNeImmediate { + target: 96, + lhs: StatePartIndex(6), // (0x2 2) SlotDebugData { name: "", ty: Enum {A, B, C} }, + rhs: 0x2, + }, + // at: module-XXXXXXXXXX.rs:25:1 + 94: Copy { + dest: StatePartIndex(5), // (0x2) SlotDebugData { name: "InstantiatedModule(enums: enums).enums::which_out", ty: UInt<2> }, + src: StatePartIndex(70), // (0x2) SlotDebugData { name: "", ty: UInt<2> }, + }, + // at: module-XXXXXXXXXX.rs:26:1 95: Copy { dest: StatePartIndex(6), // (0xf) SlotDebugData { name: "InstantiatedModule(enums: enums).enums::data_out", ty: UInt<4> }, - src: StatePartIndex(102), // (0xf) SlotDebugData { name: "", ty: UInt<4> }, + src: StatePartIndex(110), // (0xf) SlotDebugData { name: "", ty: UInt<4> }, }, - // at: module-XXXXXXXXXX.rs:9:1 + // at: module-XXXXXXXXXX.rs:11:1 96: IsNonZeroDestIsSmall { - dest: StatePartIndex(4), // (0x0 0) SlotDebugData { name: "", ty: Bool }, + dest: StatePartIndex(5), // (0x0 0) SlotDebugData { name: "", ty: Bool }, src: StatePartIndex(1), // (0x0) SlotDebugData { name: "InstantiatedModule(enums: enums).enums::cd.rst", ty: SyncReset }, }, // at: module-XXXXXXXXXX.rs:1:1 97: Const { - dest: StatePartIndex(17), // (0x0) SlotDebugData { name: "", ty: UInt<6> }, + dest: StatePartIndex(25), // (0x0) SlotDebugData { name: "", ty: UInt<6> }, value: 0x0, }, 98: Copy { - dest: StatePartIndex(18), // (0x0) SlotDebugData { name: "", ty: Enum {A, B(Bundle {0: UInt<1>, 1: Bool}), C(Bundle {a: Array, 2>, b: SInt<2>})} }, - src: StatePartIndex(17), // (0x0) SlotDebugData { name: "", ty: UInt<6> }, + dest: StatePartIndex(26), // (0x0) SlotDebugData { name: "", ty: Enum {A, B(Bundle {0: UInt<1>, 1: Bool}), C(Bundle {a: Array, 2>, b: SInt<2>})} }, + src: StatePartIndex(25), // (0x0) SlotDebugData { name: "", ty: UInt<6> }, }, - // at: module-XXXXXXXXXX.rs:10:1 + // at: module-XXXXXXXXXX.rs:12:1 99: BranchIfZero { target: 107, value: StatePartIndex(2), // (0x1) SlotDebugData { name: "InstantiatedModule(enums: enums).enums::en", ty: Bool }, }, - // at: module-XXXXXXXXXX.rs:11:1 + // at: module-XXXXXXXXXX.rs:13:1 100: BranchIfZero { target: 102, - value: StatePartIndex(38), // (0x0) SlotDebugData { name: "", ty: Bool }, - }, - // at: module-XXXXXXXXXX.rs:12:1 - 101: Copy { - dest: StatePartIndex(16), // (0x3e) SlotDebugData { name: "InstantiatedModule(enums: enums).enums::the_reg$next", ty: Enum {A, B(Bundle {0: UInt<1>, 1: Bool}), C(Bundle {a: Array, 2>, b: SInt<2>})} }, - src: StatePartIndex(18), // (0x0) SlotDebugData { name: "", ty: Enum {A, B(Bundle {0: UInt<1>, 1: Bool}), C(Bundle {a: Array, 2>, b: SInt<2>})} }, - }, - // at: module-XXXXXXXXXX.rs:11:1 - 102: BranchIfNonZero { - target: 107, - value: StatePartIndex(38), // (0x0) SlotDebugData { name: "", ty: Bool }, - }, - // at: module-XXXXXXXXXX.rs:13:1 - 103: BranchIfZero { - target: 105, - value: StatePartIndex(40), // (0x0) SlotDebugData { name: "", ty: Bool }, + value: StatePartIndex(46), // (0x0) SlotDebugData { name: "", ty: Bool }, }, // at: module-XXXXXXXXXX.rs:14:1 - 104: Copy { - dest: StatePartIndex(16), // (0x3e) SlotDebugData { name: "InstantiatedModule(enums: enums).enums::the_reg$next", ty: Enum {A, B(Bundle {0: UInt<1>, 1: Bool}), C(Bundle {a: Array, 2>, b: SInt<2>})} }, - src: StatePartIndex(57), // (0xd) SlotDebugData { name: "", ty: Enum {A, B(Bundle {0: UInt<1>, 1: Bool}), C(Bundle {a: Array, 2>, b: SInt<2>})} }, + 101: Copy { + dest: StatePartIndex(24), // (0x3e) SlotDebugData { name: "InstantiatedModule(enums: enums).enums::the_reg$next", ty: Enum {A, B(Bundle {0: UInt<1>, 1: Bool}), C(Bundle {a: Array, 2>, b: SInt<2>})} }, + src: StatePartIndex(26), // (0x0) SlotDebugData { name: "", ty: Enum {A, B(Bundle {0: UInt<1>, 1: Bool}), C(Bundle {a: Array, 2>, b: SInt<2>})} }, }, // at: module-XXXXXXXXXX.rs:13:1 - 105: BranchIfNonZero { + 102: BranchIfNonZero { target: 107, - value: StatePartIndex(40), // (0x0) SlotDebugData { name: "", ty: Bool }, + value: StatePartIndex(46), // (0x0) SlotDebugData { name: "", ty: Bool }, }, // at: module-XXXXXXXXXX.rs:15:1 - 106: Copy { - dest: StatePartIndex(16), // (0x3e) SlotDebugData { name: "InstantiatedModule(enums: enums).enums::the_reg$next", ty: Enum {A, B(Bundle {0: UInt<1>, 1: Bool}), C(Bundle {a: Array, 2>, b: SInt<2>})} }, - src: StatePartIndex(79), // (0x3e) SlotDebugData { name: "", ty: Enum {A, B(Bundle {0: UInt<1>, 1: Bool}), C(Bundle {a: Array, 2>, b: SInt<2>})} }, + 103: BranchIfZero { + target: 105, + value: StatePartIndex(48), // (0x0) SlotDebugData { name: "", ty: Bool }, }, - // at: module-XXXXXXXXXX.rs:9:1 + // at: module-XXXXXXXXXX.rs:16:1 + 104: Copy { + dest: StatePartIndex(24), // (0x3e) SlotDebugData { name: "InstantiatedModule(enums: enums).enums::the_reg$next", ty: Enum {A, B(Bundle {0: UInt<1>, 1: Bool}), C(Bundle {a: Array, 2>, b: SInt<2>})} }, + src: StatePartIndex(65), // (0xd) SlotDebugData { name: "", ty: Enum {A, B(Bundle {0: UInt<1>, 1: Bool}), C(Bundle {a: Array, 2>, b: SInt<2>})} }, + }, + // at: module-XXXXXXXXXX.rs:15:1 + 105: BranchIfNonZero { + target: 107, + value: StatePartIndex(48), // (0x0) SlotDebugData { name: "", ty: Bool }, + }, + // at: module-XXXXXXXXXX.rs:17:1 + 106: Copy { + dest: StatePartIndex(24), // (0x3e) SlotDebugData { name: "InstantiatedModule(enums: enums).enums::the_reg$next", ty: Enum {A, B(Bundle {0: UInt<1>, 1: Bool}), C(Bundle {a: Array, 2>, b: SInt<2>})} }, + src: StatePartIndex(87), // (0x3e) SlotDebugData { name: "", ty: Enum {A, B(Bundle {0: UInt<1>, 1: Bool}), C(Bundle {a: Array, 2>, b: SInt<2>})} }, + }, + // at: module-XXXXXXXXXX.rs:11:1 107: IsNonZeroDestIsSmall { - dest: StatePartIndex(3), // (0x1 1) SlotDebugData { name: "", ty: Bool }, + dest: StatePartIndex(4), // (0x1 1) SlotDebugData { name: "", ty: Bool }, src: StatePartIndex(0), // (0x1) SlotDebugData { name: "InstantiatedModule(enums: enums).enums::cd.clk", ty: Clock }, }, 108: AndSmall { - dest: StatePartIndex(2), // (0x0 0) SlotDebugData { name: "", ty: Bool }, - lhs: StatePartIndex(3), // (0x1 1) SlotDebugData { name: "", ty: Bool }, - rhs: StatePartIndex(1), // (0x0 0) SlotDebugData { name: "", ty: Bool }, + dest: StatePartIndex(3), // (0x0 0) SlotDebugData { name: "", ty: Bool }, + lhs: StatePartIndex(4), // (0x1 1) SlotDebugData { name: "", ty: Bool }, + rhs: StatePartIndex(2), // (0x0 0) SlotDebugData { name: "", ty: Bool }, + }, + // at: module-XXXXXXXXXX.rs:10:1 + 109: Copy { + dest: StatePartIndex(15), // (0x0) SlotDebugData { name: "InstantiatedModule(enums: enums).enums::b2_out", ty: Enum {HdlNone, HdlSome(Bundle {0: UInt<1>, 1: Bool})} }, + src: StatePartIndex(7), // (0x0) SlotDebugData { name: "InstantiatedModule(enums: enums).enums::b_out", ty: Enum {HdlNone, HdlSome(Bundle {0: UInt<1>, 1: Bool})} }, }, // at: module-XXXXXXXXXX.rs:1:1 - 109: Copy { + 110: Copy { + dest: StatePartIndex(18), // (0x0) SlotDebugData { name: "", ty: UInt<3> }, + src: StatePartIndex(15), // (0x0) SlotDebugData { name: "InstantiatedModule(enums: enums).enums::b2_out", ty: Enum {HdlNone, HdlSome(Bundle {0: UInt<1>, 1: Bool})} }, + }, + 111: SliceInt { + dest: StatePartIndex(19), // (0x0) SlotDebugData { name: "", ty: UInt<2> }, + src: StatePartIndex(18), // (0x0) SlotDebugData { name: "", ty: UInt<3> }, + start: 1, + len: 2, + }, + 112: SliceInt { + dest: StatePartIndex(20), // (0x0) SlotDebugData { name: "", ty: UInt<1> }, + src: StatePartIndex(19), // (0x0) SlotDebugData { name: "", ty: UInt<2> }, + start: 0, + len: 1, + }, + 113: SliceInt { + dest: StatePartIndex(21), // (0x0) SlotDebugData { name: "", ty: UInt<1> }, + src: StatePartIndex(19), // (0x0) SlotDebugData { name: "", ty: UInt<2> }, + start: 1, + len: 1, + }, + 114: Copy { + dest: StatePartIndex(22), // (0x0) SlotDebugData { name: "", ty: Bool }, + src: StatePartIndex(21), // (0x0) SlotDebugData { name: "", ty: UInt<1> }, + }, + 115: Copy { + dest: StatePartIndex(16), // (0x0) SlotDebugData { name: ".0", ty: UInt<1> }, + src: StatePartIndex(20), // (0x0) SlotDebugData { name: "", ty: UInt<1> }, + }, + 116: Copy { + dest: StatePartIndex(17), // (0x0) SlotDebugData { name: ".1", ty: Bool }, + src: StatePartIndex(22), // (0x0) SlotDebugData { name: "", ty: Bool }, + }, + // at: module-XXXXXXXXXX.rs:9:1 + 117: AndBigWithSmallImmediate { + dest: StatePartIndex(1), // (0x0 0) SlotDebugData { name: "", ty: Enum {HdlNone, HdlSome} }, + lhs: StatePartIndex(15), // (0x0) SlotDebugData { name: "InstantiatedModule(enums: enums).enums::b2_out", ty: Enum {HdlNone, HdlSome(Bundle {0: UInt<1>, 1: Bool})} }, + rhs: 0x1, + }, + // at: module-XXXXXXXXXX.rs:1:1 + 118: Copy { dest: StatePartIndex(10), // (0x0) SlotDebugData { name: "", ty: UInt<3> }, src: StatePartIndex(7), // (0x0) SlotDebugData { name: "InstantiatedModule(enums: enums).enums::b_out", ty: Enum {HdlNone, HdlSome(Bundle {0: UInt<1>, 1: Bool})} }, }, - 110: SliceInt { + 119: SliceInt { dest: StatePartIndex(11), // (0x0) SlotDebugData { name: "", ty: UInt<2> }, src: StatePartIndex(10), // (0x0) SlotDebugData { name: "", ty: UInt<3> }, start: 1, len: 2, }, - 111: SliceInt { + 120: SliceInt { dest: StatePartIndex(12), // (0x0) SlotDebugData { name: "", ty: UInt<1> }, src: StatePartIndex(11), // (0x0) SlotDebugData { name: "", ty: UInt<2> }, start: 0, len: 1, }, - 112: SliceInt { + 121: SliceInt { dest: StatePartIndex(13), // (0x0) SlotDebugData { name: "", ty: UInt<1> }, src: StatePartIndex(11), // (0x0) SlotDebugData { name: "", ty: UInt<2> }, start: 1, len: 1, }, - 113: Copy { + 122: Copy { dest: StatePartIndex(14), // (0x0) SlotDebugData { name: "", ty: Bool }, src: StatePartIndex(13), // (0x0) SlotDebugData { name: "", ty: UInt<1> }, }, - 114: Copy { + 123: Copy { dest: StatePartIndex(8), // (0x0) SlotDebugData { name: ".0", ty: UInt<1> }, src: StatePartIndex(12), // (0x0) SlotDebugData { name: "", ty: UInt<1> }, }, - 115: Copy { + 124: Copy { dest: StatePartIndex(9), // (0x0) SlotDebugData { name: ".1", ty: Bool }, src: StatePartIndex(14), // (0x0) SlotDebugData { name: "", ty: Bool }, }, // at: module-XXXXXXXXXX.rs:8:1 - 116: AndBigWithSmallImmediate { + 125: AndBigWithSmallImmediate { dest: StatePartIndex(0), // (0x0 0) SlotDebugData { name: "", ty: Enum {HdlNone, HdlSome} }, lhs: StatePartIndex(7), // (0x0) SlotDebugData { name: "InstantiatedModule(enums: enums).enums::b_out", ty: Enum {HdlNone, HdlSome(Bundle {0: UInt<1>, 1: Bool})} }, rhs: 0x1, }, - // at: module-XXXXXXXXXX.rs:9:1 - 117: BranchIfSmallZero { - target: 122, - value: StatePartIndex(2), // (0x0 0) SlotDebugData { name: "", ty: Bool }, + // at: module-XXXXXXXXXX.rs:11:1 + 126: BranchIfSmallZero { + target: 131, + value: StatePartIndex(3), // (0x0 0) SlotDebugData { name: "", ty: Bool }, }, - 118: BranchIfSmallNonZero { - target: 121, - value: StatePartIndex(4), // (0x0 0) SlotDebugData { name: "", ty: Bool }, + 127: BranchIfSmallNonZero { + target: 130, + value: StatePartIndex(5), // (0x0 0) SlotDebugData { name: "", ty: Bool }, }, - 119: Copy { - dest: StatePartIndex(15), // (0x3e) SlotDebugData { name: "InstantiatedModule(enums: enums).enums::the_reg", ty: Enum {A, B(Bundle {0: UInt<1>, 1: Bool}), C(Bundle {a: Array, 2>, b: SInt<2>})} }, - src: StatePartIndex(16), // (0x3e) SlotDebugData { name: "InstantiatedModule(enums: enums).enums::the_reg$next", ty: Enum {A, B(Bundle {0: UInt<1>, 1: Bool}), C(Bundle {a: Array, 2>, b: SInt<2>})} }, + 128: Copy { + dest: StatePartIndex(23), // (0x3e) SlotDebugData { name: "InstantiatedModule(enums: enums).enums::the_reg", ty: Enum {A, B(Bundle {0: UInt<1>, 1: Bool}), C(Bundle {a: Array, 2>, b: SInt<2>})} }, + src: StatePartIndex(24), // (0x3e) SlotDebugData { name: "InstantiatedModule(enums: enums).enums::the_reg$next", ty: Enum {A, B(Bundle {0: UInt<1>, 1: Bool}), C(Bundle {a: Array, 2>, b: SInt<2>})} }, }, - 120: Branch { - target: 122, + 129: Branch { + target: 131, }, - 121: Copy { - dest: StatePartIndex(15), // (0x3e) SlotDebugData { name: "InstantiatedModule(enums: enums).enums::the_reg", ty: Enum {A, B(Bundle {0: UInt<1>, 1: Bool}), C(Bundle {a: Array, 2>, b: SInt<2>})} }, - src: StatePartIndex(18), // (0x0) SlotDebugData { name: "", ty: Enum {A, B(Bundle {0: UInt<1>, 1: Bool}), C(Bundle {a: Array, 2>, b: SInt<2>})} }, + 130: Copy { + dest: StatePartIndex(23), // (0x3e) SlotDebugData { name: "InstantiatedModule(enums: enums).enums::the_reg", ty: Enum {A, B(Bundle {0: UInt<1>, 1: Bool}), C(Bundle {a: Array, 2>, b: SInt<2>})} }, + src: StatePartIndex(26), // (0x0) SlotDebugData { name: "", ty: Enum {A, B(Bundle {0: UInt<1>, 1: Bool}), C(Bundle {a: Array, 2>, b: SInt<2>})} }, }, - 122: XorSmallImmediate { - dest: StatePartIndex(1), // (0x0 0) SlotDebugData { name: "", ty: Bool }, - lhs: StatePartIndex(3), // (0x1 1) SlotDebugData { name: "", ty: Bool }, + 131: XorSmallImmediate { + dest: StatePartIndex(2), // (0x0 0) SlotDebugData { name: "", ty: Bool }, + lhs: StatePartIndex(4), // (0x1 1) SlotDebugData { name: "", ty: Bool }, rhs: 0x1, }, // at: module-XXXXXXXXXX.rs:1:1 - 123: Return, + 132: Return, ], .. }, - pc: 123, + pc: 132, memory_write_log: [], memories: StatePart { value: [], @@ -1095,6 +1183,7 @@ Simulation { 0, 0, 0, + 0, 1, 0, 2, @@ -1117,6 +1206,14 @@ Simulation { 0, 0, 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, 62, 62, 0, @@ -1266,9 +1363,23 @@ Simulation { .. }, }.b_out, + Instance { + name: ::enums, + instantiated: Module { + name: enums, + .. + }, + }.b2_out, ], uninitialized_ios: {}, io_targets: { + Instance { + name: ::enums, + instantiated: Module { + name: enums, + .. + }, + }.b2_out, Instance { name: ::enums, instantiated: Module { @@ -1476,23 +1587,22 @@ Simulation { }, flow: Sink, }, - TraceReg { - name: "the_reg", + TraceModuleIO { + name: "b2_out", child: TraceEnumWithFields { - name: "the_reg", + name: "b2_out", discriminant: TraceEnumDiscriminant { location: TraceScalarId(10), name: "$tag", ty: Enum { - A, - B(Bundle {0: UInt<1>, 1: Bool}), - C(Bundle {a: Array, 2>, b: SInt<2>}), + HdlNone, + HdlSome(Bundle {0: UInt<1>, 1: Bool}), }, - flow: Duplex, + flow: Sink, }, non_empty_fields: [ TraceBundle { - name: "B", + name: "HdlSome", fields: [ TraceUInt { location: TraceScalarId(11), @@ -1514,6 +1624,57 @@ Simulation { }, flow: Source, }, + ], + ty: Enum { + HdlNone, + HdlSome(Bundle {0: UInt<1>, 1: Bool}), + }, + flow: Sink, + }, + ty: Enum { + HdlNone, + HdlSome(Bundle {0: UInt<1>, 1: Bool}), + }, + flow: Sink, + }, + TraceReg { + name: "the_reg", + child: TraceEnumWithFields { + name: "the_reg", + discriminant: TraceEnumDiscriminant { + location: TraceScalarId(13), + name: "$tag", + ty: Enum { + A, + B(Bundle {0: UInt<1>, 1: Bool}), + C(Bundle {a: Array, 2>, b: SInt<2>}), + }, + flow: Duplex, + }, + non_empty_fields: [ + TraceBundle { + name: "B", + fields: [ + TraceUInt { + location: TraceScalarId(14), + name: "0", + ty: UInt<1>, + flow: Source, + }, + TraceBool { + location: TraceScalarId(15), + name: "1", + flow: Source, + }, + ], + ty: Bundle { + /* offset = 0 */ + 0: UInt<1>, + /* offset = 1 */ + 1: Bool, + }, + flow: Source, + }, TraceBundle { name: "C", fields: [ @@ -1521,13 +1682,13 @@ Simulation { name: "a", elements: [ TraceUInt { - location: TraceScalarId(13), + location: TraceScalarId(16), name: "[0]", ty: UInt<1>, flow: Source, }, TraceUInt { - location: TraceScalarId(14), + location: TraceScalarId(17), name: "[1]", ty: UInt<1>, flow: Source, @@ -1537,7 +1698,7 @@ Simulation { flow: Source, }, TraceSInt { - location: TraceScalarId(15), + location: TraceScalarId(18), name: "b", ty: SInt<2>, flow: Source, @@ -1660,7 +1821,36 @@ Simulation { SimTrace { id: TraceScalarId(10), kind: EnumDiscriminant { - index: StatePartIndex(5), + index: StatePartIndex(1), + ty: Enum { + HdlNone, + HdlSome(Bundle {0: UInt<1>, 1: Bool}), + }, + }, + state: 0x0, + last_state: 0x0, + }, + SimTrace { + id: TraceScalarId(11), + kind: BigUInt { + index: StatePartIndex(16), + ty: UInt<1>, + }, + state: 0x0, + last_state: 0x0, + }, + SimTrace { + id: TraceScalarId(12), + kind: BigBool { + index: StatePartIndex(17), + }, + state: 0x0, + last_state: 0x0, + }, + SimTrace { + id: TraceScalarId(13), + kind: EnumDiscriminant { + index: StatePartIndex(6), ty: Enum { A, B(Bundle {0: UInt<1>, 1: Bool}), @@ -1670,32 +1860,6 @@ Simulation { state: 0x2, last_state: 0x2, }, - SimTrace { - id: TraceScalarId(11), - kind: BigUInt { - index: StatePartIndex(19), - ty: UInt<1>, - }, - state: 0x1, - last_state: 0x1, - }, - SimTrace { - id: TraceScalarId(12), - kind: BigBool { - index: StatePartIndex(20), - }, - state: 0x1, - last_state: 0x1, - }, - SimTrace { - id: TraceScalarId(13), - kind: BigUInt { - index: StatePartIndex(26), - ty: UInt<1>, - }, - state: 0x1, - last_state: 0x1, - }, SimTrace { id: TraceScalarId(14), kind: BigUInt { @@ -1707,8 +1871,34 @@ Simulation { }, SimTrace { id: TraceScalarId(15), - kind: BigSInt { + kind: BigBool { index: StatePartIndex(28), + }, + state: 0x1, + last_state: 0x1, + }, + SimTrace { + id: TraceScalarId(16), + kind: BigUInt { + index: StatePartIndex(34), + ty: UInt<1>, + }, + state: 0x1, + last_state: 0x1, + }, + SimTrace { + id: TraceScalarId(17), + kind: BigUInt { + index: StatePartIndex(35), + ty: UInt<1>, + }, + state: 0x1, + last_state: 0x1, + }, + SimTrace { + id: TraceScalarId(18), + kind: BigSInt { + index: StatePartIndex(36), ty: SInt<2>, }, state: 0x3, @@ -1727,7 +1917,7 @@ Simulation { ], instant: 16 μs, clocks_triggered: [ - StatePartIndex(2), + StatePartIndex(3), ], .. } \ No newline at end of file diff --git a/crates/fayalite/tests/sim/expected/enums.vcd b/crates/fayalite/tests/sim/expected/enums.vcd index 07cbd32..aff867b 100644 --- a/crates/fayalite/tests/sim/expected/enums.vcd +++ b/crates/fayalite/tests/sim/expected/enums.vcd @@ -16,18 +16,25 @@ $var wire 1 ) \0 $end $var wire 1 * \1 $end $upscope $end $upscope $end -$scope struct the_reg $end +$scope struct b2_out $end $var string 1 + \$tag $end +$scope struct HdlSome $end +$var wire 1 , \0 $end +$var wire 1 - \1 $end +$upscope $end +$upscope $end +$scope struct the_reg $end +$var string 1 . \$tag $end $scope struct B $end -$var reg 1 , \0 $end -$var reg 1 - \1 $end +$var reg 1 / \0 $end +$var reg 1 0 \1 $end $upscope $end $scope struct C $end $scope struct a $end -$var reg 1 . \[0] $end -$var reg 1 / \[1] $end +$var reg 1 1 \[0] $end +$var reg 1 2 \[1] $end $upscope $end -$var reg 2 0 b $end +$var reg 2 3 b $end $upscope $end $upscope $end $upscope $end @@ -43,12 +50,15 @@ b0 ' sHdlNone\x20(0) ( 0) 0* -sA\x20(0) + +sHdlNone\x20(0) + 0, 0- -0. +sA\x20(0) . 0/ -b0 0 +00 +01 +02 +b0 3 $end #1000000 1! @@ -66,7 +76,8 @@ b1 $ 1! b1 & sHdlSome\x20(1) ( -sB\x20(1) + +sHdlSome\x20(1) + +sB\x20(1) . #6000000 0# b0 $ @@ -85,8 +96,10 @@ b11 ' 1* 1, 1- -1. 1/ +10 +11 +12 #10000000 0! #11000000 @@ -101,8 +114,11 @@ b1111 ' sHdlNone\x20(0) ( 0) 0* -sC\x20(2) + -b11 0 +sHdlNone\x20(0) + +0, +0- +sC\x20(2) . +b11 3 #14000000 0! #15000000 From 62058dc141b2673d502295730b29abf1351b3ca1 Mon Sep 17 00:00:00 2001 From: Jacob Lifshay Date: Tue, 1 Apr 2025 22:22:54 -0700 Subject: [PATCH 22/38] fix cargo doc warnings -- convert urls to auto links --- crates/fayalite/src/util/prefix_sum.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/fayalite/src/util/prefix_sum.rs b/crates/fayalite/src/util/prefix_sum.rs index 758d89c..98e6d95 100644 --- a/crates/fayalite/src/util/prefix_sum.rs +++ b/crates/fayalite/src/util/prefix_sum.rs @@ -210,10 +210,10 @@ impl PrefixSumOp { #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] pub enum PrefixSumAlgorithm { /// Uses the algorithm from: - /// https://en.wikipedia.org/wiki/Prefix_sum#Algorithm_1:_Shorter_span,_more_parallel + /// LowLatency, /// Uses the algorithm from: - /// https://en.wikipedia.org/wiki/Prefix_sum#Algorithm_2:_Work-efficient + /// WorkEfficient, } From 6929352be77883e3ce724bf3271f1d57d0b6331f Mon Sep 17 00:00:00 2001 From: Jacob Lifshay Date: Thu, 3 Apr 2025 15:59:03 -0700 Subject: [PATCH 23/38] re-export `bitvec` and add types useful for simulation to the prelude --- crates/fayalite-proc-macros-impl/src/hdl_bundle.rs | 12 ++++++------ crates/fayalite-proc-macros-impl/src/hdl_enum.rs | 6 +++--- crates/fayalite/src/lib.rs | 4 ++-- crates/fayalite/src/prelude.rs | 8 +++++++- crates/fayalite/tests/sim.rs | 3 +-- 5 files changed, 19 insertions(+), 14 deletions(-) diff --git a/crates/fayalite-proc-macros-impl/src/hdl_bundle.rs b/crates/fayalite-proc-macros-impl/src/hdl_bundle.rs index 8e49ac4..5d13d39 100644 --- a/crates/fayalite-proc-macros-impl/src/hdl_bundle.rs +++ b/crates/fayalite-proc-macros-impl/src/hdl_bundle.rs @@ -746,7 +746,7 @@ impl ToTokens for ParsedBundle { } fn sim_value_from_bits( &self, - bits: &::fayalite::__bitvec::slice::BitSlice, + bits: &::fayalite::bitvec::slice::BitSlice, ) -> ::SimValue { #![allow(unused_mut, unused_variables)] let mut v = ::fayalite::bundle::BundleSimValueFromBits::new(*self, bits); @@ -757,7 +757,7 @@ impl ToTokens for ParsedBundle { fn sim_value_clone_from_bits( &self, value: &mut ::SimValue, - bits: &::fayalite::__bitvec::slice::BitSlice, + bits: &::fayalite::bitvec::slice::BitSlice, ) { #![allow(unused_mut, unused_variables)] let mut v = ::fayalite::bundle::BundleSimValueFromBits::new(*self, bits); @@ -766,7 +766,7 @@ impl ToTokens for ParsedBundle { fn sim_value_to_bits( &self, value: &::SimValue, - bits: &mut ::fayalite::__bitvec::slice::BitSlice, + bits: &mut ::fayalite::bitvec::slice::BitSlice, ) { #![allow(unused_mut, unused_variables)] let mut v = ::fayalite::bundle::BundleSimValueToBits::new(*self, bits); @@ -895,7 +895,7 @@ impl ToTokens for ParsedBundle { } fn sim_value_from_bits( &self, - bits: &::fayalite::__bitvec::slice::BitSlice, + bits: &::fayalite::bitvec::slice::BitSlice, ) -> ::SimValue { #![allow(unused_mut, unused_variables)] let mut v = ::fayalite::bundle::BundleSimValueFromBits::new(*self, bits); @@ -906,7 +906,7 @@ impl ToTokens for ParsedBundle { fn sim_value_clone_from_bits( &self, value: &mut ::SimValue, - bits: &::fayalite::__bitvec::slice::BitSlice, + bits: &::fayalite::bitvec::slice::BitSlice, ) { #![allow(unused_mut, unused_variables)] let mut v = ::fayalite::bundle::BundleSimValueFromBits::new(*self, bits); @@ -915,7 +915,7 @@ impl ToTokens for ParsedBundle { fn sim_value_to_bits( &self, value: &::SimValue, - bits: &mut ::fayalite::__bitvec::slice::BitSlice, + bits: &mut ::fayalite::bitvec::slice::BitSlice, ) { #![allow(unused_mut, unused_variables)] let mut v = ::fayalite::bundle::BundleSimValueToBits::new(*self, bits); diff --git a/crates/fayalite-proc-macros-impl/src/hdl_enum.rs b/crates/fayalite-proc-macros-impl/src/hdl_enum.rs index 6fb2a56..5437410 100644 --- a/crates/fayalite-proc-macros-impl/src/hdl_enum.rs +++ b/crates/fayalite-proc-macros-impl/src/hdl_enum.rs @@ -880,7 +880,7 @@ impl ToTokens for ParsedEnum { } fn sim_value_from_bits( &self, - bits: &::fayalite::__bitvec::slice::BitSlice, + bits: &::fayalite::bitvec::slice::BitSlice, ) -> ::SimValue { let v = ::fayalite::enum_::EnumSimValueFromBits::new(*self, bits); match v.discriminant() { @@ -890,7 +890,7 @@ impl ToTokens for ParsedEnum { fn sim_value_clone_from_bits( &self, value: &mut ::SimValue, - bits: &::fayalite::__bitvec::slice::BitSlice, + bits: &::fayalite::bitvec::slice::BitSlice, ) { let v = ::fayalite::enum_::EnumSimValueFromBits::new(*self, bits); match v.discriminant() { @@ -900,7 +900,7 @@ impl ToTokens for ParsedEnum { fn sim_value_to_bits( &self, value: &::SimValue, - bits: &mut ::fayalite::__bitvec::slice::BitSlice, + bits: &mut ::fayalite::bitvec::slice::BitSlice, ) { let v = ::fayalite::enum_::EnumSimValueToBits::new(*self, bits); match value { diff --git a/crates/fayalite/src/lib.rs b/crates/fayalite/src/lib.rs index 0843589..932464b 100644 --- a/crates/fayalite/src/lib.rs +++ b/crates/fayalite/src/lib.rs @@ -8,8 +8,6 @@ extern crate self as fayalite; -#[doc(hidden)] -pub use bitvec as __bitvec; #[doc(hidden)] pub use std as __std; @@ -78,6 +76,8 @@ pub use fayalite_proc_macros::hdl_module; #[doc(inline)] pub use fayalite_proc_macros::hdl; +pub use bitvec; + /// struct used as a placeholder when applying defaults #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Default)] pub struct __; diff --git a/crates/fayalite/src/prelude.rs b/crates/fayalite/src/prelude.rs index 39fa143..519210f 100644 --- a/crates/fayalite/src/prelude.rs +++ b/crates/fayalite/src/prelude.rs @@ -20,7 +20,7 @@ pub use crate::{ hdl_cover_with_enable, MakeFormalExpr, }, hdl, hdl_module, - int::{Bool, DynSize, KnownSize, SInt, SIntType, Size, UInt, UIntType}, + int::{Bool, DynSize, KnownSize, SInt, SIntType, SIntValue, Size, UInt, UIntType, UIntValue}, memory::{Mem, MemBuilder, ReadUnderWrite}, module::{ annotate, connect, connect_any, incomplete_wire, instance, memory, memory_array, @@ -29,9 +29,15 @@ pub use crate::{ phantom_const::PhantomConst, reg::Reg, reset::{AsyncReset, Reset, SyncReset, ToAsyncReset, ToReset, ToSyncReset}, + sim::{ + time::{SimDuration, SimInstant}, + value::{SimValue, ToSimValue, ToSimValueWithType}, + ExternModuleSimulationState, Simulation, + }, source_location::SourceLocation, ty::{AsMask, CanonicalType, Type}, util::{ConstUsize, GenericConstUsize}, wire::Wire, __, }; +pub use bitvec::{slice::BitSlice, vec::BitVec}; diff --git a/crates/fayalite/tests/sim.rs b/crates/fayalite/tests/sim.rs index 6433844..655a497 100644 --- a/crates/fayalite/tests/sim.rs +++ b/crates/fayalite/tests/sim.rs @@ -2,12 +2,11 @@ // See Notices.txt for copyright information use fayalite::{ - int::UIntValue, memory::{ReadStruct, ReadWriteStruct, WriteStruct}, module::{instance_with_loc, reg_builder_with_loc}, prelude::*, reset::ResetType, - sim::{time::SimDuration, vcd::VcdWriterDecls, Simulation}, + sim::vcd::VcdWriterDecls, util::RcWriter, }; use std::num::NonZeroUsize; From 57aae7b7fba63d3376003549e13b93fc6fe2adda Mon Sep 17 00:00:00 2001 From: Jacob Lifshay Date: Fri, 4 Apr 2025 01:04:26 -0700 Subject: [PATCH 24/38] implement [de]serializing `BaseType`s, `SimValue`s, and support PhantomConst in #[hdl] struct S --- .../src/hdl_bundle.rs | 17 + .../fayalite-proc-macros-impl/src/hdl_enum.rs | 9 + crates/fayalite/src/array.rs | 57 +- crates/fayalite/src/bundle.rs | 3 +- crates/fayalite/src/enum_.rs | 3 +- crates/fayalite/src/int.rs | 527 +++++++++++++++++- crates/fayalite/src/phantom_const.rs | 79 ++- crates/fayalite/src/sim/value.rs | 35 ++ crates/fayalite/src/ty.rs | 76 ++- crates/fayalite/src/ty/serde_impls.rs | 130 +++++ crates/fayalite/src/util/const_bool.rs | 42 +- crates/fayalite/src/util/const_usize.rs | 42 +- crates/fayalite/tests/hdl_types.rs | 6 + 13 files changed, 991 insertions(+), 35 deletions(-) create mode 100644 crates/fayalite/src/ty/serde_impls.rs diff --git a/crates/fayalite-proc-macros-impl/src/hdl_bundle.rs b/crates/fayalite-proc-macros-impl/src/hdl_bundle.rs index 5d13d39..7441cb3 100644 --- a/crates/fayalite-proc-macros-impl/src/hdl_bundle.rs +++ b/crates/fayalite-proc-macros-impl/src/hdl_bundle.rs @@ -1124,6 +1124,14 @@ impl ToTokens for ParsedBundle { } })); quote_spanned! {span=> + #[automatically_derived] + impl #static_impl_generics ::fayalite::__std::default::Default for #mask_type_ident #static_type_generics + #static_where_clause + { + fn default() -> Self { + ::TYPE + } + } #[automatically_derived] impl #static_impl_generics ::fayalite::ty::StaticType for #mask_type_ident #static_type_generics #static_where_clause @@ -1146,6 +1154,15 @@ impl ToTokens for ParsedBundle { }; } #[automatically_derived] + impl #static_impl_generics ::fayalite::__std::default::Default + for #target #static_type_generics + #static_where_clause + { + fn default() -> Self { + ::TYPE + } + } + #[automatically_derived] impl #static_impl_generics ::fayalite::ty::StaticType for #target #static_type_generics #static_where_clause { diff --git a/crates/fayalite-proc-macros-impl/src/hdl_enum.rs b/crates/fayalite-proc-macros-impl/src/hdl_enum.rs index 5437410..e072135 100644 --- a/crates/fayalite-proc-macros-impl/src/hdl_enum.rs +++ b/crates/fayalite-proc-macros-impl/src/hdl_enum.rs @@ -995,6 +995,15 @@ impl ToTokens for ParsedEnum { } })); quote_spanned! {span=> + #[automatically_derived] + impl #static_impl_generics ::fayalite::__std::default::Default + for #target #static_type_generics + #static_where_clause + { + fn default() -> Self { + ::TYPE + } + } #[automatically_derived] impl #static_impl_generics ::fayalite::ty::StaticType for #target #static_type_generics diff --git a/crates/fayalite/src/array.rs b/crates/fayalite/src/array.rs index a2df6cf..6d9b043 100644 --- a/crates/fayalite/src/array.rs +++ b/crates/fayalite/src/array.rs @@ -1,8 +1,6 @@ // SPDX-License-Identifier: LGPL-3.0-or-later // See Notices.txt for copyright information -use bitvec::slice::BitSlice; - use crate::{ expr::{ ops::{ArrayLiteral, ExprFromIterator, ExprIntoIterator, ExprPartialEq}, @@ -14,10 +12,13 @@ use crate::{ sim::value::{SimValue, SimValuePartialEq}, source_location::SourceLocation, ty::{ - CanonicalType, MatchVariantWithoutScope, StaticType, Type, TypeProperties, TypeWithDeref, + serde_impls::SerdeCanonicalType, CanonicalType, MatchVariantWithoutScope, StaticType, Type, + TypeProperties, TypeWithDeref, }, util::ConstUsize, }; +use bitvec::slice::BitSlice; +use serde::{de::Error, Deserialize, Deserializer, Serialize, Serializer}; use std::{iter::FusedIterator, ops::Index}; #[derive(Copy, Clone, PartialEq, Eq, Hash)] @@ -97,6 +98,12 @@ impl> ArrayType { } } +impl Default for ArrayType { + fn default() -> Self { + Self::TYPE + } +} + impl StaticType for ArrayType { const TYPE: Self = Self { element: LazyInterned::new_lazy(&|| T::TYPE.intern_sized()), @@ -226,6 +233,50 @@ impl Type for ArrayType { } } +impl Serialize for ArrayType { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + SerdeCanonicalType::::Array { + element: self.element(), + len: self.len(), + } + .serialize(serializer) + } +} + +impl<'de, T: Type + Deserialize<'de>, Len: Size> Deserialize<'de> for ArrayType { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let name = |len| -> String { + if let Some(len) = len { + format!("an Array<_, {len}>") + } else { + "an Array<_>".to_string() + } + }; + match SerdeCanonicalType::::deserialize(deserializer)? { + SerdeCanonicalType::Array { element, len } => { + if let Some(len) = Len::try_from_usize(len) { + Ok(Self::new(element, len)) + } else { + Err(Error::invalid_value( + serde::de::Unexpected::Other(&name(Some(len))), + &&*name(Len::KNOWN_VALUE), + )) + } + } + ty => Err(Error::invalid_value( + serde::de::Unexpected::Other(ty.as_serde_unexpected_str()), + &&*name(Len::KNOWN_VALUE), + )), + } + } +} + impl TypeWithDeref for ArrayType { fn expr_deref(this: &Expr) -> &Self::MatchVariant { let retval = Vec::from_iter(*this); diff --git a/crates/fayalite/src/bundle.rs b/crates/fayalite/src/bundle.rs index 9279b57..0fd89f1 100644 --- a/crates/fayalite/src/bundle.rs +++ b/crates/fayalite/src/bundle.rs @@ -17,9 +17,10 @@ use crate::{ }; use bitvec::{slice::BitSlice, vec::BitVec}; use hashbrown::HashMap; +use serde::{Deserialize, Serialize}; use std::{fmt, marker::PhantomData}; -#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)] +#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, Serialize, Deserialize)] pub struct BundleField { pub name: Interned, pub flipped: bool, diff --git a/crates/fayalite/src/enum_.rs b/crates/fayalite/src/enum_.rs index e37b7a5..6205855 100644 --- a/crates/fayalite/src/enum_.rs +++ b/crates/fayalite/src/enum_.rs @@ -22,9 +22,10 @@ use crate::{ }; use bitvec::{order::Lsb0, slice::BitSlice, view::BitView}; use hashbrown::HashMap; +use serde::{Deserialize, Serialize}; use std::{convert::Infallible, fmt, iter::FusedIterator, sync::Arc}; -#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, Serialize, Deserialize)] pub struct EnumVariant { pub name: Interned, pub ty: Option, diff --git a/crates/fayalite/src/int.rs b/crates/fayalite/src/int.rs index 373e150..13b8cf1 100644 --- a/crates/fayalite/src/int.rs +++ b/crates/fayalite/src/int.rs @@ -13,15 +13,20 @@ use crate::{ ty::{impl_match_variant_as_self, CanonicalType, StaticType, Type, TypeProperties}, util::{interned_bit, ConstBool, ConstUsize, GenericConstBool, GenericConstUsize}, }; -use bitvec::{order::Lsb0, slice::BitSlice, vec::BitVec}; +use bitvec::{order::Lsb0, slice::BitSlice, vec::BitVec, view::BitView}; use num_bigint::{BigInt, BigUint, Sign}; -use num_traits::{Signed, Zero}; +use num_traits::{One, Signed, Zero}; +use serde::{ + de::{DeserializeOwned, Error, Visitor}, + Deserialize, Deserializer, Serialize, Serializer, +}; use std::{ borrow::{BorrowMut, Cow}, fmt, marker::PhantomData, num::NonZero, ops::{Bound, Index, Not, Range, RangeBounds, RangeInclusive}, + str::FromStr, sync::Arc, }; @@ -93,13 +98,31 @@ macro_rules! known_widths { known_widths!([2 2 2 2 2 2 2 2 2]); pub trait SizeType: - sealed::SizeTypeSealed + Copy + Ord + std::hash::Hash + std::fmt::Debug + Send + Sync + 'static + sealed::SizeTypeSealed + + Copy + + Ord + + std::hash::Hash + + std::fmt::Debug + + Send + + Sync + + 'static + + Serialize + + DeserializeOwned { type Size: Size; } pub trait Size: - sealed::SizeSealed + Copy + Ord + std::hash::Hash + std::fmt::Debug + Send + Sync + 'static + sealed::SizeSealed + + Copy + + Ord + + std::hash::Hash + + std::fmt::Debug + + Send + + Sync + + 'static + + Serialize + + DeserializeOwned { type ArrayMatch: AsRef<[Expr]> + AsMut<[Expr]> @@ -191,6 +214,305 @@ impl Size for T { } } +#[derive(Clone, PartialEq, Eq, Debug)] +pub enum ParseIntValueError { + Empty, + InvalidDigit, + MissingDigits, + InvalidRadix, + MissingType, + InvalidType, + TypeMismatch { + parsed_signed: bool, + parsed_width: usize, + expected_signed: bool, + expected_width: usize, + }, + PosOverflow, + NegOverflow, + WidthOverflow, + MissingWidth, +} + +impl std::error::Error for ParseIntValueError {} + +impl fmt::Display for ParseIntValueError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str(match self { + Self::Empty => "can't parse integer from empty string", + Self::InvalidDigit => "invalid digit", + Self::MissingDigits => "missing digits", + Self::InvalidRadix => "invalid radix", + Self::MissingType => "missing type", + Self::InvalidType => "invalid type", + Self::TypeMismatch { + parsed_signed, + parsed_width, + expected_signed, + expected_width, + } => { + return write!( + f, + "type mismatch: parsed type {parsed_signed_str}{parsed_width}, \ + expected type {expected_signed_str}{expected_width}", + parsed_signed_str = if *parsed_signed { "i" } else { "u" }, + expected_signed_str = if *expected_signed { "i" } else { "u" }, + ); + } + Self::PosOverflow => "value too large to fit in type", + Self::NegOverflow => "value too small to fit in type", + Self::WidthOverflow => "width is too large", + Self::MissingWidth => "missing width", + }) + } +} + +fn parse_int_value( + s: &str, + type_is_signed: bool, + type_width: Option, + parse_type: bool, +) -> Result, ParseIntValueError> { + if !parse_type && type_width.is_none() { + return Err(ParseIntValueError::MissingWidth); + } + let mut s = s.trim(); + if s.is_empty() { + return Err(ParseIntValueError::Empty); + } + let negative = match s.bytes().next() { + Some(ch @ (b'+' | b'-')) => { + s = s[1..].trim_start(); + ch == b'-' + } + _ => false, + }; + let radix = match s.bytes().next() { + Some(b'0') => match s.bytes().nth(1) { + Some(b'x' | b'X') => { + s = &s[2..]; + 16 + } + Some(b'b' | b'B') => { + s = &s[2..]; + 2 + } + Some(b'o' | b'O') => { + s = &s[2..]; + 8 + } + _ => 10, + }, + Some(b'1'..=b'9') => 10, + _ => return Err(ParseIntValueError::InvalidDigit), + }; + let mut any_digits = false; + let digits_end = s + .as_bytes() + .iter() + .position(|&ch| { + if ch == b'_' { + false + } else if (ch as char).to_digit(radix).is_some() { + any_digits = true; + false + } else { + true + } + }) + .unwrap_or(s.len()); + let digits = &s[..digits_end]; + s = &s[digits_end..]; + if !any_digits { + return Err(ParseIntValueError::MissingDigits); + } + let width = if parse_type { + const HDL_PREFIX: &[u8] = b"hdl_"; + let mut missing_type = ParseIntValueError::MissingType; + if s.as_bytes() + .get(..HDL_PREFIX.len()) + .is_some_and(|bytes| bytes.eq_ignore_ascii_case(HDL_PREFIX)) + { + s = &s[HDL_PREFIX.len()..]; + missing_type = ParseIntValueError::InvalidType; + } + let signed = match s.bytes().next() { + Some(b'u' | b'U') => false, + Some(b'i' | b'I') => true, + Some(_) => return Err(ParseIntValueError::InvalidType), + None => return Err(missing_type), + }; + s = &s[1..]; + let mut width = 0usize; + let mut any_digits = false; + for ch in s.bytes() { + let digit = (ch as char) + .to_digit(10) + .ok_or(ParseIntValueError::InvalidDigit)?; + any_digits = true; + width = width + .checked_mul(10) + .and_then(|v| v.checked_add(digit as usize)) + .ok_or(ParseIntValueError::WidthOverflow)?; + } + if !any_digits { + return Err(ParseIntValueError::MissingDigits); + } + if width > ::MAX_BITS { + return Err(ParseIntValueError::WidthOverflow); + } + let expected_width = type_width.unwrap_or(width); + if type_is_signed != signed || expected_width != width { + let expected_width = type_width.unwrap_or(width); + return Err(ParseIntValueError::TypeMismatch { + parsed_signed: signed, + parsed_width: width, + expected_signed: type_is_signed, + expected_width, + }); + } + width + } else { + if !s.is_empty() { + return Err(ParseIntValueError::InvalidDigit); + } + type_width.expect("checked earlier") + }; + if !type_is_signed && negative { + return Err(ParseIntValueError::InvalidDigit); + } + if radix == 10 { + let mut value: BigInt = digits + .replace("_", "") + .parse() + .expect("checked that the digits are valid already"); + if negative { + value = -value; + } + let uint_value: UIntValue = UInt::new(width).from_bigint_wrapping(&value); + if value.is_zero() { + Ok(uint_value.into_bits()) + } else { + for i in 0..width { + value.set_bit(i as u64, type_is_signed && negative); + } + if value.is_zero() { + Ok(uint_value.into_bits()) + } else if type_is_signed && negative { + if value.sign() == Sign::Minus && value.magnitude().is_one() { + Ok(uint_value.into_bits()) + } else { + Err(ParseIntValueError::NegOverflow) + } + } else { + Err(ParseIntValueError::PosOverflow) + } + } + } else { + let mut value = BitVec::repeat(false, width); + let bits_per_digit = match radix { + 2 => 1, + 8 => 3, + 16 => 4, + _ => unreachable!(), + }; + let mut digits = digits + .bytes() + .rev() + .filter_map(|ch| (ch as char).to_digit(radix)); + let overflow_error = if negative { + ParseIntValueError::NegOverflow + } else { + ParseIntValueError::PosOverflow + }; + for chunk in value.chunks_mut(bits_per_digit) { + if let Some(mut digit) = digits.next() { + let digit_bits = &mut digit.view_bits_mut::()[..chunk.len()]; + chunk.clone_from_bitslice(digit_bits); + digit_bits.fill(false); + if digit != 0 { + return Err(overflow_error); + } + } else { + break; + } + } + for digit in digits { + if digit != 0 { + return Err(overflow_error); + } + } + let negative_zero = if negative { + // negating a value happens in three regions: + // * the least-significant zeros, which are left as zeros + // * the least-significant one bit, which is left as a one bit + // * all the most-significant bits, which are inverted + // e.g.: + const { + let inp = 0b1010_1_000_u8; + let out = 0b0101_1_000_u8; + assert!(inp.wrapping_neg() == out); + }; + if let Some(first_one) = value.first_one() { + let most_significant_bits = &mut value[first_one + 1..]; + // modifies in-place despite using `Not::not` + let _ = Not::not(most_significant_bits); + false + } else { + true + } + } else { + false + }; + if !negative_zero && type_is_signed && negative != value[value.len() - 1] { + Err(overflow_error) + } else { + Ok(Arc::new(value)) + } + } +} + +fn deserialize_int_value<'de, D: Deserializer<'de>>( + deserializer: D, + type_is_signed: bool, + type_width: Option, +) -> Result, D::Error> { + struct IntValueVisitor { + type_is_signed: bool, + type_width: Option, + } + impl<'de> Visitor<'de> for IntValueVisitor { + type Value = Arc; + + fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str(if self.type_is_signed { + "SIntValue" + } else { + "UIntValue" + })?; + if let Some(type_width) = self.type_width { + write!(f, "<{type_width}>")?; + } + Ok(()) + } + + fn visit_str(self, v: &str) -> Result { + parse_int_value(v, self.type_is_signed, self.type_width, true).map_err(E::custom) + } + + fn visit_bytes(self, v: &[u8]) -> Result { + match std::str::from_utf8(v) { + Ok(v) => self.visit_str(v), + Err(_) => Err(Error::invalid_value(serde::de::Unexpected::Bytes(v), &self)), + } + } + } + deserializer.deserialize_str(IntValueVisitor { + type_is_signed, + type_width, + }) +} + macro_rules! impl_int { ($pretty_name:ident, $name:ident, $generic_name:ident, $value:ident, $SIGNED:literal) => { #[derive(Copy, Clone, PartialEq, Eq, Hash)] @@ -289,6 +611,12 @@ macro_rules! impl_int { } Expr::from_dyn_int(MemoizeBitsToExpr.get_cow(bits)) } + fn from_str_without_ty( + self, + s: &str, + ) -> Result::Err> { + parse_int_value(s, $SIGNED, Some(self.width()), false).map(Self::Value::new) + } } impl IntType for $name { @@ -324,7 +652,7 @@ macro_rules! impl_int { #[track_caller] fn from_canonical(canonical_type: CanonicalType) -> Self { let CanonicalType::$pretty_name(retval) = canonical_type else { - panic!("expected {}", stringify!($name)); + panic!("expected {}", stringify!($pretty_name)); }; $name { width: Width::from_usize(retval.width), @@ -349,6 +677,12 @@ macro_rules! impl_int { } } + impl Default for $name { + fn default() -> Self { + Self::TYPE + } + } + impl StaticType for $name { const TYPE: Self = Self { width: Width::SIZE }; const MASK_TYPE: Self::MaskType = Bool; @@ -361,6 +695,46 @@ macro_rules! impl_int { const MASK_TYPE_PROPERTIES: TypeProperties = Bool::TYPE_PROPERTIES; } + impl Serialize for $name { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + self.canonical().serialize(serializer) + } + } + + impl<'de, Width: Size> Deserialize<'de> for $name { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let name = |width| -> String { + if let Some(width) = width { + format!("a {}<{width}>", stringify!($pretty_name)) + } else { + format!("a {}", stringify!($pretty_name)) + } + }; + match CanonicalType::deserialize(deserializer)? { + CanonicalType::$pretty_name(retval) => { + if let Some(width) = Width::try_from_usize(retval.width()) { + Ok($name { width }) + } else { + Err(Error::invalid_value( + serde::de::Unexpected::Other(&name(Some(retval.width()))), + &&*name(Width::KNOWN_VALUE), + )) + } + } + ty => Err(Error::invalid_value( + serde::de::Unexpected::Other(ty.as_serde_unexpected_str()), + &&*name(Width::KNOWN_VALUE), + )), + } + } + } + #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, Default)] pub struct $generic_name; @@ -378,7 +752,7 @@ macro_rules! impl_int { _phantom: PhantomData, } - impl fmt::Debug for $value { + impl fmt::Display for $value { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let value = self.to_bigint(); let (sign, magnitude) = value.into_parts(); @@ -392,6 +766,32 @@ macro_rules! impl_int { } } + impl fmt::Debug for $value { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Display::fmt(self, f) + } + } + + impl std::str::FromStr for $value { + type Err = ParseIntValueError; + + fn from_str(s: &str) -> Result { + parse_int_value(s, $SIGNED, Width::KNOWN_VALUE, true).map(Self::new) + } + } + + impl Serialize for $value { + fn serialize(&self, serializer: S) -> Result { + self.to_string().serialize(serializer) + } + } + + impl<'de, Width: Size> Deserialize<'de> for $value { + fn deserialize>(deserializer: D) -> Result { + deserialize_int_value(deserializer, $SIGNED, Width::KNOWN_VALUE).map(Self::new) + } + } + impl PartialEq<$value> for $value { fn eq(&self, other: &$value) -> bool { self.to_bigint() == other.to_bigint() @@ -633,11 +1033,13 @@ pub trait BoolOrIntType: Type + sealed::BoolOrIntTypeSealed { + Ord + std::hash::Hash + fmt::Debug + + fmt::Display + Send + Sync + 'static + ToExpr - + Into; + + Into + + std::str::FromStr; fn width(self) -> usize; fn new(width: ::SizeType) -> Self; fn new_static() -> Self @@ -710,9 +1112,12 @@ pub trait BoolOrIntType: Type + sealed::BoolOrIntTypeSealed { bytes, bit_width, ))) } + fn from_str_without_ty(self, s: &str) -> Result::Err>; } -pub trait IntType: BoolOrIntType::Dyn> { +pub trait IntType: + BoolOrIntType::Dyn, Value: FromStr> +{ type Dyn: IntType; fn as_dyn_int(self) -> Self::Dyn { Self::new_dyn(self.width()) @@ -752,7 +1157,7 @@ pub trait IntType: BoolOrIntType::Dyn> { } } -#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, Default)] pub struct Bool; impl sealed::BoolOrIntTypeSealed for Bool {} @@ -784,6 +1189,10 @@ impl BoolOrIntType for Bool { assert_eq!(bits.len(), 1); bits[0] } + + fn from_str_without_ty(self, s: &str) -> Result::Err> { + FromStr::from_str(s) + } } impl Bool { @@ -874,4 +1283,104 @@ mod tests { assert_eq!(SInt::for_value(3).width, 3); assert_eq!(SInt::for_value(4).width, 4); } + + #[test] + fn test_serde_round_trip() { + use serde_json::json; + #[track_caller] + fn check( + value: T, + expected: serde_json::Value, + ) { + assert_eq!(serde_json::to_value(&value).unwrap(), expected); + assert_eq!(value, T::deserialize(expected).unwrap()); + } + check(UInt[0], json! { { "UInt": { "width": 0 } } }); + check(UInt::<0>::TYPE, json! { { "UInt": { "width": 0 } } }); + check(UInt::<35>::TYPE, json! { { "UInt": { "width": 35 } } }); + check(SInt[0], json! { { "SInt": { "width": 0 } } }); + check(SInt::<0>::TYPE, json! { { "SInt": { "width": 0 } } }); + check(SInt::<35>::TYPE, json! { { "SInt": { "width": 35 } } }); + check(Bool, json! { "Bool" }); + check(UIntValue::from(0u8), json! { "0x0_u8" }); + check(SIntValue::from(-128i8), json! { "-0x80_i8" }); + check(UInt[3].from_int_wrapping(5), json! { "0x5_u3" }); + check(UInt[12].from_int_wrapping(0x1123), json! { "0x123_u12" }); + check(SInt[12].from_int_wrapping(0xFEE), json! { "-0x12_i12" }); + check(SInt[12].from_int_wrapping(0x7EE), json! { "0x7EE_i12" }); + } + + #[test] + fn test_deserialize() { + use serde_json::json; + #[track_caller] + fn check( + expected: Result, + input: serde_json::Value, + ) { + let mut error = String::new(); + let value = T::deserialize(input).map_err(|e| -> &str { + error = e.to_string(); + &error + }); + assert_eq!(value, expected); + } + check::>( + Err("invalid value: a UInt<2>, expected a UInt<0>"), + json! { { "UInt": { "width": 2 } } }, + ); + check::>( + Err("invalid value: a Bool, expected a UInt<0>"), + json! { "Bool" }, + ); + check::>( + Err("invalid value: a Bool, expected a SInt<0>"), + json! { "Bool" }, + ); + check::( + Err("invalid value: a Bool, expected a UInt"), + json! { "Bool" }, + ); + check::( + Err("invalid value: a Bool, expected a SInt"), + json! { "Bool" }, + ); + check::(Err("value too large to fit in type"), json! { "2_u1" }); + check::(Err("value too large to fit in type"), json! { "10_u1" }); + check::(Err("value too large to fit in type"), json! { "0x2_u1" }); + check::(Err("value too large to fit in type"), json! { "0b10_u1" }); + check::(Err("value too large to fit in type"), json! { "0o2_u1" }); + check::(Err("value too large to fit in type"), json! { "0o377_i8" }); + check::(Err("value too large to fit in type"), json! { "0o200_i8" }); + check(Ok(SInt[8].from_int_wrapping(i8::MAX)), json! { "0o177_i8" }); + check::(Err("value too small to fit in type"), json! { "-0o201_i8" }); + check::(Err("value too small to fit in type"), json! { "-0o377_i8" }); + check::(Err("value too small to fit in type"), json! { "-0o400_i8" }); + check::( + Err("value too small to fit in type"), + json! { "-0o4000_i8" }, + ); + check(Ok(UIntValue::from(0u8)), json! { "0_u8" }); + check(Ok(UIntValue::from(0u8)), json! { "0b0_u8" }); + check(Ok(UIntValue::from(0u8)), json! { "00_u8" }); + check(Ok(UIntValue::from(0u8)), json! { "0x0_u8" }); + check(Ok(UIntValue::from(0u8)), json! { "0o0_u8" }); + check(Ok(SIntValue::from(-128i8)), json! { "-0x000_80_i8" }); + check(Ok(SIntValue::from(-128i8)), json! { "-0o002_00_hdl_i8" }); + check(Ok(SIntValue::from(-128i8)), json! { "-0b1__000_0000_i8" }); + check(Ok(UInt[3].from_int_wrapping(5)), json! { " + 0x5_u3 " }); + check( + Ok(UInt[12].from_int_wrapping(0x1123)), + json! { "0x1_2_3_hdl_u12" }, + ); + check(Ok(SInt[12].from_int_wrapping(0xFEE)), json! { "-0x12_i12" }); + check( + Ok(SInt[12].from_int_wrapping(0x7EE)), + json! { " + \t0x7__E_e_i012\n" }, + ); + check(Ok(SInt[0].from_int_wrapping(0)), json! { "-0i0" }); + check(Ok(SInt[1].from_int_wrapping(0)), json! { "-0i1" }); + check(Ok(SInt[0].from_int_wrapping(0)), json! { "-0x0i0" }); + check(Ok(SInt[1].from_int_wrapping(0)), json! { "-0x0i1" }); + } } diff --git a/crates/fayalite/src/phantom_const.rs b/crates/fayalite/src/phantom_const.rs index 8422ae0..d2e94d9 100644 --- a/crates/fayalite/src/phantom_const.rs +++ b/crates/fayalite/src/phantom_const.rs @@ -1,9 +1,6 @@ // SPDX-License-Identifier: LGPL-3.0-or-later // See Notices.txt for copyright information -use bitvec::slice::BitSlice; -use serde::{de::DeserializeOwned, Deserialize, Serialize}; - use crate::{ expr::{ ops::{ExprPartialEq, ExprPartialOrd}, @@ -13,13 +10,23 @@ use crate::{ intern::{Intern, Interned, InternedCompare, LazyInterned, Memoize}, sim::value::{SimValue, SimValuePartialEq, ToSimValue, ToSimValueWithType}, source_location::SourceLocation, - ty::{impl_match_variant_as_self, CanonicalType, StaticType, Type, TypeProperties}, + ty::{ + impl_match_variant_as_self, + serde_impls::{SerdeCanonicalType, SerdePhantomConst}, + CanonicalType, StaticType, Type, TypeProperties, + }, +}; +use bitvec::slice::BitSlice; +use serde::{ + de::{DeserializeOwned, Error}, + Deserialize, Deserializer, Serialize, Serializer, }; use std::{ any::Any, fmt, hash::{Hash, Hasher}, marker::PhantomData, + ops::Index, }; #[derive(Clone)] @@ -115,6 +122,20 @@ pub struct PhantomConst, } +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, Default)] +pub struct PhantomConstWithoutGenerics; + +#[allow(non_upper_case_globals)] +pub const PhantomConst: PhantomConstWithoutGenerics = PhantomConstWithoutGenerics; + +impl Index for PhantomConstWithoutGenerics { + type Output = PhantomConst; + + fn index(&self, value: T) -> &Self::Output { + Interned::into_inner(PhantomConst::new(value.intern()).intern_sized()) + } +} + impl fmt::Debug for PhantomConst { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_tuple("PhantomConst").field(&self.get()).finish() @@ -201,17 +222,6 @@ impl Memoize for PhantomConstCanonicalMemoize PhantomConst -where - Interned: Default, -{ - pub const fn default() -> Self { - PhantomConst { - value: LazyInterned::new_lazy(&Interned::::default), - } - } -} - impl PhantomConst { pub fn new(value: Interned) -> Self { Self { @@ -286,16 +296,53 @@ impl Type for PhantomConst { } } +impl Default for PhantomConst +where + Interned: Default, +{ + fn default() -> Self { + Self::TYPE + } +} + impl StaticType for PhantomConst where Interned: Default, { - const TYPE: Self = Self::default(); + const TYPE: Self = PhantomConst { + value: LazyInterned::new_lazy(&Interned::::default), + }; const MASK_TYPE: Self::MaskType = (); const TYPE_PROPERTIES: TypeProperties = <()>::TYPE_PROPERTIES; const MASK_TYPE_PROPERTIES: TypeProperties = <()>::TYPE_PROPERTIES; } +type SerdeType = SerdeCanonicalType>>; + +impl Serialize for PhantomConst { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + SerdeType::::PhantomConst(SerdePhantomConst(self.get())).serialize(serializer) + } +} + +impl<'de, T: ?Sized + PhantomConstValue> Deserialize<'de> for PhantomConst { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + match SerdeType::::deserialize(deserializer)? { + SerdeCanonicalType::PhantomConst(SerdePhantomConst(value)) => Ok(Self::new(value)), + ty => Err(Error::invalid_value( + serde::de::Unexpected::Other(ty.as_serde_unexpected_str()), + &"a PhantomConst", + )), + } + } +} + impl ExprPartialEq for PhantomConst { fn cmp_eq(lhs: Expr, rhs: Expr) -> Expr { assert_eq!(Expr::ty(lhs), Expr::ty(rhs)); diff --git a/crates/fayalite/src/sim/value.rs b/crates/fayalite/src/sim/value.rs index d415af4..737043a 100644 --- a/crates/fayalite/src/sim/value.rs +++ b/crates/fayalite/src/sim/value.rs @@ -16,6 +16,7 @@ use crate::{ }, }; use bitvec::{slice::BitSlice, vec::BitVec}; +use serde::{Deserialize, Deserializer, Serialize, Serializer}; use std::{ fmt, ops::{Deref, DerefMut}, @@ -94,6 +95,40 @@ impl AlternatingCellMethods for SimValueInner { fn shared_to_unique(&mut self) {} } +#[derive(Serialize, Deserialize)] +#[serde(rename = "SimValue")] +#[serde(bound( + serialize = "T: Type + Serialize", + deserialize = "T: Type> + Deserialize<'de>" +))] +struct SerdeSimValue<'a, T: Type> { + ty: T, + value: std::borrow::Cow<'a, T::SimValue>, +} + +impl + Serialize> Serialize for SimValue { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + SerdeSimValue { + ty: SimValue::ty(self), + value: std::borrow::Cow::Borrowed(&*self), + } + .serialize(serializer) + } +} + +impl<'de, T: Type> + Deserialize<'de>> Deserialize<'de> for SimValue { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let SerdeSimValue { ty, value } = SerdeSimValue::::deserialize(deserializer)?; + Ok(SimValue::from_value(ty, value.into_owned())) + } +} + pub struct SimValue { inner: AlternatingCell>, } diff --git a/crates/fayalite/src/ty.rs b/crates/fayalite/src/ty.rs index 23680f7..8f41c5c 100644 --- a/crates/fayalite/src/ty.rs +++ b/crates/fayalite/src/ty.rs @@ -16,8 +16,11 @@ use crate::{ util::ConstUsize, }; use bitvec::slice::BitSlice; +use serde::{de::DeserializeOwned, Deserialize, Deserializer, Serialize, Serializer}; use std::{fmt, hash::Hash, iter::FusedIterator, ops::Index, sync::Arc}; +pub(crate) mod serde_impls; + #[derive(Copy, Clone, Hash, PartialEq, Eq, Debug)] #[non_exhaustive] pub struct TypeProperties { @@ -60,6 +63,24 @@ impl fmt::Debug for CanonicalType { } } +impl Serialize for CanonicalType { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + serde_impls::SerdeCanonicalType::from(*self).serialize(serializer) + } +} + +impl<'de> Deserialize<'de> for CanonicalType { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + Ok(serde_impls::SerdeCanonicalType::deserialize(deserializer)?.into()) + } +} + impl CanonicalType { pub fn type_properties(self) -> TypeProperties { match self { @@ -158,6 +179,9 @@ impl CanonicalType { } } } + pub(crate) fn as_serde_unexpected_str(self) -> &'static str { + serde_impls::SerdeCanonicalType::from(self).as_serde_unexpected_str() + } } pub trait MatchVariantAndInactiveScope: Sized { @@ -224,6 +248,34 @@ macro_rules! impl_base_type { }; } +macro_rules! impl_base_type_serde { + ($name:ident, $expected:literal) => { + impl Serialize for $name { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + self.canonical().serialize(serializer) + } + } + + impl<'de> Deserialize<'de> for $name { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + match CanonicalType::deserialize(deserializer)? { + CanonicalType::$name(retval) => Ok(retval), + ty => Err(serde::de::Error::invalid_value( + serde::de::Unexpected::Other(ty.as_serde_unexpected_str()), + &$expected, + )), + } + } + } + }; +} + impl_base_type!(UInt); impl_base_type!(SInt); impl_base_type!(Bool); @@ -236,6 +288,14 @@ impl_base_type!(Reset); impl_base_type!(Clock); impl_base_type!(PhantomConst); +impl_base_type_serde!(Bool, "a Bool"); +impl_base_type_serde!(Enum, "an Enum"); +impl_base_type_serde!(Bundle, "a Bundle"); +impl_base_type_serde!(AsyncReset, "an AsyncReset"); +impl_base_type_serde!(SyncReset, "a SyncReset"); +impl_base_type_serde!(Reset, "a Reset"); +impl_base_type_serde!(Clock, "a Clock"); + impl sealed::BaseTypeSealed for CanonicalType {} impl BaseType for CanonicalType {} @@ -293,7 +353,17 @@ pub trait Type: fn sim_value_to_bits(&self, value: &Self::SimValue, bits: &mut BitSlice); } -pub trait BaseType: Type + sealed::BaseTypeSealed + Into {} +pub trait BaseType: + Type< + BaseType = Self, + MaskType: Serialize + DeserializeOwned, + SimValue: Serialize + DeserializeOwned, + > + sealed::BaseTypeSealed + + Into + + Serialize + + DeserializeOwned +{ +} macro_rules! impl_match_variant_as_self { () => { @@ -362,7 +432,7 @@ impl Type for CanonicalType { } } -#[derive(Clone, PartialEq, Eq, Hash, Debug)] +#[derive(Clone, PartialEq, Eq, Hash, Debug, Serialize, Deserialize)] pub struct OpaqueSimValue { bits: UIntValue, } @@ -399,7 +469,7 @@ impl> ToSimValueWithType for OpaqueSimValu } } -pub trait StaticType: Type { +pub trait StaticType: Type + Default { const TYPE: Self; const MASK_TYPE: Self::MaskType; const TYPE_PROPERTIES: TypeProperties; diff --git a/crates/fayalite/src/ty/serde_impls.rs b/crates/fayalite/src/ty/serde_impls.rs new file mode 100644 index 0000000..2ea4362 --- /dev/null +++ b/crates/fayalite/src/ty/serde_impls.rs @@ -0,0 +1,130 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// See Notices.txt for copyright information + +use crate::{ + array::Array, + bundle::{Bundle, BundleType}, + clock::Clock, + enum_::{Enum, EnumType}, + int::{Bool, SInt, UInt}, + intern::Interned, + phantom_const::{PhantomConstCanonicalValue, PhantomConstValue}, + prelude::PhantomConst, + reset::{AsyncReset, Reset, SyncReset}, + ty::{BaseType, CanonicalType}, +}; +use serde::{Deserialize, Deserializer, Serialize, Serializer}; + +pub(crate) struct SerdePhantomConst(pub T); + +impl Serialize for SerdePhantomConst> { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + self.0.serialize(serializer) + } +} + +impl<'de, T: ?Sized + PhantomConstValue> Deserialize<'de> for SerdePhantomConst> { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + T::deserialize_value(deserializer).map(Self) + } +} + +#[derive(Serialize, Deserialize)] +#[serde(rename = "CanonicalType")] +pub(crate) enum SerdeCanonicalType< + ArrayElement = CanonicalType, + ThePhantomConst = SerdePhantomConst>, +> { + UInt { + width: usize, + }, + SInt { + width: usize, + }, + Bool, + Array { + element: ArrayElement, + len: usize, + }, + Enum { + variants: Interned<[crate::enum_::EnumVariant]>, + }, + Bundle { + fields: Interned<[crate::bundle::BundleField]>, + }, + AsyncReset, + SyncReset, + Reset, + Clock, + PhantomConst(ThePhantomConst), +} + +impl SerdeCanonicalType { + pub(crate) fn as_serde_unexpected_str(&self) -> &'static str { + match self { + Self::UInt { .. } => "a UInt", + Self::SInt { .. } => "a SInt", + Self::Bool => "a Bool", + Self::Array { .. } => "an Array", + Self::Enum { .. } => "an Enum", + Self::Bundle { .. } => "a Bundle", + Self::AsyncReset => "an AsyncReset", + Self::SyncReset => "a SyncReset", + Self::Reset => "a Reset", + Self::Clock => "a Clock", + Self::PhantomConst(_) => "a PhantomConst", + } + } +} + +impl From for SerdeCanonicalType { + fn from(ty: T) -> Self { + let ty: CanonicalType = ty.into(); + match ty { + CanonicalType::UInt(ty) => Self::UInt { width: ty.width() }, + CanonicalType::SInt(ty) => Self::SInt { width: ty.width() }, + CanonicalType::Bool(Bool {}) => Self::Bool, + CanonicalType::Array(ty) => Self::Array { + element: ty.element(), + len: ty.len(), + }, + CanonicalType::Enum(ty) => Self::Enum { + variants: ty.variants(), + }, + CanonicalType::Bundle(ty) => Self::Bundle { + fields: ty.fields(), + }, + CanonicalType::AsyncReset(AsyncReset {}) => Self::AsyncReset, + CanonicalType::SyncReset(SyncReset {}) => Self::SyncReset, + CanonicalType::Reset(Reset {}) => Self::Reset, + CanonicalType::Clock(Clock {}) => Self::Clock, + CanonicalType::PhantomConst(ty) => Self::PhantomConst(SerdePhantomConst(ty.get())), + } + } +} + +impl From for CanonicalType { + fn from(ty: SerdeCanonicalType) -> Self { + match ty { + SerdeCanonicalType::UInt { width } => Self::UInt(UInt::new(width)), + SerdeCanonicalType::SInt { width } => Self::SInt(SInt::new(width)), + SerdeCanonicalType::Bool => Self::Bool(Bool), + SerdeCanonicalType::Array { element, len } => Self::Array(Array::new(element, len)), + SerdeCanonicalType::Enum { variants } => Self::Enum(Enum::new(variants)), + SerdeCanonicalType::Bundle { fields } => Self::Bundle(Bundle::new(fields)), + SerdeCanonicalType::AsyncReset => Self::AsyncReset(AsyncReset), + SerdeCanonicalType::SyncReset => Self::SyncReset(SyncReset), + SerdeCanonicalType::Reset => Self::Reset(Reset), + SerdeCanonicalType::Clock => Self::Clock(Clock), + SerdeCanonicalType::PhantomConst(value) => { + Self::PhantomConst(PhantomConst::new(value.0)) + } + } + } +} diff --git a/crates/fayalite/src/util/const_bool.rs b/crates/fayalite/src/util/const_bool.rs index 7033d6a..7def3b5 100644 --- a/crates/fayalite/src/util/const_bool.rs +++ b/crates/fayalite/src/util/const_bool.rs @@ -1,5 +1,9 @@ // SPDX-License-Identifier: LGPL-3.0-or-later // See Notices.txt for copyright information +use serde::{ + de::{DeserializeOwned, Error, Unexpected}, + Deserialize, Deserializer, Serialize, Serializer, +}; use std::{fmt::Debug, hash::Hash, mem::ManuallyDrop, ptr}; mod sealed { @@ -9,7 +13,17 @@ mod sealed { /// # Safety /// the only implementation is `ConstBool` pub unsafe trait GenericConstBool: - sealed::Sealed + Copy + Ord + Hash + Default + Debug + 'static + Send + Sync + sealed::Sealed + + Copy + + Ord + + Hash + + Default + + Debug + + 'static + + Send + + Sync + + Serialize + + DeserializeOwned { const VALUE: bool; } @@ -30,6 +44,32 @@ unsafe impl GenericConstBool for ConstBool { const VALUE: bool = VALUE; } +impl Serialize for ConstBool { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + VALUE.serialize(serializer) + } +} + +impl<'de, const VALUE: bool> Deserialize<'de> for ConstBool { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let value = bool::deserialize(deserializer)?; + if value == VALUE { + Ok(ConstBool) + } else { + Err(D::Error::invalid_value( + Unexpected::Bool(value), + &if VALUE { "true" } else { "false" }, + )) + } + } +} + pub trait ConstBoolDispatchTag { type Type; } diff --git a/crates/fayalite/src/util/const_usize.rs b/crates/fayalite/src/util/const_usize.rs index a605336..e098a12 100644 --- a/crates/fayalite/src/util/const_usize.rs +++ b/crates/fayalite/src/util/const_usize.rs @@ -1,5 +1,9 @@ // SPDX-License-Identifier: LGPL-3.0-or-later // See Notices.txt for copyright information +use serde::{ + de::{DeserializeOwned, Error, Unexpected}, + Deserialize, Deserializer, Serialize, Serializer, +}; use std::{fmt::Debug, hash::Hash}; mod sealed { @@ -8,7 +12,17 @@ mod sealed { /// the only implementation is `ConstUsize` pub trait GenericConstUsize: - sealed::Sealed + Copy + Ord + Hash + Default + Debug + 'static + Send + Sync + sealed::Sealed + + Copy + + Ord + + Hash + + Default + + Debug + + 'static + + Send + + Sync + + Serialize + + DeserializeOwned { const VALUE: usize; } @@ -27,3 +41,29 @@ impl sealed::Sealed for ConstUsize {} impl GenericConstUsize for ConstUsize { const VALUE: usize = VALUE; } + +impl Serialize for ConstUsize { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + VALUE.serialize(serializer) + } +} + +impl<'de, const VALUE: usize> Deserialize<'de> for ConstUsize { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let value = usize::deserialize(deserializer)?; + if value == VALUE { + Ok(ConstUsize) + } else { + Err(D::Error::invalid_value( + Unexpected::Unsigned(value as u64), + &&*VALUE.to_string(), + )) + } + } +} diff --git a/crates/fayalite/tests/hdl_types.rs b/crates/fayalite/tests/hdl_types.rs index b191016..8802fd4 100644 --- a/crates/fayalite/tests/hdl_types.rs +++ b/crates/fayalite/tests/hdl_types.rs @@ -4,11 +4,17 @@ use fayalite::{ bundle::BundleType, enum_::EnumType, int::{BoolOrIntType, IntType}, + phantom_const::PhantomConst, prelude::*, ty::StaticType, }; use std::marker::PhantomData; +#[hdl(outline_generated)] +pub struct MyConstSize { + pub size: PhantomConst>, +} + #[hdl(outline_generated)] pub struct S { pub a: T, From 001fd31451398eec481375d3ee1395504580a315 Mon Sep 17 00:00:00 2001 From: Jacob Lifshay Date: Fri, 4 Apr 2025 19:05:04 -0700 Subject: [PATCH 25/38] add UIntInRange[Inclusive][Type] --- crates/fayalite/src/int.rs | 67 ++- crates/fayalite/src/int/uint_in_range.rs | 614 +++++++++++++++++++++++ crates/fayalite/src/phantom_const.rs | 7 +- crates/fayalite/tests/module.rs | 88 +++- 4 files changed, 759 insertions(+), 17 deletions(-) create mode 100644 crates/fayalite/src/int/uint_in_range.rs diff --git a/crates/fayalite/src/int.rs b/crates/fayalite/src/int.rs index 13b8cf1..053bd1d 100644 --- a/crates/fayalite/src/int.rs +++ b/crates/fayalite/src/int.rs @@ -7,6 +7,7 @@ use crate::{ target::{GetTarget, Target}, Expr, NotALiteralExpr, ToExpr, ToLiteralBits, }, + hdl, intern::{Intern, Interned, Memoize}, sim::value::{SimValue, ToSimValueWithType}, source_location::SourceLocation, @@ -30,6 +31,23 @@ use std::{ sync::Arc, }; +mod uint_in_range; + +#[hdl] +pub type UIntInRangeType = uint_in_range::UIntInRangeType; + +#[hdl] +pub type UIntInRange = + UIntInRangeType, ConstUsize>; + +#[hdl] +pub type UIntInRangeInclusiveType = + uint_in_range::UIntInRangeInclusiveType; + +#[hdl] +pub type UIntInRangeInclusive = + UIntInRangeInclusiveType, ConstUsize>; + mod sealed { pub trait BoolOrIntTypeSealed {} pub trait SizeSealed {} @@ -536,19 +554,14 @@ macro_rules! impl_int { pub const $name: $generic_name = $generic_name; impl $name { - pub fn new(width: Width::SizeType) -> Self { + pub const fn new(width: Width::SizeType) -> Self { Self { width } } pub fn width(self) -> usize { Width::as_usize(self.width) } pub fn type_properties(self) -> TypeProperties { - TypeProperties { - is_passive: true, - is_storable: true, - is_castable_from_bits: true, - bit_width: self.width(), - } + self.as_dyn_int().type_properties_dyn() } pub fn bits_from_bigint_wrapping(self, v: &BigInt) -> BitVec { BoolOrIntType::bits_from_bigint_wrapping(self, v) @@ -624,12 +637,20 @@ macro_rules! impl_int { } impl $name { - pub fn new_dyn(width: usize) -> Self { + pub const fn new_dyn(width: usize) -> Self { Self { width } } pub fn bits_to_bigint(bits: &BitSlice) -> BigInt { ::bits_to_bigint(bits) } + pub const fn type_properties_dyn(self) -> TypeProperties { + TypeProperties { + is_passive: true, + is_storable: true, + is_castable_from_bits: true, + bit_width: self.width, + } + } } impl $name { @@ -686,12 +707,10 @@ macro_rules! impl_int { impl StaticType for $name { const TYPE: Self = Self { width: Width::SIZE }; const MASK_TYPE: Self::MaskType = Bool; - const TYPE_PROPERTIES: TypeProperties = TypeProperties { - is_passive: true, - is_storable: true, - is_castable_from_bits: true, - bit_width: Width::VALUE, - }; + const TYPE_PROPERTIES: TypeProperties = $name { + width: Width::VALUE, + } + .type_properties_dyn(); const MASK_TYPE_PROPERTIES: TypeProperties = Bool::TYPE_PROPERTIES; } @@ -905,6 +924,10 @@ impl UInt { let v: BigUint = v.into(); Self::new(v.bits().try_into().expect("too big")) } + /// gets the smallest `UInt` that fits `v` losslessly + pub const fn for_value_usize(v: usize) -> Self { + Self::new((usize::BITS - v.leading_zeros()) as usize) + } /// gets the smallest `UInt` that fits `r` losslessly, panics if `r` is empty #[track_caller] pub fn range(r: Range>) -> Self { @@ -915,6 +938,12 @@ impl UInt { } /// gets the smallest `UInt` that fits `r` losslessly, panics if `r` is empty #[track_caller] + pub const fn range_usize(r: Range) -> Self { + assert!(r.end != 0, "empty range"); + Self::range_inclusive_usize(r.start..=(r.end - 1)) + } + /// gets the smallest `UInt` that fits `r` losslessly, panics if `r` is empty + #[track_caller] pub fn range_inclusive(r: RangeInclusive>) -> Self { let (start, end) = r.into_inner(); let start: BigUint = start.into(); @@ -924,6 +953,16 @@ impl UInt { // so must not take more bits than `end` Self::for_value(end) } + /// gets the smallest `UInt` that fits `r` losslessly, panics if `r` is empty + #[track_caller] + pub const fn range_inclusive_usize(r: RangeInclusive) -> Self { + let start = *r.start(); + let end = *r.end(); + assert!(start <= end, "empty range"); + // no need to check `start`` since it's no larger than `end` + // so must not take more bits than `end` + Self::for_value_usize(end) + } } impl SInt { diff --git a/crates/fayalite/src/int/uint_in_range.rs b/crates/fayalite/src/int/uint_in_range.rs new file mode 100644 index 0000000..0e2d07e --- /dev/null +++ b/crates/fayalite/src/int/uint_in_range.rs @@ -0,0 +1,614 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// See Notices.txt for copyright information + +use crate::{ + bundle::{Bundle, BundleField, BundleType, BundleTypePropertiesBuilder, NoBuilder}, + expr::{ + ops::{ExprCastTo, ExprPartialEq, ExprPartialOrd}, + CastBitsTo, CastTo, CastToBits, Expr, HdlPartialEq, HdlPartialOrd, + }, + int::{Bool, DynSize, KnownSize, Size, SizeType, UInt, UIntType}, + intern::{Intern, Interned}, + phantom_const::PhantomConst, + sim::value::{SimValue, SimValuePartialEq, ToSimValueWithType}, + source_location::SourceLocation, + ty::{impl_match_variant_as_self, CanonicalType, StaticType, Type, TypeProperties}, +}; +use bitvec::{order::Lsb0, slice::BitSlice, view::BitView}; +use serde::{ + de::{value::UsizeDeserializer, Error, Visitor}, + Deserialize, Deserializer, Serialize, Serializer, +}; +use std::{fmt, marker::PhantomData, ops::Index}; + +const UINT_IN_RANGE_TYPE_FIELD_NAMES: [&'static str; 2] = ["value", "range"]; + +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, Default)] +pub struct UIntInRangeMaskType { + value: Bool, + range: PhantomConstRangeMaskType, +} + +impl Type for UIntInRangeMaskType { + type BaseType = Bundle; + type MaskType = Self; + type SimValue = bool; + impl_match_variant_as_self!(); + + fn mask_type(&self) -> Self::MaskType { + *self + } + + fn canonical(&self) -> CanonicalType { + CanonicalType::Bundle(Bundle::new(self.fields())) + } + + fn from_canonical(canonical_type: CanonicalType) -> Self { + let fields = Bundle::from_canonical(canonical_type).fields(); + let [BundleField { + name: value_name, + flipped: false, + ty: value, + }, BundleField { + name: range_name, + flipped: false, + ty: range, + }] = *fields + else { + panic!("expected UIntInRangeMaskType"); + }; + assert_eq!([&*value_name, &*range_name], UINT_IN_RANGE_TYPE_FIELD_NAMES); + let value = Bool::from_canonical(value); + let range = PhantomConstRangeMaskType::from_canonical(range); + Self { value, range } + } + + fn source_location() -> SourceLocation { + SourceLocation::builtin() + } + + fn sim_value_from_bits(&self, bits: &BitSlice) -> Self::SimValue { + Bool.sim_value_from_bits(bits) + } + + fn sim_value_clone_from_bits(&self, value: &mut Self::SimValue, bits: &BitSlice) { + Bool.sim_value_clone_from_bits(value, bits); + } + + fn sim_value_to_bits(&self, value: &Self::SimValue, bits: &mut BitSlice) { + Bool.sim_value_to_bits(value, bits); + } +} + +impl BundleType for UIntInRangeMaskType { + type Builder = NoBuilder; + type FilledBuilder = Expr; + + fn fields(&self) -> Interned<[BundleField]> { + let [value_name, range_name] = UINT_IN_RANGE_TYPE_FIELD_NAMES; + let Self { value, range } = self; + [ + BundleField { + name: value_name.intern(), + flipped: false, + ty: value.canonical(), + }, + BundleField { + name: range_name.intern(), + flipped: false, + ty: range.canonical(), + }, + ][..] + .intern() + } +} + +impl StaticType for UIntInRangeMaskType { + const TYPE: Self = Self { + value: Bool, + range: PhantomConstRangeMaskType::TYPE, + }; + const MASK_TYPE: Self::MaskType = Self::TYPE; + const TYPE_PROPERTIES: TypeProperties = BundleTypePropertiesBuilder::new() + .field(false, Bool::TYPE_PROPERTIES) + .field(false, PhantomConstRangeMaskType::TYPE_PROPERTIES) + .finish(); + const MASK_TYPE_PROPERTIES: TypeProperties = Self::TYPE_PROPERTIES; +} + +impl ToSimValueWithType for bool { + fn to_sim_value_with_type(&self, ty: UIntInRangeMaskType) -> SimValue { + SimValue::from_value(ty, *self) + } +} + +impl ExprCastTo for UIntInRangeMaskType { + fn cast_to(src: Expr, to_type: Bool) -> Expr { + src.cast_to_bits().cast_to(to_type) + } +} + +impl ExprCastTo for Bool { + fn cast_to(src: Expr, to_type: UIntInRangeMaskType) -> Expr { + src.cast_to_static::>().cast_bits_to(to_type) + } +} + +impl ExprPartialEq for UIntInRangeMaskType { + fn cmp_eq(lhs: Expr, rhs: Expr) -> Expr { + lhs.cast_to_bits().cmp_eq(rhs.cast_to_bits()) + } + fn cmp_ne(lhs: Expr, rhs: Expr) -> Expr { + lhs.cast_to_bits().cmp_ne(rhs.cast_to_bits()) + } +} + +impl SimValuePartialEq for UIntInRangeMaskType { + fn sim_value_eq(this: &SimValue, other: &SimValue) -> bool { + **this == **other + } +} + +type PhantomConstRangeMaskType = > as Type>::MaskType; + +#[derive(Default, Copy, Clone, Debug)] +struct RangeParseError; + +macro_rules! define_uint_in_range_type { + ( + $UIntInRange:ident, + $UIntInRangeType:ident, + $UIntInRangeTypeWithoutGenerics:ident, + $UIntInRangeTypeWithStart:ident, + $SerdeRange:ident, + $range_operator_str:literal, + |$uint_range_usize_start:ident, $uint_range_usize_end:ident| $uint_range_usize:expr, + ) => { + #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] + struct $SerdeRange { + start: Start::SizeType, + end: End::SizeType, + } + + impl Default for $SerdeRange { + fn default() -> Self { + Self { + start: Start::SIZE, + end: End::SIZE, + } + } + } + + impl std::str::FromStr for $SerdeRange { + type Err = RangeParseError; + + fn from_str(s: &str) -> Result { + let Some((start, end)) = s.split_once($range_operator_str) else { + return Err(RangeParseError); + }; + if start.is_empty() + || start.bytes().any(|b| !b.is_ascii_digit()) + || end.is_empty() + || end.bytes().any(|b| !b.is_ascii_digit()) + { + return Err(RangeParseError); + } + let start = start.parse().map_err(|_| RangeParseError)?; + let end = end.parse().map_err(|_| RangeParseError)?; + let retval = Self { start, end }; + if retval.is_empty() { + Err(RangeParseError) + } else { + Ok(retval) + } + } + } + + impl fmt::Display for $SerdeRange { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let Self { start, end } = *self; + write!( + f, + "{}{}{}", + Start::as_usize(start), + $range_operator_str, + End::as_usize(end), + ) + } + } + + impl Serialize for $SerdeRange { + fn serialize(&self, serializer: S) -> Result { + serializer.collect_str(self) + } + } + + impl<'de, Start: Size, End: Size> Deserialize<'de> for $SerdeRange { + fn deserialize>(deserializer: D) -> Result { + struct SerdeRangeVisitor(PhantomData<(Start, End)>); + impl<'de, Start: Size, End: Size> Visitor<'de> for SerdeRangeVisitor { + type Value = $SerdeRange; + + fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("a string with format \"")?; + if let Some(start) = Start::KNOWN_VALUE { + write!(f, "{start}")?; + } else { + f.write_str("")?; + }; + f.write_str($range_operator_str)?; + if let Some(end) = End::KNOWN_VALUE { + write!(f, "{end}")?; + } else { + f.write_str("")?; + }; + f.write_str("\" that is a non-empty range") + } + + fn visit_str(self, v: &str) -> Result { + let $SerdeRange:: { start, end } = + v.parse().map_err(|_| { + Error::invalid_value(serde::de::Unexpected::Str(v), &self) + })?; + let start = + Start::SizeType::deserialize(UsizeDeserializer::::new(start))?; + let end = End::SizeType::deserialize(UsizeDeserializer::::new(end))?; + Ok($SerdeRange { start, end }) + } + + fn visit_bytes(self, v: &[u8]) -> Result { + match std::str::from_utf8(v) { + Ok(v) => self.visit_str(v), + Err(_) => { + Err(Error::invalid_value(serde::de::Unexpected::Bytes(v), &self)) + } + } + } + } + deserializer.deserialize_str(SerdeRangeVisitor(PhantomData)) + } + } + + #[derive(Copy, Clone, PartialEq, Eq, Hash)] + pub struct $UIntInRangeType { + value: UInt, + range: PhantomConst<$SerdeRange>, + } + + impl $UIntInRangeType { + fn from_phantom_const_range(range: PhantomConst<$SerdeRange>) -> Self { + let $SerdeRange { start, end } = *range.get(); + let $uint_range_usize_start = Start::as_usize(start); + let $uint_range_usize_end = End::as_usize(end); + Self { + value: $uint_range_usize, + range, + } + } + pub fn new(start: Start::SizeType, end: End::SizeType) -> Self { + Self::from_phantom_const_range(PhantomConst::new( + $SerdeRange { start, end }.intern_sized(), + )) + } + } + + impl fmt::Debug for $UIntInRangeType { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let Self { value, range } = self; + let $SerdeRange { start, end } = *range.get(); + f.debug_struct(&format!( + "{}<{}, {}>", + stringify!($UIntInRange), + Start::as_usize(start), + End::as_usize(end), + )) + .field("value", value) + .finish_non_exhaustive() + } + } + + impl Type for $UIntInRangeType { + type BaseType = Bundle; + type MaskType = UIntInRangeMaskType; + type SimValue = usize; + impl_match_variant_as_self!(); + + fn mask_type(&self) -> Self::MaskType { + UIntInRangeMaskType::TYPE + } + + fn canonical(&self) -> CanonicalType { + CanonicalType::Bundle(Bundle::new(self.fields())) + } + + fn from_canonical(canonical_type: CanonicalType) -> Self { + let fields = Bundle::from_canonical(canonical_type).fields(); + let [BundleField { + name: value_name, + flipped: false, + ty: value, + }, BundleField { + name: range_name, + flipped: false, + ty: range, + }] = *fields + else { + panic!("expected {}", stringify!($UIntInRange)); + }; + assert_eq!([&*value_name, &*range_name], UINT_IN_RANGE_TYPE_FIELD_NAMES); + let value = UInt::from_canonical(value); + let range = PhantomConst::<$SerdeRange>::from_canonical(range); + let retval = Self::from_phantom_const_range(range); + assert_eq!(retval, Self { value, range }); + retval + } + + fn source_location() -> SourceLocation { + SourceLocation::builtin() + } + + fn sim_value_from_bits(&self, bits: &BitSlice) -> Self::SimValue { + let mut retval = 0usize; + retval.view_bits_mut::()[..bits.len()].clone_from_bitslice(bits); + retval + } + + fn sim_value_clone_from_bits(&self, value: &mut Self::SimValue, bits: &BitSlice) { + *value = self.sim_value_from_bits(bits); + } + + fn sim_value_to_bits(&self, value: &Self::SimValue, bits: &mut BitSlice) { + bits.clone_from_bitslice(&value.view_bits::()[..bits.len()]); + } + } + + impl BundleType for $UIntInRangeType { + type Builder = NoBuilder; + type FilledBuilder = Expr; + + fn fields(&self) -> Interned<[BundleField]> { + let [value_name, range_name] = UINT_IN_RANGE_TYPE_FIELD_NAMES; + let Self { value, range } = self; + [ + BundleField { + name: value_name.intern(), + flipped: false, + ty: value.canonical(), + }, + BundleField { + name: range_name.intern(), + flipped: false, + ty: range.canonical(), + }, + ][..] + .intern() + } + } + + impl Default for $UIntInRangeType { + fn default() -> Self { + Self::TYPE + } + } + + impl StaticType for $UIntInRangeType { + const TYPE: Self = { + let $uint_range_usize_start = Start::VALUE; + let $uint_range_usize_end = End::VALUE; + Self { + value: $uint_range_usize, + range: PhantomConst::<$SerdeRange>::TYPE, + } + }; + const MASK_TYPE: Self::MaskType = UIntInRangeMaskType::TYPE; + const TYPE_PROPERTIES: TypeProperties = BundleTypePropertiesBuilder::new() + .field(false, Self::TYPE.value.type_properties_dyn()) + .field( + false, + PhantomConst::<$SerdeRange>::TYPE_PROPERTIES, + ) + .finish(); + const MASK_TYPE_PROPERTIES: TypeProperties = UIntInRangeMaskType::TYPE_PROPERTIES; + } + + impl ToSimValueWithType<$UIntInRangeType> for usize { + fn to_sim_value_with_type( + &self, + ty: $UIntInRangeType, + ) -> SimValue<$UIntInRangeType> { + SimValue::from_value(ty, *self) + } + } + + #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, Default)] + pub struct $UIntInRangeTypeWithoutGenerics; + + #[allow(non_upper_case_globals)] + pub const $UIntInRangeType: $UIntInRangeTypeWithoutGenerics = + $UIntInRangeTypeWithoutGenerics; + + impl Index for $UIntInRangeTypeWithoutGenerics { + type Output = $UIntInRangeTypeWithStart; + + fn index(&self, start: StartSize) -> &Self::Output { + Interned::into_inner($UIntInRangeTypeWithStart(start).intern_sized()) + } + } + + #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] + pub struct $UIntInRangeTypeWithStart(Start::SizeType); + + impl, End: Size> + Index for $UIntInRangeTypeWithStart + { + type Output = $UIntInRangeType; + + fn index(&self, end: EndSize) -> &Self::Output { + Interned::into_inner($UIntInRangeType::new(self.0, end).intern_sized()) + } + } + + impl ExprCastTo for $UIntInRangeType { + fn cast_to(src: Expr, to_type: UInt) -> Expr { + src.cast_to_bits().cast_to(to_type) + } + } + + impl ExprCastTo<$UIntInRangeType> for UInt { + fn cast_to( + src: Expr, + to_type: $UIntInRangeType, + ) -> Expr<$UIntInRangeType> { + src.cast_bits_to(to_type) + } + } + + impl + ExprPartialEq<$UIntInRangeType> + for $UIntInRangeType + { + fn cmp_eq( + lhs: Expr, + rhs: Expr<$UIntInRangeType>, + ) -> Expr { + lhs.cast_to_bits().cmp_eq(rhs.cast_to_bits()) + } + fn cmp_ne( + lhs: Expr, + rhs: Expr<$UIntInRangeType>, + ) -> Expr { + lhs.cast_to_bits().cmp_ne(rhs.cast_to_bits()) + } + } + + impl + ExprPartialOrd<$UIntInRangeType> + for $UIntInRangeType + { + fn cmp_lt( + lhs: Expr, + rhs: Expr<$UIntInRangeType>, + ) -> Expr { + lhs.cast_to_bits().cmp_lt(rhs.cast_to_bits()) + } + fn cmp_le( + lhs: Expr, + rhs: Expr<$UIntInRangeType>, + ) -> Expr { + lhs.cast_to_bits().cmp_le(rhs.cast_to_bits()) + } + fn cmp_gt( + lhs: Expr, + rhs: Expr<$UIntInRangeType>, + ) -> Expr { + lhs.cast_to_bits().cmp_gt(rhs.cast_to_bits()) + } + fn cmp_ge( + lhs: Expr, + rhs: Expr<$UIntInRangeType>, + ) -> Expr { + lhs.cast_to_bits().cmp_ge(rhs.cast_to_bits()) + } + } + + impl + SimValuePartialEq<$UIntInRangeType> + for $UIntInRangeType + { + fn sim_value_eq( + this: &SimValue, + other: &SimValue<$UIntInRangeType>, + ) -> bool { + **this == **other + } + } + + impl ExprPartialEq> + for $UIntInRangeType + { + fn cmp_eq(lhs: Expr, rhs: Expr>) -> Expr { + lhs.cast_to_bits().cmp_eq(rhs) + } + fn cmp_ne(lhs: Expr, rhs: Expr>) -> Expr { + lhs.cast_to_bits().cmp_ne(rhs) + } + } + + impl ExprPartialEq<$UIntInRangeType> + for UIntType + { + fn cmp_eq(lhs: Expr, rhs: Expr<$UIntInRangeType>) -> Expr { + lhs.cmp_eq(rhs.cast_to_bits()) + } + fn cmp_ne(lhs: Expr, rhs: Expr<$UIntInRangeType>) -> Expr { + lhs.cmp_ne(rhs.cast_to_bits()) + } + } + + impl ExprPartialOrd> + for $UIntInRangeType + { + fn cmp_lt(lhs: Expr, rhs: Expr>) -> Expr { + lhs.cast_to_bits().cmp_lt(rhs) + } + fn cmp_le(lhs: Expr, rhs: Expr>) -> Expr { + lhs.cast_to_bits().cmp_le(rhs) + } + fn cmp_gt(lhs: Expr, rhs: Expr>) -> Expr { + lhs.cast_to_bits().cmp_gt(rhs) + } + fn cmp_ge(lhs: Expr, rhs: Expr>) -> Expr { + lhs.cast_to_bits().cmp_ge(rhs) + } + } + + impl ExprPartialOrd<$UIntInRangeType> + for UIntType + { + fn cmp_lt(lhs: Expr, rhs: Expr<$UIntInRangeType>) -> Expr { + lhs.cmp_lt(rhs.cast_to_bits()) + } + fn cmp_le(lhs: Expr, rhs: Expr<$UIntInRangeType>) -> Expr { + lhs.cmp_le(rhs.cast_to_bits()) + } + fn cmp_gt(lhs: Expr, rhs: Expr<$UIntInRangeType>) -> Expr { + lhs.cmp_gt(rhs.cast_to_bits()) + } + fn cmp_ge(lhs: Expr, rhs: Expr<$UIntInRangeType>) -> Expr { + lhs.cmp_ge(rhs.cast_to_bits()) + } + } + }; +} + +define_uint_in_range_type! { + UIntInRange, + UIntInRangeType, + UIntInRangeTypeWithoutGenerics, + UIntInRangeTypeWithStart, + SerdeRange, + "..", + |start, end| UInt::range_usize(start..end), +} + +define_uint_in_range_type! { + UIntInRangeInclusive, + UIntInRangeInclusiveType, + UIntInRangeInclusiveTypeWithoutGenerics, + UIntInRangeInclusiveTypeWithStart, + SerdeRangeInclusive, + "..=", + |start, end| UInt::range_inclusive_usize(start..=end), +} + +impl SerdeRange { + fn is_empty(self) -> bool { + self.start >= self.end + } +} + +impl SerdeRangeInclusive { + fn is_empty(self) -> bool { + self.start > self.end + } +} diff --git a/crates/fayalite/src/phantom_const.rs b/crates/fayalite/src/phantom_const.rs index d2e94d9..c481692 100644 --- a/crates/fayalite/src/phantom_const.rs +++ b/crates/fayalite/src/phantom_const.rs @@ -7,7 +7,7 @@ use crate::{ Expr, ToExpr, }, int::Bool, - intern::{Intern, Interned, InternedCompare, LazyInterned, Memoize}, + intern::{Intern, Interned, InternedCompare, LazyInterned, LazyInternedTrait, Memoize}, sim::value::{SimValue, SimValuePartialEq, ToSimValue, ToSimValueWithType}, source_location::SourceLocation, ty::{ @@ -228,6 +228,11 @@ impl PhantomConst { value: LazyInterned::Interned(value), } } + pub const fn new_lazy(v: &'static dyn LazyInternedTrait) -> Self { + Self { + value: LazyInterned::new_lazy(v), + } + } pub fn get(self) -> Interned { self.value.interned() } diff --git a/crates/fayalite/tests/module.rs b/crates/fayalite/tests/module.rs index 49b226a..c2dc24e 100644 --- a/crates/fayalite/tests/module.rs +++ b/crates/fayalite/tests/module.rs @@ -1,8 +1,13 @@ // SPDX-License-Identifier: LGPL-3.0-or-later // See Notices.txt for copyright information use fayalite::{ - assert_export_firrtl, firrtl::ExportOptions, intern::Intern, - module::transform::simplify_enums::SimplifyEnumsKind, prelude::*, reset::ResetType, + assert_export_firrtl, + firrtl::ExportOptions, + int::{UIntInRange, UIntInRangeInclusive}, + intern::Intern, + module::transform::simplify_enums::SimplifyEnumsKind, + prelude::*, + reset::ResetType, ty::StaticType, }; use serde_json::json; @@ -4547,3 +4552,82 @@ circuit check_struct_cmp_eq: ", }; } + +#[hdl_module(outline_generated)] +pub fn check_uint_in_range() { + #[hdl] + let i_0_to_1: UIntInRange<0, 1> = m.input(); + #[hdl] + let i_0_to_2: UIntInRange<0, 2> = m.input(); + #[hdl] + let i_0_to_3: UIntInRange<0, 3> = m.input(); + #[hdl] + let i_0_to_4: UIntInRange<0, 4> = m.input(); + #[hdl] + let i_0_to_7: UIntInRange<0, 7> = m.input(); + #[hdl] + let i_0_to_8: UIntInRange<0, 8> = m.input(); + #[hdl] + let i_0_to_9: UIntInRange<0, 9> = m.input(); + #[hdl] + let i_0_through_0: UIntInRangeInclusive<0, 0> = m.input(); + #[hdl] + let i_0_through_1: UIntInRangeInclusive<0, 1> = m.input(); + #[hdl] + let i_0_through_2: UIntInRangeInclusive<0, 2> = m.input(); + #[hdl] + let i_0_through_3: UIntInRangeInclusive<0, 3> = m.input(); + #[hdl] + let i_0_through_4: UIntInRangeInclusive<0, 4> = m.input(); + #[hdl] + let i_0_through_7: UIntInRangeInclusive<0, 7> = m.input(); + #[hdl] + let i_0_through_8: UIntInRangeInclusive<0, 8> = m.input(); + #[hdl] + let i_0_through_9: UIntInRangeInclusive<0, 9> = m.input(); +} + +#[test] +fn test_uint_in_range() { + let _n = SourceLocation::normalize_files_for_tests(); + let m = check_uint_in_range(); + dbg!(m); + #[rustfmt::skip] // work around https://github.com/rust-lang/rustfmt/issues/6161 + assert_export_firrtl! { + m => + "/test/check_uint_in_range.fir": r"FIRRTL version 3.2.0 +circuit check_uint_in_range: + type Ty0 = {value: UInt<0>, range: {}} + type Ty1 = {value: UInt<1>, range: {}} + type Ty2 = {value: UInt<2>, range: {}} + type Ty3 = {value: UInt<2>, range: {}} + type Ty4 = {value: UInt<3>, range: {}} + type Ty5 = {value: UInt<3>, range: {}} + type Ty6 = {value: UInt<4>, range: {}} + type Ty7 = {value: UInt<0>, range: {}} + type Ty8 = {value: UInt<1>, range: {}} + type Ty9 = {value: UInt<2>, range: {}} + type Ty10 = {value: UInt<2>, range: {}} + type Ty11 = {value: UInt<3>, range: {}} + type Ty12 = {value: UInt<3>, range: {}} + type Ty13 = {value: UInt<4>, range: {}} + type Ty14 = {value: UInt<4>, range: {}} + module check_uint_in_range: @[module-XXXXXXXXXX.rs 1:1] + input i_0_to_1: Ty0 @[module-XXXXXXXXXX.rs 2:1] + input i_0_to_2: Ty1 @[module-XXXXXXXXXX.rs 3:1] + input i_0_to_3: Ty2 @[module-XXXXXXXXXX.rs 4:1] + input i_0_to_4: Ty3 @[module-XXXXXXXXXX.rs 5:1] + input i_0_to_7: Ty4 @[module-XXXXXXXXXX.rs 6:1] + input i_0_to_8: Ty5 @[module-XXXXXXXXXX.rs 7:1] + input i_0_to_9: Ty6 @[module-XXXXXXXXXX.rs 8:1] + input i_0_through_0: Ty7 @[module-XXXXXXXXXX.rs 9:1] + input i_0_through_1: Ty8 @[module-XXXXXXXXXX.rs 10:1] + input i_0_through_2: Ty9 @[module-XXXXXXXXXX.rs 11:1] + input i_0_through_3: Ty10 @[module-XXXXXXXXXX.rs 12:1] + input i_0_through_4: Ty11 @[module-XXXXXXXXXX.rs 13:1] + input i_0_through_7: Ty12 @[module-XXXXXXXXXX.rs 14:1] + input i_0_through_8: Ty13 @[module-XXXXXXXXXX.rs 15:1] + input i_0_through_9: Ty14 @[module-XXXXXXXXXX.rs 16:1] +", + }; +} From 5967e812a2a887f44fb49443f06e1fac5cf4e916 Mon Sep 17 00:00:00 2001 From: Jacob Lifshay Date: Tue, 8 Apr 2025 21:52:47 -0700 Subject: [PATCH 26/38] fix [SU]IntValue's PartialEq for interning different widths must make values compare not equal otherwise interning will e.g. substitute a 0x0_u8 for a 0x0_u2 --- crates/fayalite/src/int.rs | 31 +++++++++++++++---------------- crates/fayalite/src/sim/value.rs | 8 ++++---- 2 files changed, 19 insertions(+), 20 deletions(-) diff --git a/crates/fayalite/src/int.rs b/crates/fayalite/src/int.rs index 053bd1d..d8364b1 100644 --- a/crates/fayalite/src/int.rs +++ b/crates/fayalite/src/int.rs @@ -765,7 +765,7 @@ macro_rules! impl_int { } } - #[derive(Clone, Eq, Hash)] + #[derive(Clone, PartialEq, Eq, Hash)] pub struct $value { bits: Arc, _phantom: PhantomData, @@ -811,24 +811,15 @@ macro_rules! impl_int { } } - impl PartialEq<$value> for $value { - fn eq(&self, other: &$value) -> bool { - self.to_bigint() == other.to_bigint() - } - } - - impl PartialOrd<$value> for $value { - fn partial_cmp(&self, other: &$value) -> Option { + impl PartialOrd for $value { + fn partial_cmp(&self, other: &Self) -> Option { + if self.width() != other.width() { + return None; + } Some(self.to_bigint().cmp(&other.to_bigint())) } } - impl Ord for $value { - fn cmp(&self, other: &Self) -> std::cmp::Ordering { - self.to_bigint().cmp(&other.to_bigint()) - } - } - impl From<$value> for BigInt { fn from(v: $value) -> BigInt { v.to_bigint() @@ -1069,7 +1060,8 @@ pub trait BoolOrIntType: Type + sealed::BoolOrIntTypeSealed { type Width: Size; type Signed: GenericConstBool; type Value: Clone - + Ord + + PartialOrd + + Eq + std::hash::Hash + fmt::Debug + fmt::Display @@ -1300,6 +1292,13 @@ impl ToLiteralBits for bool { mod tests { use super::*; + #[test] + fn test_different_value_widths_compare_ne() { + // interning relies on [SU]IntValue with different `width` comparing not equal + assert_ne!(UInt[3].from_int_wrapping(0), UInt[4].from_int_wrapping(0)); + assert_ne!(SInt[3].from_int_wrapping(0), SInt[4].from_int_wrapping(0)); + } + #[test] fn test_uint_for_value() { assert_eq!(UInt::for_value(0u8).width, 0); diff --git a/crates/fayalite/src/sim/value.rs b/crates/fayalite/src/sim/value.rs index 737043a..8dace78 100644 --- a/crates/fayalite/src/sim/value.rs +++ b/crates/fayalite/src/sim/value.rs @@ -324,14 +324,14 @@ impl, U: Type> PartialEq> for SimValue { } } -impl SimValuePartialEq> for UIntType { - fn sim_value_eq(this: &SimValue, other: &SimValue>) -> bool { +impl SimValuePartialEq for UIntType { + fn sim_value_eq(this: &SimValue, other: &SimValue) -> bool { **this == **other } } -impl SimValuePartialEq> for SIntType { - fn sim_value_eq(this: &SimValue, other: &SimValue>) -> bool { +impl SimValuePartialEq for SIntType { + fn sim_value_eq(this: &SimValue, other: &SimValue) -> bool { **this == **other } } From 9a1b047d2f9cef4c4590d1bb079ada10cc80d740 Mon Sep 17 00:00:00 2001 From: Jacob Lifshay Date: Wed, 9 Apr 2025 16:25:56 -0700 Subject: [PATCH 27/38] change TypeIdMap to not use any unsafe code --- crates/fayalite/src/intern/type_map.rs | 55 +++++++------------------- 1 file changed, 15 insertions(+), 40 deletions(-) diff --git a/crates/fayalite/src/intern/type_map.rs b/crates/fayalite/src/intern/type_map.rs index 48433af..945116b 100644 --- a/crates/fayalite/src/intern/type_map.rs +++ b/crates/fayalite/src/intern/type_map.rs @@ -1,10 +1,8 @@ // SPDX-License-Identifier: LGPL-3.0-or-later // See Notices.txt for copyright information -use hashbrown::HashMap; use std::{ any::{Any, TypeId}, hash::{BuildHasher, Hasher}, - ptr::NonNull, sync::RwLock, }; @@ -75,59 +73,36 @@ impl BuildHasher for TypeIdBuildHasher { } } -struct Value(NonNull); - -impl Value { - unsafe fn get_transmute_lifetime<'b>(&self) -> &'b (dyn Any + Send + Sync) { - unsafe { &*self.0.as_ptr() } - } - fn new(v: Box) -> Self { - unsafe { Self(NonNull::new_unchecked(Box::into_raw(v))) } - } -} - -unsafe impl Send for Value {} -unsafe impl Sync for Value {} - -impl Drop for Value { - fn drop(&mut self) { - unsafe { std::ptr::drop_in_place(self.0.as_ptr()) } - } -} - -pub struct TypeIdMap(RwLock>); +pub(crate) struct TypeIdMap( + RwLock>, +); impl TypeIdMap { - pub const fn new() -> Self { - Self(RwLock::new(HashMap::with_hasher(TypeIdBuildHasher))) + pub(crate) const fn new() -> Self { + Self(RwLock::new(hashbrown::HashMap::with_hasher( + TypeIdBuildHasher, + ))) } #[cold] - unsafe fn insert_slow( + fn insert_slow( &self, type_id: TypeId, make: fn() -> Box, - ) -> &(dyn Any + Sync + Send) { - let value = Value::new(make()); + ) -> &'static (dyn Any + Sync + Send) { + let value = Box::leak(make()); let mut write_guard = self.0.write().unwrap(); - unsafe { - write_guard - .entry(type_id) - .or_insert(value) - .get_transmute_lifetime() - } + *write_guard.entry(type_id).or_insert(value) } - pub fn get_or_insert_default(&self) -> &T { + pub(crate) fn get_or_insert_default(&self) -> &T { let type_id = TypeId::of::(); let read_guard = self.0.read().unwrap(); - let retval = read_guard - .get(&type_id) - .map(|v| unsafe { Value::get_transmute_lifetime(v) }); + let retval = read_guard.get(&type_id).map(|v| *v); drop(read_guard); let retval = match retval { Some(retval) => retval, - None => unsafe { self.insert_slow(type_id, move || Box::new(T::default())) }, + None => self.insert_slow(type_id, move || Box::new(T::default())), }; - unsafe { &*(retval as *const dyn Any as *const T) } + retval.downcast_ref().expect("known to have correct TypeId") } } From 36f1b9bbb60031ee9770ba3181551b7c1117d5ea Mon Sep 17 00:00:00 2001 From: Jacob Lifshay Date: Wed, 9 Apr 2025 19:19:25 -0700 Subject: [PATCH 28/38] add derive(Debug) to all types that are interned --- crates/fayalite/src/annotations.rs | 2 +- crates/fayalite/src/memory.rs | 2 +- crates/fayalite/src/sim.rs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/fayalite/src/annotations.rs b/crates/fayalite/src/annotations.rs index 8eff4a0..1c517ae 100644 --- a/crates/fayalite/src/annotations.rs +++ b/crates/fayalite/src/annotations.rs @@ -12,7 +12,7 @@ use std::{ ops::Deref, }; -#[derive(Clone)] +#[derive(Clone, Debug)] struct CustomFirrtlAnnotationFieldsImpl { value: serde_json::Map, serialized: Interned, diff --git a/crates/fayalite/src/memory.rs b/crates/fayalite/src/memory.rs index 1101157..622ffc6 100644 --- a/crates/fayalite/src/memory.rs +++ b/crates/fayalite/src/memory.rs @@ -470,7 +470,7 @@ pub enum ReadUnderWrite { Undefined, } -#[derive(Copy, Clone, PartialEq, Eq, Hash)] +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] struct MemImpl { scoped_name: ScopedNameId, source_location: SourceLocation, diff --git a/crates/fayalite/src/sim.rs b/crates/fayalite/src/sim.rs index 563cd37..da7c293 100644 --- a/crates/fayalite/src/sim.rs +++ b/crates/fayalite/src/sim.rs @@ -5783,7 +5783,7 @@ where } } -#[derive(Clone, PartialEq, Eq, Hash)] +#[derive(Clone, PartialEq, Eq, Hash, Debug)] struct SimTrace { kind: K, state: S, From 07725ab489346d18e8928acf9685301adb8d778f Mon Sep 17 00:00:00 2001 From: Jacob Lifshay Date: Wed, 9 Apr 2025 19:30:02 -0700 Subject: [PATCH 29/38] switch interning to use HashTable rather than HashMap --- crates/fayalite/src/intern.rs | 39 +++++++++++++++++++++-------------- 1 file changed, 23 insertions(+), 16 deletions(-) diff --git a/crates/fayalite/src/intern.rs b/crates/fayalite/src/intern.rs index 3780ad3..a8f7fc0 100644 --- a/crates/fayalite/src/intern.rs +++ b/crates/fayalite/src/intern.rs @@ -3,7 +3,7 @@ #![allow(clippy::type_complexity)] use crate::intern::type_map::TypeIdMap; use bitvec::{ptr::BitPtr, slice::BitSlice, vec::BitVec}; -use hashbrown::{hash_map::RawEntryMut, HashMap, HashTable}; +use hashbrown::{HashTable, hash_map::DefaultHashBuilder as DefaultBuildHasher}; use serde::{Deserialize, Serialize}; use std::{ any::{Any, TypeId}, @@ -17,7 +17,7 @@ use std::{ sync::{Mutex, RwLock}, }; -pub mod type_map; +mod type_map; pub trait LazyInternedTrait: Send + Sync + Any { fn get(&self) -> Interned; @@ -316,8 +316,13 @@ pub trait Intern: Any + Send + Sync { } } +struct InternerState { + table: HashTable<&'static T>, + hasher: DefaultBuildHasher, +} + pub struct Interner { - map: Mutex>, + state: Mutex>, } impl Interner { @@ -330,7 +335,10 @@ impl Interner { impl Default for Interner { fn default() -> Self { Self { - map: Default::default(), + state: Mutex::new(InternerState { + table: HashTable::new(), + hasher: Default::default(), + }), } } } @@ -341,17 +349,16 @@ impl Interner { alloc: F, value: Cow<'_, T>, ) -> Interned { - let mut map = self.map.lock().unwrap(); - let hasher = map.hasher().clone(); - let hash = hasher.hash_one(&*value); - let inner = match map.raw_entry_mut().from_hash(hash, |k| **k == *value) { - RawEntryMut::Occupied(entry) => *entry.key(), - RawEntryMut::Vacant(entry) => { - *entry - .insert_with_hasher(hash, alloc(value), (), |k| hasher.hash_one(&**k)) - .0 - } - }; + let mut state = self.state.lock().unwrap(); + let InternerState { table, hasher } = &mut *state; + let inner = *table + .entry( + hasher.hash_one(&*value), + |k| **k == *value, + |k| hasher.hash_one(&**k), + ) + .or_insert_with(|| alloc(value)) + .get(); Interned { inner } } } @@ -742,7 +749,7 @@ pub trait MemoizeGeneric: 'static + Send + Sync + Hash + Eq + Copy { fn get_cow(self, input: Self::InputCow<'_>) -> Self::Output { static TYPE_ID_MAP: TypeIdMap = TypeIdMap::new(); let map: &RwLock<( - hashbrown::hash_map::DefaultHashBuilder, + DefaultBuildHasher, HashTable<(Self, Self::InputOwned, Self::Output)>, )> = TYPE_ID_MAP.get_or_insert_default(); fn hash_eq_key<'a, 'b, T: MemoizeGeneric>( From e0c9939147f04e62b4e117c87a2c03726880366b Mon Sep 17 00:00:00 2001 From: Jacob Lifshay Date: Wed, 9 Apr 2025 19:55:09 -0700 Subject: [PATCH 30/38] add test that SimValue can't be interned, since its PartialEq may ignore types --- .../tests/ui/simvalue_is_not_internable.rs | 15 ++ .../ui/simvalue_is_not_internable.stderr | 178 ++++++++++++++++++ 2 files changed, 193 insertions(+) create mode 100644 crates/fayalite/tests/ui/simvalue_is_not_internable.rs create mode 100644 crates/fayalite/tests/ui/simvalue_is_not_internable.stderr diff --git a/crates/fayalite/tests/ui/simvalue_is_not_internable.rs b/crates/fayalite/tests/ui/simvalue_is_not_internable.rs new file mode 100644 index 0000000..d40990f --- /dev/null +++ b/crates/fayalite/tests/ui/simvalue_is_not_internable.rs @@ -0,0 +1,15 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// See Notices.txt for copyright information + +//! check that SimValue can't be interned, since equality may ignore types + +use fayalite::{ + intern::{Intern, Interned}, + sim::value::SimValue, +}; + +fn f(v: SimValue<()>) -> Interned> { + Intern::intern_sized(v) +} + +fn main() {} diff --git a/crates/fayalite/tests/ui/simvalue_is_not_internable.stderr b/crates/fayalite/tests/ui/simvalue_is_not_internable.stderr new file mode 100644 index 0000000..eb8877b --- /dev/null +++ b/crates/fayalite/tests/ui/simvalue_is_not_internable.stderr @@ -0,0 +1,178 @@ +error[E0277]: `Cell` cannot be shared between threads safely + --> tests/ui/simvalue_is_not_internable.rs:11:26 + | +11 | fn f(v: SimValue<()>) -> Interned> { + | ^^^^^^^^^^^^^^^^^^^^^^ `Cell` cannot be shared between threads safely + | + = help: within `SimValue<()>`, the trait `Sync` is not implemented for `Cell`, which is required by `SimValue<()>: Sync` + = note: if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` +note: required because it appears within the type `util::alternating_cell::AlternatingCell>` + --> src/util/alternating_cell.rs + | + | pub(crate) struct AlternatingCell { + | ^^^^^^^^^^^^^^^ +note: required because it appears within the type `SimValue<()>` + --> src/sim/value.rs + | + | pub struct SimValue { + | ^^^^^^^^ +note: required by a bound in `fayalite::intern::Interned` + --> src/intern.rs + | + | pub struct Interned { + | ^^^^ required by this bound in `Interned` + +error[E0277]: `UnsafeCell>` cannot be shared between threads safely + --> tests/ui/simvalue_is_not_internable.rs:11:26 + | +11 | fn f(v: SimValue<()>) -> Interned> { + | ^^^^^^^^^^^^^^^^^^^^^^ `UnsafeCell>` cannot be shared between threads safely + | + = help: within `SimValue<()>`, the trait `Sync` is not implemented for `UnsafeCell>`, which is required by `SimValue<()>: Sync` +note: required because it appears within the type `util::alternating_cell::AlternatingCell>` + --> src/util/alternating_cell.rs + | + | pub(crate) struct AlternatingCell { + | ^^^^^^^^^^^^^^^ +note: required because it appears within the type `SimValue<()>` + --> src/sim/value.rs + | + | pub struct SimValue { + | ^^^^^^^^ +note: required by a bound in `fayalite::intern::Interned` + --> src/intern.rs + | + | pub struct Interned { + | ^^^^ required by this bound in `Interned` + +error[E0277]: the trait bound `SimValue<()>: Intern` is not satisfied + --> tests/ui/simvalue_is_not_internable.rs:12:26 + | +12 | Intern::intern_sized(v) + | -------------------- ^ the trait `Hash` is not implemented for `SimValue<()>`, which is required by `SimValue<()>: Intern` + | | + | required by a bound introduced by this call + | + = help: the following other types implement trait `Intern`: + BitSlice + [T] + str + = note: required for `SimValue<()>` to implement `Intern` + +error[E0277]: the trait bound `SimValue<()>: Intern` is not satisfied + --> tests/ui/simvalue_is_not_internable.rs:12:26 + | +12 | Intern::intern_sized(v) + | -------------------- ^ the trait `std::cmp::Eq` is not implemented for `SimValue<()>`, which is required by `SimValue<()>: Intern` + | | + | required by a bound introduced by this call + | + = help: the following other types implement trait `Intern`: + BitSlice + [T] + str + = note: required for `SimValue<()>` to implement `Intern` + +error[E0277]: `Cell` cannot be shared between threads safely + --> tests/ui/simvalue_is_not_internable.rs:12:26 + | +12 | Intern::intern_sized(v) + | -------------------- ^ `Cell` cannot be shared between threads safely + | | + | required by a bound introduced by this call + | + = help: within `SimValue<()>`, the trait `Sync` is not implemented for `Cell`, which is required by `SimValue<()>: Sync` + = note: if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` +note: required because it appears within the type `util::alternating_cell::AlternatingCell>` + --> src/util/alternating_cell.rs + | + | pub(crate) struct AlternatingCell { + | ^^^^^^^^^^^^^^^ +note: required because it appears within the type `SimValue<()>` + --> src/sim/value.rs + | + | pub struct SimValue { + | ^^^^^^^^ +note: required by a bound in `intern_sized` + --> src/intern.rs + | + | pub trait Intern: Any + Send + Sync { + | ^^^^ required by this bound in `Intern::intern_sized` + | fn intern(&self) -> Interned; + | fn intern_sized(self) -> Interned + | ------------ required by a bound in this associated function + +error[E0277]: `UnsafeCell>` cannot be shared between threads safely + --> tests/ui/simvalue_is_not_internable.rs:12:26 + | +12 | Intern::intern_sized(v) + | -------------------- ^ `UnsafeCell>` cannot be shared between threads safely + | | + | required by a bound introduced by this call + | + = help: within `SimValue<()>`, the trait `Sync` is not implemented for `UnsafeCell>`, which is required by `SimValue<()>: Sync` +note: required because it appears within the type `util::alternating_cell::AlternatingCell>` + --> src/util/alternating_cell.rs + | + | pub(crate) struct AlternatingCell { + | ^^^^^^^^^^^^^^^ +note: required because it appears within the type `SimValue<()>` + --> src/sim/value.rs + | + | pub struct SimValue { + | ^^^^^^^^ +note: required by a bound in `intern_sized` + --> src/intern.rs + | + | pub trait Intern: Any + Send + Sync { + | ^^^^ required by this bound in `Intern::intern_sized` + | fn intern(&self) -> Interned; + | fn intern_sized(self) -> Interned + | ------------ required by a bound in this associated function + +error[E0277]: `Cell` cannot be shared between threads safely + --> tests/ui/simvalue_is_not_internable.rs:12:5 + | +12 | Intern::intern_sized(v) + | ^^^^^^^^^^^^^^^^^^^^^^^ `Cell` cannot be shared between threads safely + | + = help: within `SimValue<()>`, the trait `Sync` is not implemented for `Cell`, which is required by `SimValue<()>: Sync` + = note: if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` +note: required because it appears within the type `util::alternating_cell::AlternatingCell>` + --> src/util/alternating_cell.rs + | + | pub(crate) struct AlternatingCell { + | ^^^^^^^^^^^^^^^ +note: required because it appears within the type `SimValue<()>` + --> src/sim/value.rs + | + | pub struct SimValue { + | ^^^^^^^^ +note: required by a bound in `fayalite::intern::Interned` + --> src/intern.rs + | + | pub struct Interned { + | ^^^^ required by this bound in `Interned` + +error[E0277]: `UnsafeCell>` cannot be shared between threads safely + --> tests/ui/simvalue_is_not_internable.rs:12:5 + | +12 | Intern::intern_sized(v) + | ^^^^^^^^^^^^^^^^^^^^^^^ `UnsafeCell>` cannot be shared between threads safely + | + = help: within `SimValue<()>`, the trait `Sync` is not implemented for `UnsafeCell>`, which is required by `SimValue<()>: Sync` +note: required because it appears within the type `util::alternating_cell::AlternatingCell>` + --> src/util/alternating_cell.rs + | + | pub(crate) struct AlternatingCell { + | ^^^^^^^^^^^^^^^ +note: required because it appears within the type `SimValue<()>` + --> src/sim/value.rs + | + | pub struct SimValue { + | ^^^^^^^^ +note: required by a bound in `fayalite::intern::Interned` + --> src/intern.rs + | + | pub struct Interned { + | ^^^^ required by this bound in `Interned` From b08a747e20494d79287e0886dc0637ebaeac37d9 Mon Sep 17 00:00:00 2001 From: Jacob Lifshay Date: Wed, 9 Apr 2025 20:03:14 -0700 Subject: [PATCH 31/38] switch to using type aliases for HashMap/HashSet to allow easily switching hashers --- crates/fayalite/src/bundle.rs | 4 +-- crates/fayalite/src/enum_.rs | 5 +-- crates/fayalite/src/firrtl.rs | 5 ++- crates/fayalite/src/intern.rs | 4 +-- crates/fayalite/src/module.rs | 14 ++++---- .../src/module/transform/deduce_resets.rs | 9 ++--- .../src/module/transform/simplify_enums.rs | 6 ++-- .../src/module/transform/simplify_memories.rs | 5 ++- crates/fayalite/src/sim.rs | 34 +++++++++---------- crates/fayalite/src/sim/interpreter.rs | 3 +- crates/fayalite/src/sim/vcd.rs | 3 +- crates/fayalite/src/source_location.rs | 7 ++-- crates/fayalite/src/testing.rs | 4 +-- crates/fayalite/src/util.rs | 6 ++++ 14 files changed, 57 insertions(+), 52 deletions(-) diff --git a/crates/fayalite/src/bundle.rs b/crates/fayalite/src/bundle.rs index 0fd89f1..240c0c6 100644 --- a/crates/fayalite/src/bundle.rs +++ b/crates/fayalite/src/bundle.rs @@ -14,9 +14,9 @@ use crate::{ impl_match_variant_as_self, CanonicalType, MatchVariantWithoutScope, OpaqueSimValue, StaticType, Type, TypeProperties, TypeWithDeref, }, + util::HashMap, }; use bitvec::{slice::BitSlice, vec::BitVec}; -use hashbrown::HashMap; use serde::{Deserialize, Serialize}; use std::{fmt, marker::PhantomData}; @@ -160,7 +160,7 @@ impl Default for BundleTypePropertiesBuilder { impl Bundle { #[track_caller] pub fn new(fields: Interned<[BundleField]>) -> Self { - let mut name_indexes = HashMap::with_capacity(fields.len()); + let mut name_indexes = HashMap::with_capacity_and_hasher(fields.len(), Default::default()); let mut field_offsets = Vec::with_capacity(fields.len()); let mut type_props_builder = BundleTypePropertiesBuilder::new(); for (index, &BundleField { name, flipped, ty }) in fields.iter().enumerate() { diff --git a/crates/fayalite/src/enum_.rs b/crates/fayalite/src/enum_.rs index 6205855..36b5aa7 100644 --- a/crates/fayalite/src/enum_.rs +++ b/crates/fayalite/src/enum_.rs @@ -19,9 +19,9 @@ use crate::{ CanonicalType, MatchVariantAndInactiveScope, OpaqueSimValue, StaticType, Type, TypeProperties, }, + util::HashMap, }; use bitvec::{order::Lsb0, slice::BitSlice, view::BitView}; -use hashbrown::HashMap; use serde::{Deserialize, Serialize}; use std::{convert::Infallible, fmt, iter::FusedIterator, sync::Arc}; @@ -193,7 +193,8 @@ impl Default for EnumTypePropertiesBuilder { impl Enum { #[track_caller] pub fn new(variants: Interned<[EnumVariant]>) -> Self { - let mut name_indexes = HashMap::with_capacity(variants.len()); + let mut name_indexes = + HashMap::with_capacity_and_hasher(variants.len(), Default::default()); let mut type_props_builder = EnumTypePropertiesBuilder::new(); for (index, EnumVariant { name, ty }) in variants.iter().enumerate() { if let Some(old_index) = name_indexes.insert(*name, index) { diff --git a/crates/fayalite/src/firrtl.rs b/crates/fayalite/src/firrtl.rs index d082187..d33c7a9 100644 --- a/crates/fayalite/src/firrtl.rs +++ b/crates/fayalite/src/firrtl.rs @@ -36,12 +36,11 @@ use crate::{ ty::{CanonicalType, Type}, util::{ const_str_array_is_strictly_ascending, BitSliceWriteWithBase, DebugAsRawString, - GenericConstBool, + GenericConstBool, HashMap, HashSet, }, }; use bitvec::slice::BitSlice; use clap::value_parser; -use hashbrown::{HashMap, HashSet}; use num_traits::Signed; use serde::Serialize; use std::{ @@ -2622,7 +2621,7 @@ fn export_impl( indent_depth: &indent_depth, indent: " ", }, - seen_modules: HashSet::new(), + seen_modules: HashSet::default(), unwritten_modules: VecDeque::new(), global_ns, module: ModuleState::default(), diff --git a/crates/fayalite/src/intern.rs b/crates/fayalite/src/intern.rs index a8f7fc0..af91f0a 100644 --- a/crates/fayalite/src/intern.rs +++ b/crates/fayalite/src/intern.rs @@ -1,9 +1,9 @@ // SPDX-License-Identifier: LGPL-3.0-or-later // See Notices.txt for copyright information #![allow(clippy::type_complexity)] -use crate::intern::type_map::TypeIdMap; +use crate::{intern::type_map::TypeIdMap, util::DefaultBuildHasher}; use bitvec::{ptr::BitPtr, slice::BitSlice, vec::BitVec}; -use hashbrown::{HashTable, hash_map::DefaultHashBuilder as DefaultBuildHasher}; +use hashbrown::HashTable; use serde::{Deserialize, Serialize}; use std::{ any::{Any, TypeId}, diff --git a/crates/fayalite/src/module.rs b/crates/fayalite/src/module.rs index 1fcb529..920b0af 100644 --- a/crates/fayalite/src/module.rs +++ b/crates/fayalite/src/module.rs @@ -24,10 +24,10 @@ use crate::{ sim::{ExternModuleSimGenerator, ExternModuleSimulation}, source_location::SourceLocation, ty::{CanonicalType, Type}, - util::ScopedRef, + util::{HashMap, HashSet, ScopedRef}, wire::{IncompleteWire, Wire}, }; -use hashbrown::{hash_map::Entry, HashMap, HashSet}; +use hashbrown::hash_map::Entry; use num_bigint::BigInt; use std::{ cell::RefCell, @@ -1498,7 +1498,7 @@ impl TargetState { .collect(), }, CanonicalType::PhantomConst(_) => TargetStateInner::Decomposed { - subtargets: HashMap::new(), + subtargets: HashMap::default(), }, CanonicalType::Array(ty) => TargetStateInner::Decomposed { subtargets: (0..ty.len()) @@ -1864,7 +1864,7 @@ impl Module { AssertValidityState { module: self.canonical(), blocks: vec![], - target_states: HashMap::with_capacity(64), + target_states: HashMap::with_capacity_and_hasher(64, Default::default()), } .assert_validity(); } @@ -2125,8 +2125,8 @@ impl ModuleBuilder { incomplete_declarations: vec![], stmts: vec![], }], - annotations_map: HashMap::new(), - memory_map: HashMap::new(), + annotations_map: HashMap::default(), + memory_map: HashMap::default(), }, }), }; @@ -2136,7 +2136,7 @@ impl ModuleBuilder { impl_: RefCell::new(ModuleBuilderImpl { body, io: vec![], - io_indexes: HashMap::new(), + io_indexes: HashMap::default(), module_annotations: vec![], }), }; diff --git a/crates/fayalite/src/module/transform/deduce_resets.rs b/crates/fayalite/src/module/transform/deduce_resets.rs index a70dc33..5fb829e 100644 --- a/crates/fayalite/src/module/transform/deduce_resets.rs +++ b/crates/fayalite/src/module/transform/deduce_resets.rs @@ -24,8 +24,9 @@ use crate::{ }, prelude::*, reset::{ResetType, ResetTypeDispatch}, + util::{HashMap, HashSet}, }; -use hashbrown::{hash_map::Entry, HashMap, HashSet}; +use hashbrown::hash_map::Entry; use num_bigint::BigInt; use petgraph::unionfind::UnionFind; use std::{fmt, marker::PhantomData}; @@ -2251,9 +2252,9 @@ pub fn deduce_resets( fallback_to_sync_reset: bool, ) -> Result>, DeduceResetsError> { let mut state = State { - modules_added_to_graph: HashSet::new(), - substituted_modules: HashMap::new(), - expr_resets: HashMap::new(), + modules_added_to_graph: HashSet::default(), + substituted_modules: HashMap::default(), + expr_resets: HashMap::default(), reset_graph: ResetGraph::default(), fallback_to_sync_reset, }; diff --git a/crates/fayalite/src/module/transform/simplify_enums.rs b/crates/fayalite/src/module/transform/simplify_enums.rs index e8b6168..333451d 100644 --- a/crates/fayalite/src/module/transform/simplify_enums.rs +++ b/crates/fayalite/src/module/transform/simplify_enums.rs @@ -18,10 +18,10 @@ use crate::{ }, source_location::SourceLocation, ty::{CanonicalType, Type}, + util::HashMap, wire::Wire, }; use core::fmt; -use hashbrown::HashMap; #[derive(Debug)] pub enum SimplifyEnumsError { @@ -965,8 +965,8 @@ pub fn simplify_enums( kind: SimplifyEnumsKind, ) -> Result>, SimplifyEnumsError> { module.fold(&mut State { - enum_types: HashMap::new(), - replacement_mem_ports: HashMap::new(), + enum_types: HashMap::default(), + replacement_mem_ports: HashMap::default(), kind, module_state_stack: vec![], }) diff --git a/crates/fayalite/src/module/transform/simplify_memories.rs b/crates/fayalite/src/module/transform/simplify_memories.rs index 101385e..6357843 100644 --- a/crates/fayalite/src/module/transform/simplify_memories.rs +++ b/crates/fayalite/src/module/transform/simplify_memories.rs @@ -14,11 +14,10 @@ use crate::{ }, source_location::SourceLocation, ty::{CanonicalType, Type}, - util::MakeMutSlice, + util::{HashMap, MakeMutSlice}, wire::Wire, }; use bitvec::{slice::BitSlice, vec::BitVec}; -use hashbrown::HashMap; use std::{ convert::Infallible, fmt::Write, @@ -897,7 +896,7 @@ impl Folder for State { module, ModuleState { output_module: None, - memories: HashMap::new(), + memories: HashMap::default(), }, ); let mut this = PushedState::push_module(self, module); diff --git a/crates/fayalite/src/sim.rs b/crates/fayalite/src/sim.rs index da7c293..b7845f4 100644 --- a/crates/fayalite/src/sim.rs +++ b/crates/fayalite/src/sim.rs @@ -41,10 +41,9 @@ use crate::{ value::SimValue, }, ty::StaticType, - util::{BitSliceWriteWithBase, DebugAsDisplay}, + util::{BitSliceWriteWithBase, DebugAsDisplay, HashMap, HashSet}, }; use bitvec::{bits, order::Lsb0, slice::BitSlice, vec::BitVec, view::BitView}; -use hashbrown::{HashMap, HashSet}; use num_bigint::BigInt; use num_traits::{Signed, Zero}; use petgraph::{ @@ -580,8 +579,9 @@ impl Assignments { big_slots, }) = self.slot_readers(); AssignmentsElements { - node_indexes: HashMap::with_capacity( + node_indexes: HashMap::with_capacity_and_hasher( self.assignments().len() + small_slots.len() + big_slots.len(), + Default::default(), ), nodes: self.node_references(), edges: self.edge_references(), @@ -1676,18 +1676,18 @@ impl Compiler { insns: Insns::new(), original_base_module, base_module, - modules: HashMap::new(), + modules: HashMap::default(), extern_modules: Vec::new(), - compiled_values: HashMap::new(), - compiled_exprs: HashMap::new(), - compiled_exprs_to_values: HashMap::new(), - decl_conditions: HashMap::new(), - compiled_values_to_dyn_array_indexes: HashMap::new(), - compiled_value_bool_dest_is_small_map: HashMap::new(), + compiled_values: HashMap::default(), + compiled_exprs: HashMap::default(), + compiled_exprs_to_values: HashMap::default(), + decl_conditions: HashMap::default(), + compiled_values_to_dyn_array_indexes: HashMap::default(), + compiled_value_bool_dest_is_small_map: HashMap::default(), assignments: Assignments::default(), clock_triggers: Vec::new(), - compiled_value_to_clock_trigger_map: HashMap::new(), - enum_discriminants: HashMap::new(), + compiled_value_to_clock_trigger_map: HashMap::default(), + enum_discriminants: HashMap::default(), registers: Vec::new(), traces: SimTraces(Vec::new()), memories: Vec::new(), @@ -5976,8 +5976,8 @@ impl SimulationModuleState { fn new(base_targets: impl IntoIterator)>) -> Self { let mut retval = Self { base_targets: Vec::new(), - uninitialized_ios: HashMap::new(), - io_targets: HashMap::new(), + uninitialized_ios: HashMap::default(), + io_targets: HashMap::default(), did_initial_settle: false, }; for (base_target, value) in base_targets { @@ -6207,7 +6207,7 @@ impl Default for EarliestWaitTargets { Self { settle: false, instant: None, - changes: HashMap::new(), + changes: HashMap::default(), } } } @@ -6217,14 +6217,14 @@ impl EarliestWaitTargets { Self { settle: true, instant: None, - changes: HashMap::new(), + changes: HashMap::default(), } } fn instant(instant: SimInstant) -> Self { Self { settle: false, instant: Some(instant), - changes: HashMap::new(), + changes: HashMap::default(), } } fn len(&self) -> usize { diff --git a/crates/fayalite/src/sim/interpreter.rs b/crates/fayalite/src/sim/interpreter.rs index 22f6f5f..de582f0 100644 --- a/crates/fayalite/src/sim/interpreter.rs +++ b/crates/fayalite/src/sim/interpreter.rs @@ -7,10 +7,9 @@ use crate::{ intern::{Intern, Interned, Memoize}, source_location::SourceLocation, ty::CanonicalType, - util::get_many_mut, + util::{get_many_mut, HashMap, HashSet}, }; use bitvec::{boxed::BitBox, slice::BitSlice}; -use hashbrown::{HashMap, HashSet}; use num_bigint::BigInt; use num_traits::{One, Signed, ToPrimitive, Zero}; use std::{ diff --git a/crates/fayalite/src/sim/vcd.rs b/crates/fayalite/src/sim/vcd.rs index fde30be..fcf6743 100644 --- a/crates/fayalite/src/sim/vcd.rs +++ b/crates/fayalite/src/sim/vcd.rs @@ -14,9 +14,10 @@ use crate::{ TraceModuleIO, TraceReg, TraceSInt, TraceScalar, TraceScalarId, TraceScope, TraceSyncReset, TraceUInt, TraceWire, TraceWriter, TraceWriterDecls, }, + util::HashMap, }; use bitvec::{order::Lsb0, slice::BitSlice}; -use hashbrown::{hash_map::Entry, HashMap}; +use hashbrown::hash_map::Entry; use std::{ fmt::{self, Write as _}, io, mem, diff --git a/crates/fayalite/src/source_location.rs b/crates/fayalite/src/source_location.rs index d143f22..1a168b1 100644 --- a/crates/fayalite/src/source_location.rs +++ b/crates/fayalite/src/source_location.rs @@ -2,9 +2,8 @@ // See Notices.txt for copyright information use crate::{ intern::{Intern, Interned}, - util::DebugAsDisplay, + util::{DebugAsDisplay, HashMap}, }; -use hashbrown::HashMap; use std::{cell::RefCell, fmt, num::NonZeroUsize, panic, path::Path}; #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] @@ -97,7 +96,7 @@ impl NormalizeFilesForTestsState { fn new() -> Self { Self { test_position: panic::Location::caller(), - file_pattern_matches: HashMap::new(), + file_pattern_matches: HashMap::default(), } } } @@ -143,7 +142,7 @@ impl From<&'_ panic::Location<'_>> for SourceLocation { map.entry_ref(file) .or_insert_with(|| NormalizedFileForTestState { file_name_id: NonZeroUsize::new(len + 1).unwrap(), - positions_map: HashMap::new(), + positions_map: HashMap::default(), }); file_str = m.generate_file_name(file_state.file_name_id); file = &file_str; diff --git a/crates/fayalite/src/testing.rs b/crates/fayalite/src/testing.rs index 4517e34..b81bc3f 100644 --- a/crates/fayalite/src/testing.rs +++ b/crates/fayalite/src/testing.rs @@ -3,9 +3,9 @@ use crate::{ cli::{FormalArgs, FormalMode, FormalOutput, RunPhase}, firrtl::ExportOptions, + util::HashMap, }; use clap::Parser; -use hashbrown::HashMap; use serde::Deserialize; use std::{ fmt::Write, @@ -87,7 +87,7 @@ fn get_assert_formal_target_path(test_name: &dyn std::fmt::Display) -> PathBuf { let index = *DIRS .lock() .unwrap() - .get_or_insert_with(HashMap::new) + .get_or_insert_with(HashMap::default) .entry_ref(&dir) .and_modify(|v| *v += 1) .or_insert(0); diff --git a/crates/fayalite/src/util.rs b/crates/fayalite/src/util.rs index 804ff19..8d90135 100644 --- a/crates/fayalite/src/util.rs +++ b/crates/fayalite/src/util.rs @@ -9,6 +9,12 @@ mod misc; mod scoped_ref; pub(crate) mod streaming_read_utf8; +// allow easily switching the hasher crate-wide for testing +pub(crate) type DefaultBuildHasher = hashbrown::hash_map::DefaultHashBuilder; + +pub(crate) type HashMap = hashbrown::HashMap; +pub(crate) type HashSet = hashbrown::HashSet; + #[doc(inline)] pub use const_bool::{ConstBool, ConstBoolDispatch, ConstBoolDispatchTag, GenericConstBool}; #[doc(inline)] From 122c08d3cf8668ed80a01e30f4b75a3f7fdf6d3d Mon Sep 17 00:00:00 2001 From: Jacob Lifshay Date: Wed, 9 Apr 2025 20:21:43 -0700 Subject: [PATCH 32/38] add fake which for miri --- crates/fayalite/src/cli.rs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/crates/fayalite/src/cli.rs b/crates/fayalite/src/cli.rs index 66741ef..1f208a8 100644 --- a/crates/fayalite/src/cli.rs +++ b/crates/fayalite/src/cli.rs @@ -258,7 +258,7 @@ pub struct VerilogArgs { default_value = "firtool", env = "FIRTOOL", value_hint = ValueHint::CommandName, - value_parser = OsStringValueParser::new().try_map(which::which) + value_parser = OsStringValueParser::new().try_map(which) )] pub firtool: PathBuf, #[arg(long)] @@ -428,6 +428,13 @@ impl clap::Args for FormalAdjustArgs { } } +fn which(v: std::ffi::OsString) -> which::Result { + #[cfg(not(miri))] + return which::which(v); + #[cfg(miri)] + return Ok(Path::new("/").join(v)); +} + #[derive(Parser, Clone)] #[non_exhaustive] pub struct FormalArgs { @@ -438,7 +445,7 @@ pub struct FormalArgs { default_value = "sby", env = "SBY", value_hint = ValueHint::CommandName, - value_parser = OsStringValueParser::new().try_map(which::which) + value_parser = OsStringValueParser::new().try_map(which) )] pub sby: PathBuf, #[arg(long)] From 4eda4366c86ab3b4edfb6f388f7ff864c6344d52 Mon Sep 17 00:00:00 2001 From: Jacob Lifshay Date: Wed, 9 Apr 2025 20:23:19 -0700 Subject: [PATCH 33/38] check types in debug mode in impl Debug for Expr, helping to catch bugs --- crates/fayalite/src/expr.rs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/crates/fayalite/src/expr.rs b/crates/fayalite/src/expr.rs index f511c97..e070674 100644 --- a/crates/fayalite/src/expr.rs +++ b/crates/fayalite/src/expr.rs @@ -274,6 +274,17 @@ pub struct Expr { impl fmt::Debug for Expr { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + #[cfg(debug_assertions)] + { + let Self { + __enum, + __ty, + __flow, + } = self; + let expr_ty = __ty.canonical(); + let enum_ty = __enum.to_expr().__ty; + assert_eq!(expr_ty, enum_ty, "expr ty mismatch:\nExpr {{\n__enum: {__enum:?},\n__ty: {__ty:?},\n__flow: {__flow:?}\n}}"); + } self.__enum.fmt(f) } } From b1f9706e4e06d6a8c6b23832b7e6551865cb464f Mon Sep 17 00:00:00 2001 From: Jacob Lifshay Date: Wed, 9 Apr 2025 20:24:39 -0700 Subject: [PATCH 34/38] add custom hasher for testing --- crates/fayalite/Cargo.toml | 1 + crates/fayalite/src/util.rs | 4 + crates/fayalite/src/util/test_hasher.rs | 240 ++++++++++++++++++++++++ 3 files changed, 245 insertions(+) create mode 100644 crates/fayalite/src/util/test_hasher.rs diff --git a/crates/fayalite/Cargo.toml b/crates/fayalite/Cargo.toml index 2652792..f176698 100644 --- a/crates/fayalite/Cargo.toml +++ b/crates/fayalite/Cargo.toml @@ -40,6 +40,7 @@ fayalite-visit-gen.workspace = true [features] unstable-doc = [] +unstable-test-hasher = [] [package.metadata.docs.rs] features = ["unstable-doc"] diff --git a/crates/fayalite/src/util.rs b/crates/fayalite/src/util.rs index 8d90135..ebc3f6d 100644 --- a/crates/fayalite/src/util.rs +++ b/crates/fayalite/src/util.rs @@ -8,8 +8,12 @@ mod const_usize; mod misc; mod scoped_ref; pub(crate) mod streaming_read_utf8; +mod test_hasher; // allow easily switching the hasher crate-wide for testing +#[cfg(feature = "unstable-test-hasher")] +pub type DefaultBuildHasher = test_hasher::DefaultBuildHasher; +#[cfg(not(feature = "unstable-test-hasher"))] pub(crate) type DefaultBuildHasher = hashbrown::hash_map::DefaultHashBuilder; pub(crate) type HashMap = hashbrown::HashMap; diff --git a/crates/fayalite/src/util/test_hasher.rs b/crates/fayalite/src/util/test_hasher.rs new file mode 100644 index 0000000..2a0cdd4 --- /dev/null +++ b/crates/fayalite/src/util/test_hasher.rs @@ -0,0 +1,240 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// See Notices.txt for copyright information +#![cfg(feature = "unstable-test-hasher")] + +use std::{ + fmt::Write as _, + hash::{BuildHasher, Hash, Hasher}, + io::Write as _, + marker::PhantomData, + sync::LazyLock, +}; + +type BoxDynHasher = Box; +type BoxDynBuildHasher = Box; +type BoxDynMakeBuildHasher = Box BoxDynBuildHasher + Send + Sync>; + +trait TryGetDynBuildHasher: Copy { + type Type; + fn try_get_make_build_hasher(self) -> Option; +} + +impl TryGetDynBuildHasher for PhantomData { + type Type = T; + fn try_get_make_build_hasher(self) -> Option { + None + } +} + +impl + Send + Sync + 'static + Clone> + TryGetDynBuildHasher for &'_ PhantomData +{ + type Type = T; + fn try_get_make_build_hasher(self) -> Option { + Some(Box::new(|| Box::>::default())) + } +} + +#[derive(Default, Clone)] +struct DynBuildHasher(T); + +trait DynBuildHasherTrait: BuildHasher { + fn clone_dyn_build_hasher(&self) -> BoxDynBuildHasher; +} + +impl> BuildHasher for DynBuildHasher { + type Hasher = BoxDynHasher; + + fn build_hasher(&self) -> Self::Hasher { + Box::new(self.0.build_hasher()) + } + + fn hash_one(&self, x: T) -> u64 { + self.0.hash_one(x) + } +} + +impl DynBuildHasherTrait for DynBuildHasher +where + Self: Clone + BuildHasher + Send + Sync + 'static, +{ + fn clone_dyn_build_hasher(&self) -> BoxDynBuildHasher { + Box::new(self.clone()) + } +} + +pub struct DefaultBuildHasher(BoxDynBuildHasher); + +impl Clone for DefaultBuildHasher { + fn clone(&self) -> Self { + DefaultBuildHasher(self.0.clone_dyn_build_hasher()) + } +} + +const ENV_VAR_NAME: &'static str = "FAYALITE_TEST_HASHER"; + +struct EnvVarValue { + key: &'static str, + try_get_make_build_hasher: fn() -> Option, + description: &'static str, +} + +macro_rules! env_var_value { + ( + key: $key:literal, + build_hasher: $build_hasher:ty, + description: $description:literal, + ) => { + EnvVarValue { + key: $key, + try_get_make_build_hasher: || { + // use rust method resolution to detect if $build_hasher is usable + // (e.g. hashbrown's hasher won't be usable without the right feature enabled) + (&PhantomData::>).try_get_make_build_hasher() + }, + description: $description, + } + }; +} + +#[derive(Default)] +struct AlwaysZeroHasher; + +impl Hasher for AlwaysZeroHasher { + fn write(&mut self, _bytes: &[u8]) {} + fn finish(&self) -> u64 { + 0 + } +} + +const ENV_VAR_VALUES: &'static [EnvVarValue] = &[ + env_var_value! { + key: "std", + build_hasher: std::hash::RandomState, + description: "use std::hash::RandomState", + }, + env_var_value! { + key: "hashbrown", + build_hasher: hashbrown::hash_map::DefaultHashBuilder, + description: "use hashbrown's DefaultHashBuilder", + }, + env_var_value! { + key: "always_zero", + build_hasher: std::hash::BuildHasherDefault, + description: "use a hasher that always returns 0 for all hashes,\n \ + this is useful for checking that PartialEq impls are correct", + }, +]; + +fn report_bad_env_var(msg: impl std::fmt::Display) -> ! { + let mut msg = format!("{ENV_VAR_NAME}: {msg}\n"); + for &EnvVarValue { + key, + try_get_make_build_hasher, + description, + } in ENV_VAR_VALUES + { + let availability = match try_get_make_build_hasher() { + Some(_) => "available", + None => "unavailable", + }; + writeln!(msg, "{key}: ({availability})\n {description}").expect("can't fail"); + } + std::io::stderr() + .write_all(msg.as_bytes()) + .expect("should be able to write to stderr"); + std::process::abort(); +} + +impl Default for DefaultBuildHasher { + fn default() -> Self { + static DEFAULT_FN: LazyLock = LazyLock::new(|| { + let var = std::env::var_os(ENV_VAR_NAME); + let var = var.as_deref().unwrap_or("std".as_ref()); + for &EnvVarValue { + key, + try_get_make_build_hasher, + description: _, + } in ENV_VAR_VALUES + { + if var.as_encoded_bytes().eq_ignore_ascii_case(key.as_bytes()) { + return try_get_make_build_hasher().unwrap_or_else(|| { + report_bad_env_var(format_args!( + "unavailable hasher: {key} (is the appropriate feature enabled?)" + )); + }); + } + } + report_bad_env_var(format_args!("unrecognized hasher: {var:?}")); + }); + Self(DEFAULT_FN()) + } +} + +pub struct DefaultHasher(BoxDynHasher); + +impl BuildHasher for DefaultBuildHasher { + type Hasher = DefaultHasher; + + fn build_hasher(&self) -> Self::Hasher { + DefaultHasher(self.0.build_hasher()) + } +} + +impl Hasher for DefaultHasher { + fn finish(&self) -> u64 { + self.0.finish() + } + + fn write(&mut self, bytes: &[u8]) { + self.0.write(bytes) + } + + fn write_u8(&mut self, i: u8) { + self.0.write_u8(i) + } + + fn write_u16(&mut self, i: u16) { + self.0.write_u16(i) + } + + fn write_u32(&mut self, i: u32) { + self.0.write_u32(i) + } + + fn write_u64(&mut self, i: u64) { + self.0.write_u64(i) + } + + fn write_u128(&mut self, i: u128) { + self.0.write_u128(i) + } + + fn write_usize(&mut self, i: usize) { + self.0.write_usize(i) + } + + fn write_i8(&mut self, i: i8) { + self.0.write_i8(i) + } + + fn write_i16(&mut self, i: i16) { + self.0.write_i16(i) + } + + fn write_i32(&mut self, i: i32) { + self.0.write_i32(i) + } + + fn write_i64(&mut self, i: i64) { + self.0.write_i64(i) + } + + fn write_i128(&mut self, i: i128) { + self.0.write_i128(i) + } + + fn write_isize(&mut self, i: isize) { + self.0.write_isize(i) + } +} From e2d2d4110be5d15e0bdc1eb81bd84ed7786c3ad8 Mon Sep 17 00:00:00 2001 From: Jacob Lifshay Date: Wed, 9 Apr 2025 20:33:21 -0700 Subject: [PATCH 35/38] upgrade hashbrown to 0.15.2 --- Cargo.lock | 51 ++++++++----------------- Cargo.toml | 2 +- crates/fayalite/src/util.rs | 2 +- crates/fayalite/src/util/test_hasher.rs | 2 +- 4 files changed, 19 insertions(+), 38 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 23cdc34..611e42e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,18 +2,6 @@ # It is not intended for manual editing. version = 3 -[[package]] -name = "ahash" -version = "0.8.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77c3a9648d43b9cd48db467b3f87fdd6e146bcc88ab0180006cef2179fe11d01" -dependencies = [ - "cfg-if", - "once_cell", - "version_check", - "zerocopy", -] - [[package]] name = "allocator-api2" version = "0.2.16" @@ -310,7 +298,7 @@ dependencies = [ "eyre", "fayalite-proc-macros", "fayalite-visit-gen", - "hashbrown", + "hashbrown 0.15.2", "jobslot", "num-bigint", "num-traits", @@ -365,6 +353,12 @@ version = "0.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1d674e81391d1e1ab681a28d99df07927c6d4aa5b027d7da16ba32d1d21ecd99" +[[package]] +name = "foldhash" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" + [[package]] name = "funty" version = "2.0.0" @@ -403,9 +397,16 @@ name = "hashbrown" version = "0.14.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" + +[[package]] +name = "hashbrown" +version = "0.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" dependencies = [ - "ahash", "allocator-api2", + "equivalent", + "foldhash", ] [[package]] @@ -436,7 +437,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68b900aa2f7301e21c36462b170ee99994de34dff39a4a6a528e80e7376d07e5" dependencies = [ "equivalent", - "hashbrown", + "hashbrown 0.14.3", "serde", ] @@ -893,23 +894,3 @@ checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" dependencies = [ "tap", ] - -[[package]] -name = "zerocopy" -version = "0.7.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74d4d3961e53fa4c9a25a8637fc2bfaf2595b3d3ae34875568a5cf64787716be" -dependencies = [ - "zerocopy-derive", -] - -[[package]] -name = "zerocopy-derive" -version = "0.7.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] diff --git a/Cargo.toml b/Cargo.toml index 54de3a8..8a022c9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,7 +23,7 @@ blake3 = { version = "1.5.4", features = ["serde"] } clap = { version = "4.5.9", features = ["derive", "env", "string"] } ctor = "0.2.8" eyre = "0.6.12" -hashbrown = "0.14.3" +hashbrown = "0.15.2" indexmap = { version = "2.5.0", features = ["serde"] } jobslot = "0.2.19" num-bigint = "0.4.6" diff --git a/crates/fayalite/src/util.rs b/crates/fayalite/src/util.rs index ebc3f6d..233867e 100644 --- a/crates/fayalite/src/util.rs +++ b/crates/fayalite/src/util.rs @@ -14,7 +14,7 @@ mod test_hasher; #[cfg(feature = "unstable-test-hasher")] pub type DefaultBuildHasher = test_hasher::DefaultBuildHasher; #[cfg(not(feature = "unstable-test-hasher"))] -pub(crate) type DefaultBuildHasher = hashbrown::hash_map::DefaultHashBuilder; +pub(crate) type DefaultBuildHasher = hashbrown::DefaultHashBuilder; pub(crate) type HashMap = hashbrown::HashMap; pub(crate) type HashSet = hashbrown::HashSet; diff --git a/crates/fayalite/src/util/test_hasher.rs b/crates/fayalite/src/util/test_hasher.rs index 2a0cdd4..20df5b7 100644 --- a/crates/fayalite/src/util/test_hasher.rs +++ b/crates/fayalite/src/util/test_hasher.rs @@ -115,7 +115,7 @@ const ENV_VAR_VALUES: &'static [EnvVarValue] = &[ }, env_var_value! { key: "hashbrown", - build_hasher: hashbrown::hash_map::DefaultHashBuilder, + build_hasher: hashbrown::DefaultHashBuilder, description: "use hashbrown's DefaultHashBuilder", }, env_var_value! { From 91e1b619e8e6c4130cc7e80bd683625b1ec3cd9d Mon Sep 17 00:00:00 2001 From: Jacob Lifshay Date: Wed, 9 Apr 2025 20:48:40 -0700 Subject: [PATCH 36/38] switch to petgraph 0.8.1 now that my PR was merged and released to crates.io --- Cargo.lock | 21 +++++++++------------ Cargo.toml | 3 +-- crates/fayalite/src/sim.rs | 10 ++++++++++ 3 files changed, 20 insertions(+), 14 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 611e42e..e0c32e9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -298,7 +298,7 @@ dependencies = [ "eyre", "fayalite-proc-macros", "fayalite-visit-gen", - "hashbrown 0.15.2", + "hashbrown", "jobslot", "num-bigint", "num-traits", @@ -392,12 +392,6 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" -[[package]] -name = "hashbrown" -version = "0.14.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" - [[package]] name = "hashbrown" version = "0.15.2" @@ -432,12 +426,12 @@ checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683" [[package]] name = "indexmap" -version = "2.5.0" +version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68b900aa2f7301e21c36462b170ee99994de34dff39a4a6a528e80e7376d07e5" +checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e" dependencies = [ "equivalent", - "hashbrown 0.14.3", + "hashbrown", "serde", ] @@ -525,11 +519,14 @@ dependencies = [ [[package]] name = "petgraph" -version = "0.6.5" -source = "git+https://github.com/programmerjake/petgraph.git?rev=258ea8071209a924b73fe96f9f87a3b7b45cbc9f#258ea8071209a924b73fe96f9f87a3b7b45cbc9f" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a98c6720655620a521dcc722d0ad66cd8afd5d86e34a89ef691c50b7b24de06" dependencies = [ "fixedbitset", + "hashbrown", "indexmap", + "serde", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 8a022c9..d681425 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -29,8 +29,7 @@ jobslot = "0.2.19" num-bigint = "0.4.6" num-traits = "0.2.16" os_pipe = "1.2.1" -# TODO: switch back to crates.io once petgraph accepts PR #684 and releases a new version -petgraph = { git = "https://github.com/programmerjake/petgraph.git", rev = "258ea8071209a924b73fe96f9f87a3b7b45cbc9f" } +petgraph = "0.8.1" prettyplease = "0.2.20" proc-macro2 = "1.0.83" quote = "1.0.36" diff --git a/crates/fayalite/src/sim.rs b/crates/fayalite/src/sim.rs index b7845f4..6659391 100644 --- a/crates/fayalite/src/sim.rs +++ b/crates/fayalite/src/sim.rs @@ -1022,6 +1022,16 @@ impl VisitMap for AssignmentsVisitMap { AssignmentOrSlotIndex::BigSlot(slot) => self.slots.contains(slot), } } + + fn unvisit(&mut self, n: AssignmentOrSlotIndex) -> bool { + match n { + AssignmentOrSlotIndex::AssignmentIndex(assignment_index) => { + mem::replace(&mut self.assignments[assignment_index], false) + } + AssignmentOrSlotIndex::SmallSlot(slot) => self.slots.remove(slot), + AssignmentOrSlotIndex::BigSlot(slot) => self.slots.remove(slot), + } + } } impl Visitable for Assignments { From 88323a8c164d54966ddde60a73f0132d732cd3f8 Mon Sep 17 00:00:00 2001 From: Jacob Lifshay Date: Wed, 9 Apr 2025 21:03:57 -0700 Subject: [PATCH 37/38] run some tests with always_zero hasher --- .forgejo/workflows/test.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.forgejo/workflows/test.yml b/.forgejo/workflows/test.yml index 49fb3e4..d878758 100644 --- a/.forgejo/workflows/test.yml +++ b/.forgejo/workflows/test.yml @@ -59,3 +59,4 @@ jobs: - run: cargo build --tests --features=unstable-doc - run: cargo test --doc --features=unstable-doc - run: cargo doc --features=unstable-doc + - run: cargo test --test=module --features=unstable-doc,unstable-test-hasher From 668e714dc98231eca52541aadd7ce160cfed3f90 Mon Sep 17 00:00:00 2001 From: Jacob Lifshay Date: Wed, 9 Apr 2025 21:11:09 -0700 Subject: [PATCH 38/38] actually test always_zero hasher --- .forgejo/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.forgejo/workflows/test.yml b/.forgejo/workflows/test.yml index d878758..b2d03ba 100644 --- a/.forgejo/workflows/test.yml +++ b/.forgejo/workflows/test.yml @@ -59,4 +59,4 @@ jobs: - run: cargo build --tests --features=unstable-doc - run: cargo test --doc --features=unstable-doc - run: cargo doc --features=unstable-doc - - run: cargo test --test=module --features=unstable-doc,unstable-test-hasher + - run: FAYALITE_TEST_HASHER=always_zero cargo test --test=module --features=unstable-doc,unstable-test-hasher