forked from libre-chip/fayalite
		
	Compare commits
	
		
			No commits in common. "86a1bb46be4690e854470cf4a1a6e90c92268391" and "7005fa3330d6715008a3e3cd0b57c71108b465d2" have entirely different histories.
		
	
	
		
			86a1bb46be
			...
			7005fa3330
		
	
		
					 23 changed files with 234 additions and 4768 deletions
				
			
		|  | @ -1210,29 +1210,17 @@ fn hdl_main( | ||||||
|     attr: TokenStream, |     attr: TokenStream, | ||||||
|     item: TokenStream, |     item: TokenStream, | ||||||
| ) -> syn::Result<TokenStream> { | ) -> syn::Result<TokenStream> { | ||||||
|     fn parse_evaluated_cfgs_attr<R>( |     let (evaluated_cfgs, attr): (_, TokenStream) = Parser::parse2( | ||||||
|         input: ParseStream, |  | ||||||
|         parse_inner: impl FnOnce(ParseStream) -> syn::Result<R>, |  | ||||||
|     ) -> syn::Result<R> { |  | ||||||
|         let _: Token![#] = input.parse()?; |  | ||||||
|         let bracket_content; |  | ||||||
|         bracketed!(bracket_content in input); |  | ||||||
|         let _: kw::__evaluated_cfgs = bracket_content.parse()?; |  | ||||||
|         let paren_content; |  | ||||||
|         parenthesized!(paren_content in bracket_content); |  | ||||||
|         parse_inner(&paren_content) |  | ||||||
|     } |  | ||||||
|     let (evaluated_cfgs, item): (_, TokenStream) = Parser::parse2( |  | ||||||
|         |input: ParseStream| { |         |input: ParseStream| { | ||||||
|             let peek = input.fork(); |             if input.peek(Bracket) && input.peek2(kw::__evaluated_cfgs) { | ||||||
|             if parse_evaluated_cfgs_attr(&peek, |_| Ok(())).is_ok() { |                 let cfgs = input.parse()?; | ||||||
|                 let evaluated_cfgs = parse_evaluated_cfgs_attr(input, Cfgs::<bool>::parse)?; |                 let _: kw::__evaluated_cfgs = input.parse()?; | ||||||
|                 Ok((Some(evaluated_cfgs), input.parse()?)) |                 Ok((Some(cfgs), input.parse()?)) | ||||||
|             } else { |             } else { | ||||||
|                 Ok((None, input.parse()?)) |                 Ok((None, input.parse()?)) | ||||||
|             } |             } | ||||||
|         }, |         }, | ||||||
|         item, |         attr, | ||||||
|     )?; |     )?; | ||||||
|     let cfgs = if let Some(cfgs) = evaluated_cfgs { |     let cfgs = if let Some(cfgs) = evaluated_cfgs { | ||||||
|         cfgs |         cfgs | ||||||
|  | @ -1241,11 +1229,12 @@ fn hdl_main( | ||||||
|         if cfgs.cfgs_list.is_empty() { |         if cfgs.cfgs_list.is_empty() { | ||||||
|             Cfgs::default() |             Cfgs::default() | ||||||
|         } else { |         } else { | ||||||
|  |             let key = kw::__evaluated_cfgs::default(); | ||||||
|             return Ok(quote! { |             return Ok(quote! { | ||||||
|                 ::fayalite::__cfg_expansion_helper! { |                 ::fayalite::__cfg_expansion_helper! { | ||||||
|                     [] |                     [] | ||||||
|                     #cfgs |                     #cfgs | ||||||
|                     {#[::fayalite::#kw(#attr)]} { #item } |                     #[::fayalite::#kw:(#key #attr)] { #item } | ||||||
|                 } |                 } | ||||||
|             }); |             }); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|  | @ -1109,7 +1109,7 @@ fn parse_quote_let_pat<T, R: ToTokens, C: Borrow<Token![:]>>( | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| pub(crate) fn wrap_ty_with_expr(ty: impl ToTokens) -> Type { | fn wrap_ty_with_expr(ty: impl ToTokens) -> Type { | ||||||
|     parse_quote_spanned! {ty.span()=> |     parse_quote_spanned! {ty.span()=> | ||||||
|         ::fayalite::expr::Expr<#ty> |         ::fayalite::expr::Expr<#ty> | ||||||
|     } |     } | ||||||
|  | @ -1586,7 +1586,7 @@ impl Visitor<'_> { | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| pub(crate) fn empty_let() -> Local { | fn empty_let() -> Local { | ||||||
|     Local { |     Local { | ||||||
|         attrs: vec![], |         attrs: vec![], | ||||||
|         let_token: Default::default(), |         let_token: Default::default(), | ||||||
|  | @ -1672,7 +1672,7 @@ impl Fold for Visitor<'_> { | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     fn fold_local(&mut self, mut let_stmt: Local) -> Local { |     fn fold_local(&mut self, let_stmt: Local) -> Local { | ||||||
|         match self |         match self | ||||||
|             .errors |             .errors | ||||||
|             .ok(HdlAttr::<Nothing, kw::hdl>::parse_and_leave_attr( |             .ok(HdlAttr::<Nothing, kw::hdl>::parse_and_leave_attr( | ||||||
|  | @ -1682,25 +1682,6 @@ impl Fold for Visitor<'_> { | ||||||
|             Some(None) => return fold_local(self, let_stmt), |             Some(None) => return fold_local(self, let_stmt), | ||||||
|             Some(Some(HdlAttr { .. })) => {} |             Some(Some(HdlAttr { .. })) => {} | ||||||
|         }; |         }; | ||||||
|         let mut pat = &let_stmt.pat; |  | ||||||
|         if let Pat::Type(pat_type) = pat { |  | ||||||
|             pat = &pat_type.pat; |  | ||||||
|         } |  | ||||||
|         let Pat::Ident(syn::PatIdent { |  | ||||||
|             attrs: _, |  | ||||||
|             by_ref: None, |  | ||||||
|             mutability: _, |  | ||||||
|             ident: _, |  | ||||||
|             subpat: None, |  | ||||||
|         }) = pat |  | ||||||
|         else { |  | ||||||
|             let hdl_attr = HdlAttr::<Nothing, kw::hdl>::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); |  | ||||||
|         }; |  | ||||||
|         let hdl_let = syn::parse2::<HdlLet<HdlLetKind<Type>>>(let_stmt.into_token_stream()); |         let hdl_let = syn::parse2::<HdlLet<HdlLetKind<Type>>>(let_stmt.into_token_stream()); | ||||||
|         let Some(hdl_let) = self.errors.ok(hdl_let) else { |         let Some(hdl_let) = self.errors.ok(hdl_let) else { | ||||||
|             return empty_let(); |             return empty_let(); | ||||||
|  |  | ||||||
|  | @ -3,111 +3,22 @@ | ||||||
| use crate::{ | use crate::{ | ||||||
|     fold::{impl_fold, DoFold}, |     fold::{impl_fold, DoFold}, | ||||||
|     kw, |     kw, | ||||||
|     module::transform_body::{empty_let, with_debug_clone_and_fold, wrap_ty_with_expr, Visitor}, |     module::transform_body::{with_debug_clone_and_fold, Visitor}, | ||||||
|     Errors, HdlAttr, PairsIterExt, |     Errors, HdlAttr, PairsIterExt, | ||||||
| }; | }; | ||||||
| use proc_macro2::{Span, TokenStream}; | use proc_macro2::{Span, TokenStream}; | ||||||
| use quote::{format_ident, quote_spanned, ToTokens, TokenStreamExt}; | use quote::{format_ident, quote_spanned, ToTokens, TokenStreamExt}; | ||||||
| use std::collections::BTreeSet; |  | ||||||
| use syn::{ | use syn::{ | ||||||
|     fold::{fold_arm, fold_expr_match, fold_local, fold_pat, Fold}, |     fold::{fold_arm, fold_expr_match, fold_pat, Fold}, | ||||||
|     parse::Nothing, |     parse::Nothing, | ||||||
|     parse_quote_spanned, |     parse_quote_spanned, | ||||||
|     punctuated::Punctuated, |     punctuated::Punctuated, | ||||||
|     spanned::Spanned, |     spanned::Spanned, | ||||||
|     token::{Brace, Paren}, |     token::{Brace, Paren}, | ||||||
|     Arm, Attribute, Expr, ExprMatch, FieldPat, Ident, Local, Member, Pat, PatIdent, PatOr, |     Arm, Attribute, Expr, ExprMatch, FieldPat, Ident, Member, Pat, PatIdent, PatOr, PatParen, | ||||||
|     PatParen, PatPath, PatRest, PatStruct, PatTuple, PatTupleStruct, PatWild, Path, PathSegment, |     PatPath, PatRest, PatStruct, PatTupleStruct, PatWild, Path, PathSegment, Token, TypePath, | ||||||
|     Token, TypePath, |  | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| macro_rules! visit_trait { |  | ||||||
|     ( |  | ||||||
|         $($vis:vis fn $fn:ident($state:ident: _, $value:ident: &$Value:ty) $block:block)* |  | ||||||
|     ) => { |  | ||||||
|         trait VisitMatchPat<'a> { |  | ||||||
|             $(fn $fn(&mut self, $value: &'a $Value) { |  | ||||||
|                 $fn(self, $value); |  | ||||||
|             })* |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         $($vis fn $fn<'a>($state: &mut (impl ?Sized + VisitMatchPat<'a>), $value: &'a $Value) $block)* |  | ||||||
|     }; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| visit_trait! { |  | ||||||
|     fn visit_match_pat_binding(_state: _, v: &MatchPatBinding) { |  | ||||||
|         let MatchPatBinding { ident: _ } = v; |  | ||||||
|     } |  | ||||||
|     fn visit_match_pat_wild(_state: _, v: &MatchPatWild) { |  | ||||||
|         let MatchPatWild { underscore_token: _ } = v; |  | ||||||
|     } |  | ||||||
|     fn visit_match_pat_rest(_state: _, v: &MatchPatRest) { |  | ||||||
|         let MatchPatRest { dot2_token: _ } = v; |  | ||||||
|     } |  | ||||||
|     fn visit_match_pat_paren(state: _, v: &MatchPatParen<MatchPat>) { |  | ||||||
|         let MatchPatParen { paren_token: _, pat } = v; |  | ||||||
|         state.visit_match_pat(pat); |  | ||||||
|     } |  | ||||||
|     fn visit_match_pat_paren_simple(state: _, v: &MatchPatParen<MatchPatSimple>) { |  | ||||||
|         let MatchPatParen { paren_token: _, pat } = v; |  | ||||||
|         state.visit_match_pat_simple(pat); |  | ||||||
|     } |  | ||||||
|     fn visit_match_pat_or(state: _, v: &MatchPatOr<MatchPat>) { |  | ||||||
|         let MatchPatOr { leading_vert: _, cases } = v; |  | ||||||
|         for v in cases { |  | ||||||
|             state.visit_match_pat(v); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|     fn visit_match_pat_or_simple(state: _, v: &MatchPatOr<MatchPatSimple>) { |  | ||||||
|         let MatchPatOr { leading_vert: _, cases } = v; |  | ||||||
|         for v in cases { |  | ||||||
|             state.visit_match_pat_simple(v); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|     fn visit_match_pat_struct_field(state: _, v: &MatchPatStructField) { |  | ||||||
|         let MatchPatStructField { field_name: _, colon_token: _, pat } = v; |  | ||||||
|         state.visit_match_pat_simple(pat); |  | ||||||
|     } |  | ||||||
|     fn visit_match_pat_struct(state: _, v: &MatchPatStruct) { |  | ||||||
|         let MatchPatStruct { match_span: _, path: _, brace_token: _, fields, rest: _ } = v; |  | ||||||
|         for v in fields { |  | ||||||
|             state.visit_match_pat_struct_field(v); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|     fn visit_match_pat_tuple(state: _, v: &MatchPatTuple) { |  | ||||||
|         let MatchPatTuple { paren_token: _, fields } = v; |  | ||||||
|         for v in fields { |  | ||||||
|             state.visit_match_pat_simple(v); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|     fn visit_match_pat_enum_variant(state: _, v: &MatchPatEnumVariant) { |  | ||||||
|         let MatchPatEnumVariant {match_span:_, variant_path: _, enum_path: _, variant_name: _, field } = v; |  | ||||||
|         if let Some((_, v)) = field { |  | ||||||
|             state.visit_match_pat_simple(v); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|     fn visit_match_pat_simple(state: _, v: &MatchPatSimple) { |  | ||||||
|         match v { |  | ||||||
|             MatchPatSimple::Paren(v) => state.visit_match_pat_paren_simple(v), |  | ||||||
|             MatchPatSimple::Or(v) => state.visit_match_pat_or_simple(v), |  | ||||||
|             MatchPatSimple::Binding(v) => state.visit_match_pat_binding(v), |  | ||||||
|             MatchPatSimple::Wild(v) => state.visit_match_pat_wild(v), |  | ||||||
|             MatchPatSimple::Rest(v) => state.visit_match_pat_rest(v), |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|     fn visit_match_pat(state: _, v: &MatchPat) { |  | ||||||
|         match v { |  | ||||||
|             MatchPat::Simple(v) => state.visit_match_pat_simple(v), |  | ||||||
|             MatchPat::Or(v) => state.visit_match_pat_or(v), |  | ||||||
|             MatchPat::Paren(v) => state.visit_match_pat_paren(v), |  | ||||||
|             MatchPat::Struct(v) => state.visit_match_pat_struct(v), |  | ||||||
|             MatchPat::Tuple(v) => state.visit_match_pat_tuple(v), |  | ||||||
|             MatchPat::EnumVariant(v) => state.visit_match_pat_enum_variant(v), |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| with_debug_clone_and_fold! { | with_debug_clone_and_fold! { | ||||||
|     struct MatchPatBinding<> { |     struct MatchPatBinding<> { | ||||||
|         ident: Ident, |         ident: Ident, | ||||||
|  | @ -142,15 +53,6 @@ with_debug_clone_and_fold! { | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl<P> MatchPatOr<P> { |  | ||||||
|     /// returns the first `|` between two patterns
 |  | ||||||
|     fn first_inner_vert(&self) -> Option<Token![|]> { |  | ||||||
|         let mut pairs = self.cases.pairs(); |  | ||||||
|         pairs.next_back(); |  | ||||||
|         pairs.next().and_then(|v| v.into_tuple().1.copied()) |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| impl<P: ToTokens> ToTokens for MatchPatOr<P> { | impl<P: ToTokens> ToTokens for MatchPatOr<P> { | ||||||
|     fn to_tokens(&self, tokens: &mut TokenStream) { |     fn to_tokens(&self, tokens: &mut TokenStream) { | ||||||
|         let Self { |         let Self { | ||||||
|  | @ -175,19 +77,6 @@ impl ToTokens for MatchPatWild { | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| with_debug_clone_and_fold! { |  | ||||||
|     struct MatchPatRest<> { |  | ||||||
|         dot2_token: Token![..], |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| impl ToTokens for MatchPatRest { |  | ||||||
|     fn to_tokens(&self, tokens: &mut TokenStream) { |  | ||||||
|         let Self { dot2_token } = self; |  | ||||||
|         dot2_token.to_tokens(tokens); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| with_debug_clone_and_fold! { | with_debug_clone_and_fold! { | ||||||
|     struct MatchPatStructField<> { |     struct MatchPatStructField<> { | ||||||
|         field_name: Ident, |         field_name: Ident, | ||||||
|  | @ -270,25 +159,6 @@ impl ToTokens for MatchPatStruct { | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| with_debug_clone_and_fold! { |  | ||||||
|     struct MatchPatTuple<> { |  | ||||||
|         paren_token: Paren, |  | ||||||
|         fields: Punctuated<MatchPatSimple, Token![,]>, |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| impl ToTokens for MatchPatTuple { |  | ||||||
|     fn to_tokens(&self, tokens: &mut TokenStream) { |  | ||||||
|         let Self { |  | ||||||
|             paren_token, |  | ||||||
|             fields, |  | ||||||
|         } = self; |  | ||||||
|         paren_token.surround(tokens, |tokens| { |  | ||||||
|             fields.to_tokens(tokens); |  | ||||||
|         }) |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| with_debug_clone_and_fold! { | with_debug_clone_and_fold! { | ||||||
|     struct MatchPatEnumVariant<> { |     struct MatchPatEnumVariant<> { | ||||||
|         match_span: Span, |         match_span: Span, | ||||||
|  | @ -324,7 +194,6 @@ enum MatchPatSimple { | ||||||
|     Or(MatchPatOr<MatchPatSimple>), |     Or(MatchPatOr<MatchPatSimple>), | ||||||
|     Binding(MatchPatBinding), |     Binding(MatchPatBinding), | ||||||
|     Wild(MatchPatWild), |     Wild(MatchPatWild), | ||||||
|     Rest(MatchPatRest), |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl_fold! { | impl_fold! { | ||||||
|  | @ -333,7 +202,6 @@ impl_fold! { | ||||||
|         Or(MatchPatOr<MatchPatSimple>), |         Or(MatchPatOr<MatchPatSimple>), | ||||||
|         Binding(MatchPatBinding), |         Binding(MatchPatBinding), | ||||||
|         Wild(MatchPatWild), |         Wild(MatchPatWild), | ||||||
|         Rest(MatchPatRest), |  | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -344,7 +212,6 @@ impl ToTokens for MatchPatSimple { | ||||||
|             Self::Paren(v) => v.to_tokens(tokens), |             Self::Paren(v) => v.to_tokens(tokens), | ||||||
|             Self::Binding(v) => v.to_tokens(tokens), |             Self::Binding(v) => v.to_tokens(tokens), | ||||||
|             Self::Wild(v) => v.to_tokens(tokens), |             Self::Wild(v) => v.to_tokens(tokens), | ||||||
|             Self::Rest(v) => v.to_tokens(tokens), |  | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | @ -411,7 +278,6 @@ trait ParseMatchPat: Sized { | ||||||
|     fn or(v: MatchPatOr<Self>) -> Self; |     fn or(v: MatchPatOr<Self>) -> Self; | ||||||
|     fn paren(v: MatchPatParen<Self>) -> Self; |     fn paren(v: MatchPatParen<Self>) -> Self; | ||||||
|     fn struct_(state: &mut HdlMatchParseState<'_>, v: MatchPatStruct) -> Result<Self, ()>; |     fn struct_(state: &mut HdlMatchParseState<'_>, v: MatchPatStruct) -> Result<Self, ()>; | ||||||
|     fn tuple(state: &mut HdlMatchParseState<'_>, v: MatchPatTuple) -> Result<Self, ()>; |  | ||||||
|     fn enum_variant(state: &mut HdlMatchParseState<'_>, v: MatchPatEnumVariant) |     fn enum_variant(state: &mut HdlMatchParseState<'_>, v: MatchPatEnumVariant) | ||||||
|         -> Result<Self, ()>; |         -> Result<Self, ()>; | ||||||
|     fn parse(state: &mut HdlMatchParseState<'_>, pat: Pat) -> Result<Self, ()> { |     fn parse(state: &mut HdlMatchParseState<'_>, pat: Pat) -> Result<Self, ()> { | ||||||
|  | @ -596,34 +462,7 @@ trait ParseMatchPat: Sized { | ||||||
|             }) => Ok(Self::simple(MatchPatSimple::Wild(MatchPatWild { |             }) => Ok(Self::simple(MatchPatSimple::Wild(MatchPatWild { | ||||||
|                 underscore_token, |                 underscore_token, | ||||||
|             }))), |             }))), | ||||||
|             Pat::Tuple(PatTuple { |             Pat::Tuple(_) | Pat::Slice(_) | Pat::Const(_) | Pat::Lit(_) | Pat::Range(_) => { | ||||||
|                 attrs: _, |  | ||||||
|                 paren_token, |  | ||||||
|                 elems, |  | ||||||
|             }) => { |  | ||||||
|                 let fields = elems |  | ||||||
|                     .into_pairs() |  | ||||||
|                     .filter_map_pair_value(|field_pat| { |  | ||||||
|                         if let Pat::Rest(PatRest { |  | ||||||
|                             attrs: _, |  | ||||||
|                             dot2_token, |  | ||||||
|                         }) = field_pat |  | ||||||
|                         { |  | ||||||
|                             Some(MatchPatSimple::Rest(MatchPatRest { dot2_token })) |  | ||||||
|                         } else { |  | ||||||
|                             MatchPatSimple::parse(state, field_pat).ok() |  | ||||||
|                         } |  | ||||||
|                     }) |  | ||||||
|                     .collect(); |  | ||||||
|                 Self::tuple( |  | ||||||
|                     state, |  | ||||||
|                     MatchPatTuple { |  | ||||||
|                         paren_token, |  | ||||||
|                         fields, |  | ||||||
|                     }, |  | ||||||
|                 ) |  | ||||||
|             } |  | ||||||
|             Pat::Slice(_) | Pat::Const(_) | Pat::Lit(_) | Pat::Range(_) => { |  | ||||||
|                 state |                 state | ||||||
|                     .errors |                     .errors | ||||||
|                     .error(pat, "not yet implemented in #[hdl] patterns"); |                     .error(pat, "not yet implemented in #[hdl] patterns"); | ||||||
|  | @ -658,14 +497,6 @@ impl ParseMatchPat for MatchPatSimple { | ||||||
|         Err(()) |         Err(()) | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     fn tuple(state: &mut HdlMatchParseState<'_>, v: MatchPatTuple) -> Result<Self, ()> { |  | ||||||
|         state.errors.push(syn::Error::new( |  | ||||||
|             v.paren_token.span.open(), |  | ||||||
|             "matching tuples is not yet implemented inside structs/enums in #[hdl] patterns", |  | ||||||
|         )); |  | ||||||
|         Err(()) |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     fn enum_variant( |     fn enum_variant( | ||||||
|         state: &mut HdlMatchParseState<'_>, |         state: &mut HdlMatchParseState<'_>, | ||||||
|         v: MatchPatEnumVariant, |         v: MatchPatEnumVariant, | ||||||
|  | @ -684,7 +515,6 @@ enum MatchPat { | ||||||
|     Or(MatchPatOr<MatchPat>), |     Or(MatchPatOr<MatchPat>), | ||||||
|     Paren(MatchPatParen<MatchPat>), |     Paren(MatchPatParen<MatchPat>), | ||||||
|     Struct(MatchPatStruct), |     Struct(MatchPatStruct), | ||||||
|     Tuple(MatchPatTuple), |  | ||||||
|     EnumVariant(MatchPatEnumVariant), |     EnumVariant(MatchPatEnumVariant), | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -694,7 +524,6 @@ impl_fold! { | ||||||
|         Or(MatchPatOr<MatchPat>), |         Or(MatchPatOr<MatchPat>), | ||||||
|         Paren(MatchPatParen<MatchPat>), |         Paren(MatchPatParen<MatchPat>), | ||||||
|         Struct(MatchPatStruct), |         Struct(MatchPatStruct), | ||||||
|         Tuple(MatchPatTuple), |  | ||||||
|         EnumVariant(MatchPatEnumVariant), |         EnumVariant(MatchPatEnumVariant), | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | @ -716,10 +545,6 @@ impl ParseMatchPat for MatchPat { | ||||||
|         Ok(Self::Struct(v)) |         Ok(Self::Struct(v)) | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     fn tuple(_state: &mut HdlMatchParseState<'_>, v: MatchPatTuple) -> Result<Self, ()> { |  | ||||||
|         Ok(Self::Tuple(v)) |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     fn enum_variant( |     fn enum_variant( | ||||||
|         _state: &mut HdlMatchParseState<'_>, |         _state: &mut HdlMatchParseState<'_>, | ||||||
|         v: MatchPatEnumVariant, |         v: MatchPatEnumVariant, | ||||||
|  | @ -735,7 +560,6 @@ impl ToTokens for MatchPat { | ||||||
|             Self::Or(v) => v.to_tokens(tokens), |             Self::Or(v) => v.to_tokens(tokens), | ||||||
|             Self::Paren(v) => v.to_tokens(tokens), |             Self::Paren(v) => v.to_tokens(tokens), | ||||||
|             Self::Struct(v) => v.to_tokens(tokens), |             Self::Struct(v) => v.to_tokens(tokens), | ||||||
|             Self::Tuple(v) => v.to_tokens(tokens), |  | ||||||
|             Self::EnumVariant(v) => v.to_tokens(tokens), |             Self::EnumVariant(v) => v.to_tokens(tokens), | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  | @ -798,6 +622,10 @@ struct RewriteAsCheckMatch { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl Fold for RewriteAsCheckMatch { | impl Fold for RewriteAsCheckMatch { | ||||||
|  |     fn fold_field_pat(&mut self, mut i: FieldPat) -> FieldPat { | ||||||
|  |         i.colon_token = Some(Token)); | ||||||
|  |         i | ||||||
|  |     } | ||||||
|     fn fold_pat(&mut self, pat: Pat) -> Pat { |     fn fold_pat(&mut self, pat: Pat) -> Pat { | ||||||
|         match pat { |         match pat { | ||||||
|             Pat::Ident(mut pat_ident) => match parse_enum_ident(pat_ident.ident) { |             Pat::Ident(mut pat_ident) => match parse_enum_ident(pat_ident.ident) { | ||||||
|  | @ -912,30 +740,6 @@ impl Fold for RewriteAsCheckMatch { | ||||||
|         // don't recurse into expressions
 |         // don't recurse into expressions
 | ||||||
|         i |         i | ||||||
|     } |     } | ||||||
|     fn fold_local(&mut self, mut let_stmt: Local) -> Local { |  | ||||||
|         if let Some(syn::LocalInit { |  | ||||||
|             eq_token, |  | ||||||
|             expr: _, |  | ||||||
|             diverge, |  | ||||||
|         }) = let_stmt.init.take() |  | ||||||
|         { |  | ||||||
|             let_stmt.init = Some(syn::LocalInit { |  | ||||||
|                 eq_token, |  | ||||||
|                 expr: parse_quote_spanned! {self.span=> |  | ||||||
|                     __match_value |  | ||||||
|                 }, |  | ||||||
|                 diverge: diverge.map(|(else_, _expr)| { |  | ||||||
|                     ( |  | ||||||
|                         else_, |  | ||||||
|                         parse_quote_spanned! {self.span=> |  | ||||||
|                             match __infallible {} |  | ||||||
|                         }, |  | ||||||
|                     ) |  | ||||||
|                 }), |  | ||||||
|             }); |  | ||||||
|         } |  | ||||||
|         fold_local(self, let_stmt) |  | ||||||
|     } |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| struct HdlMatchParseState<'a> { | struct HdlMatchParseState<'a> { | ||||||
|  | @ -943,123 +747,7 @@ struct HdlMatchParseState<'a> { | ||||||
|     errors: &'a mut Errors, |     errors: &'a mut Errors, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| struct HdlLetPatVisitState<'a> { |  | ||||||
|     errors: &'a mut Errors, |  | ||||||
|     bindings: BTreeSet<&'a Ident>, |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| impl<'a> VisitMatchPat<'a> for HdlLetPatVisitState<'a> { |  | ||||||
|     fn visit_match_pat_binding(&mut self, v: &'a MatchPatBinding) { |  | ||||||
|         self.bindings.insert(&v.ident); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     fn visit_match_pat_or(&mut self, v: &'a MatchPatOr<MatchPat>) { |  | ||||||
|         if let Some(first_inner_vert) = v.first_inner_vert() { |  | ||||||
|             self.errors.error( |  | ||||||
|                 first_inner_vert, |  | ||||||
|                 "or-patterns are not supported in let statements", |  | ||||||
|             ); |  | ||||||
|         } |  | ||||||
|         visit_match_pat_or(self, v); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     fn visit_match_pat_or_simple(&mut self, v: &'a MatchPatOr<MatchPatSimple>) { |  | ||||||
|         if let Some(first_inner_vert) = v.first_inner_vert() { |  | ||||||
|             self.errors.error( |  | ||||||
|                 first_inner_vert, |  | ||||||
|                 "or-patterns are not supported in let statements", |  | ||||||
|             ); |  | ||||||
|         } |  | ||||||
|         visit_match_pat_or_simple(self, v); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     fn visit_match_pat_enum_variant(&mut self, v: &'a MatchPatEnumVariant) { |  | ||||||
|         self.errors.error(v, "refutable pattern in let statement"); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| impl Visitor<'_> { | impl Visitor<'_> { | ||||||
|     pub(crate) fn process_hdl_let_pat( |  | ||||||
|         &mut self, |  | ||||||
|         _hdl_attr: HdlAttr<Nothing, kw::hdl>, |  | ||||||
|         mut let_stmt: Local, |  | ||||||
|     ) -> Local { |  | ||||||
|         let span = let_stmt.let_token.span(); |  | ||||||
|         if let Pat::Type(pat) = &mut let_stmt.pat { |  | ||||||
|             *pat.ty = wrap_ty_with_expr((*pat.ty).clone()); |  | ||||||
|         } |  | ||||||
|         let check_let_stmt = RewriteAsCheckMatch { span }.fold_local(let_stmt.clone()); |  | ||||||
|         let Local { |  | ||||||
|             attrs: _, |  | ||||||
|             let_token, |  | ||||||
|             pat, |  | ||||||
|             init, |  | ||||||
|             semi_token, |  | ||||||
|         } = let_stmt; |  | ||||||
|         self.require_normal_module_or_fn(let_token); |  | ||||||
|         let Some(syn::LocalInit { |  | ||||||
|             eq_token, |  | ||||||
|             expr, |  | ||||||
|             diverge, |  | ||||||
|         }) = init |  | ||||||
|         else { |  | ||||||
|             self.errors |  | ||||||
|                 .error(let_token, "#[hdl] let must be assigned a value"); |  | ||||||
|             return empty_let(); |  | ||||||
|         }; |  | ||||||
|         if let Some((else_, _)) = diverge { |  | ||||||
|             // TODO: implement let-else
 |  | ||||||
|             self.errors |  | ||||||
|                 .error(else_, "#[hdl] let ... else { ... } is not implemented"); |  | ||||||
|             return empty_let(); |  | ||||||
|         } |  | ||||||
|         let Ok(pat) = MatchPat::parse( |  | ||||||
|             &mut HdlMatchParseState { |  | ||||||
|                 match_span: span, |  | ||||||
|                 errors: &mut self.errors, |  | ||||||
|             }, |  | ||||||
|             pat, |  | ||||||
|         ) else { |  | ||||||
|             return empty_let(); |  | ||||||
|         }; |  | ||||||
|         let mut state = HdlLetPatVisitState { |  | ||||||
|             errors: &mut self.errors, |  | ||||||
|             bindings: BTreeSet::new(), |  | ||||||
|         }; |  | ||||||
|         state.visit_match_pat(&pat); |  | ||||||
|         let HdlLetPatVisitState { |  | ||||||
|             errors: _, |  | ||||||
|             bindings, |  | ||||||
|         } = state; |  | ||||||
|         let retval = parse_quote_spanned! {span=> |  | ||||||
|             let (#(#bindings,)* __scope,) = { |  | ||||||
|                 type __MatchTy<T> = <T as ::fayalite::ty::Type>::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 ::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, |  | ||||||
|             _ => unreachable!(), |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|     pub(crate) fn process_hdl_match( |     pub(crate) fn process_hdl_match( | ||||||
|         &mut self, |         &mut self, | ||||||
|         _hdl_attr: HdlAttr<Nothing, kw::hdl>, |         _hdl_attr: HdlAttr<Nothing, kw::hdl>, | ||||||
|  |  | ||||||
|  | @ -5,9 +5,6 @@ use std::{env, fs, path::Path}; | ||||||
| 
 | 
 | ||||||
| fn main() { | fn main() { | ||||||
|     println!("cargo::rustc-check-cfg=cfg(todo)"); |     println!("cargo::rustc-check-cfg=cfg(todo)"); | ||||||
|     println!("cargo::rustc-check-cfg=cfg(cfg_false_for_tests)"); |  | ||||||
|     println!("cargo::rustc-check-cfg=cfg(cfg_true_for_tests)"); |  | ||||||
|     println!("cargo::rustc-cfg=cfg_true_for_tests"); |  | ||||||
|     let path = "visit_types.json"; |     let path = "visit_types.json"; | ||||||
|     println!("cargo::rerun-if-changed={path}"); |     println!("cargo::rerun-if-changed={path}"); | ||||||
|     println!("cargo::rerun-if-changed=build.rs"); |     println!("cargo::rerun-if-changed=build.rs"); | ||||||
|  |  | ||||||
|  | @ -2,7 +2,6 @@ | ||||||
| // See Notices.txt for copyright information
 | // See Notices.txt for copyright information
 | ||||||
| //! ## `#[hdl] let` statements
 | //! ## `#[hdl] let` statements
 | ||||||
| 
 | 
 | ||||||
| pub mod destructuring; |  | ||||||
| pub mod inputs_outputs; | pub mod inputs_outputs; | ||||||
| pub mod instances; | pub mod instances; | ||||||
| pub mod memories; | pub mod memories; | ||||||
|  |  | ||||||
|  | @ -1,33 +0,0 @@ | ||||||
| // SPDX-License-Identifier: LGPL-3.0-or-later
 |  | ||||||
| // See Notices.txt for copyright information
 |  | ||||||
| //! ### Destructuring Let
 |  | ||||||
| //!
 |  | ||||||
| //! You can use `#[hdl] let` to destructure types, similarly to Rust `let` statements with non-trivial patterns.
 |  | ||||||
| //!
 |  | ||||||
| //! `#[hdl] let` statements can only match one level of struct/tuple pattern for now,
 |  | ||||||
| //! e.g. you can match with the pattern `MyStruct { a, b }`, but not `MyStruct { a, b: Struct2 { v } }`.
 |  | ||||||
| //!
 |  | ||||||
| //! ```
 |  | ||||||
| //! # use fayalite::prelude::*;
 |  | ||||||
| //! #[hdl]
 |  | ||||||
| //! struct MyStruct {
 |  | ||||||
| //!     a: UInt<8>,
 |  | ||||||
| //!     b: Bool,
 |  | ||||||
| //! }
 |  | ||||||
| //!
 |  | ||||||
| //! #[hdl_module]
 |  | ||||||
| //! fn my_module() {
 |  | ||||||
| //!     #[hdl]
 |  | ||||||
| //!     let my_input: MyStruct = m.input();
 |  | ||||||
| //!     #[hdl]
 |  | ||||||
| //!     let my_output: UInt<8> = m.input();
 |  | ||||||
| //!     #[hdl]
 |  | ||||||
| //!     let MyStruct { a, b } = my_input;
 |  | ||||||
| //!     #[hdl]
 |  | ||||||
| //!     if b {
 |  | ||||||
| //!         connect(my_output, a);
 |  | ||||||
| //!     } else {
 |  | ||||||
| //!         connect(my_output, 0_hdl_u8);
 |  | ||||||
| //!     }
 |  | ||||||
| //! }
 |  | ||||||
| //! ```
 |  | ||||||
|  | @ -7,5 +7,5 @@ | ||||||
| //!
 | //!
 | ||||||
| //! `#[hdl] match` statements' bodies must evaluate to type `()` for now.
 | //! `#[hdl] match` statements' bodies must evaluate to type `()` for now.
 | ||||||
| //!
 | //!
 | ||||||
| //! `#[hdl] match` statements can only match one level of struct/tuple/enum pattern for now,
 | //! `#[hdl] match` statements can only match one level of struct/enum pattern for now,
 | ||||||
| //! e.g. you can match with the pattern `HdlSome(v)`, but not `HdlSome(HdlSome(_))`.
 | //! e.g. you can match with the pattern `HdlSome(v)`, but not `HdlSome(HdlSome(_))`.
 | ||||||
|  |  | ||||||
|  | @ -567,12 +567,12 @@ impl_prim_int!(i64, SInt<64>); | ||||||
| impl_prim_int!(i128, SInt<128>); | impl_prim_int!(i128, SInt<128>); | ||||||
| 
 | 
 | ||||||
| impl_prim_int!( | impl_prim_int!( | ||||||
|     /// for portability reasons, [`usize`] always translates to [`UInt<64>`][type@UInt]
 |     /// for portability reasons, [`usize`] always translates to [`UInt<64>`]
 | ||||||
|     usize, UInt<64> |     usize, UInt<64> | ||||||
| ); | ); | ||||||
| 
 | 
 | ||||||
| impl_prim_int!( | impl_prim_int!( | ||||||
|     /// for portability reasons, [`isize`] always translates to [`SInt<64>`][type@SInt]
 |     /// for portability reasons, [`isize`] always translates to [`SInt<64>`]
 | ||||||
|     isize, SInt<64> |     isize, SInt<64> | ||||||
| ); | ); | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -22,8 +22,7 @@ macro_rules! __cfg_expansion_helper { | ||||||
|             $cfg:ident($($expr:tt)*), |             $cfg:ident($($expr:tt)*), | ||||||
|             $($unevaluated_cfgs:ident($($unevaluated_exprs:tt)*),)* |             $($unevaluated_cfgs:ident($($unevaluated_exprs:tt)*),)* | ||||||
|         ] |         ] | ||||||
|         // pass as tt so we get right span for attribute
 |         #[$after_evaluation:path:($($after_evaluation_attr_args:tt)*)] {$($after_evaluation_args:tt)*} | ||||||
|         $after_evaluation_attr:tt $after_evaluation_body:tt |  | ||||||
|     ) => { |     ) => { | ||||||
|         #[$cfg($($expr)*)] |         #[$cfg($($expr)*)] | ||||||
|         $crate::__cfg_expansion_helper! { |         $crate::__cfg_expansion_helper! { | ||||||
|  | @ -34,7 +33,7 @@ macro_rules! __cfg_expansion_helper { | ||||||
|             [ |             [ | ||||||
|                 $($unevaluated_cfgs($($unevaluated_exprs)*),)* |                 $($unevaluated_cfgs($($unevaluated_exprs)*),)* | ||||||
|             ] |             ] | ||||||
|             $after_evaluation_attr $after_evaluation_body |             #[$after_evaluation:($($after_evaluation_attr_args)*)] {$($after_evaluation_args)*} | ||||||
|         } |         } | ||||||
|         #[$cfg(not($($expr)*))] |         #[$cfg(not($($expr)*))] | ||||||
|         $crate::__cfg_expansion_helper! { |         $crate::__cfg_expansion_helper! { | ||||||
|  | @ -45,7 +44,7 @@ macro_rules! __cfg_expansion_helper { | ||||||
|             [ |             [ | ||||||
|                 $($unevaluated_cfgs($($unevaluated_exprs)*),)* |                 $($unevaluated_cfgs($($unevaluated_exprs)*),)* | ||||||
|             ] |             ] | ||||||
|             $after_evaluation_attr $after_evaluation_body |             #[$after_evaluation:($($after_evaluation_attr_args)*)] {$($after_evaluation_args)*} | ||||||
|         } |         } | ||||||
|     }; |     }; | ||||||
|     ( |     ( | ||||||
|  | @ -53,14 +52,12 @@ macro_rules! __cfg_expansion_helper { | ||||||
|             $($evaluated_cfgs:ident($($evaluated_exprs:tt)*) = $evaluated_results:ident,)* |             $($evaluated_cfgs:ident($($evaluated_exprs:tt)*) = $evaluated_results:ident,)* | ||||||
|         ] |         ] | ||||||
|         [] |         [] | ||||||
|         // don't use #[...] so we get right span for `#` and `[]` of attribute
 |         #[$after_evaluation:path:($($after_evaluation_attr_args:tt)*)] {$($after_evaluation_args:tt)*} | ||||||
|         {$($after_evaluation_attr:tt)*} {$($after_evaluation_body:tt)*} |  | ||||||
|     ) => { |     ) => { | ||||||
|         $($after_evaluation_attr)* |         #[$after_evaluation([ | ||||||
|         #[__evaluated_cfgs([ |  | ||||||
|             $($evaluated_cfgs($($evaluated_exprs)*) = $evaluated_results,)* |             $($evaluated_cfgs($($evaluated_exprs)*) = $evaluated_results,)* | ||||||
|         ])] |         ] $($after_evaluation_attr_args)*)] | ||||||
|         $($after_evaluation_body)* |         $($after_evaluation_args)* | ||||||
|     }; |     }; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -4773,9 +4773,6 @@ impl Compiler { | ||||||
|             } |             } | ||||||
|             self.insns.extend(insns.iter().copied(), *source_location); |             self.insns.extend(insns.iter().copied(), *source_location); | ||||||
|         } |         } | ||||||
|         for CondStackEntry { cond: _, end_label } in cond_stack { |  | ||||||
|             self.insns.define_label_at_next_insn(end_label); |  | ||||||
|         } |  | ||||||
|     } |     } | ||||||
|     fn process_clocks(&mut self) -> Interned<[StatePartIndex<StatePartKindSmallSlots>]> { |     fn process_clocks(&mut self) -> Interned<[StatePartIndex<StatePartKindSmallSlots>]> { | ||||||
|         mem::take(&mut self.clock_triggers) |         mem::take(&mut self.clock_triggers) | ||||||
|  |  | ||||||
|  | @ -5,7 +5,6 @@ use crate::{ | ||||||
|     enum_::{Enum, EnumType}, |     enum_::{Enum, EnumType}, | ||||||
|     expr::Flow, |     expr::Flow, | ||||||
|     int::UInt, |     int::UInt, | ||||||
|     intern::{Intern, Interned}, |  | ||||||
|     sim::{ |     sim::{ | ||||||
|         time::{SimDuration, SimInstant}, |         time::{SimDuration, SimInstant}, | ||||||
|         TraceArray, TraceAsyncReset, TraceBool, TraceBundle, TraceClock, TraceDecl, |         TraceArray, TraceAsyncReset, TraceBool, TraceBundle, TraceClock, TraceDecl, | ||||||
|  | @ -16,73 +15,12 @@ use crate::{ | ||||||
|     }, |     }, | ||||||
| }; | }; | ||||||
| use bitvec::{order::Lsb0, slice::BitSlice}; | use bitvec::{order::Lsb0, slice::BitSlice}; | ||||||
| use hashbrown::{hash_map::Entry, HashMap}; |  | ||||||
| use std::{ | use std::{ | ||||||
|     fmt::{self, Write as _}, |     fmt, | ||||||
|     io, mem, |     io::{self, Write}, | ||||||
|  |     mem, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| #[derive(Default)] |  | ||||||
| struct Scope { |  | ||||||
|     last_inserted: HashMap<Interned<str>, usize>, |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| #[derive(Copy, Clone)] |  | ||||||
| struct VerilogIdentifier { |  | ||||||
|     unescaped_name: Interned<str>, |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| impl VerilogIdentifier { |  | ||||||
|     fn needs_escape(self) -> bool { |  | ||||||
|         // we only allow ascii, so we can just check bytes
 |  | ||||||
|         let Some((&first, rest)) = self.unescaped_name.as_bytes().split_first() else { |  | ||||||
|             unreachable!("Scope::new_identifier guarantees a non-empty name"); |  | ||||||
|         }; |  | ||||||
|         if !first.is_ascii_alphabetic() && first != b'_' { |  | ||||||
|             true |  | ||||||
|         } else { |  | ||||||
|             rest.iter() |  | ||||||
|                 .any(|&ch| !ch.is_ascii_alphanumeric() && ch != b'_' && ch != b'$') |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| impl fmt::Display for VerilogIdentifier { |  | ||||||
|     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |  | ||||||
|         if self.needs_escape() { |  | ||||||
|             f.write_str("\\")?; |  | ||||||
|         } |  | ||||||
|         write!(f, "{}", Escaped(self.unescaped_name)) |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| impl Scope { |  | ||||||
|     fn new_identifier(&mut self, unescaped_name: Interned<str>) -> VerilogIdentifier { |  | ||||||
|         let next_disambiguator = match self.last_inserted.entry(unescaped_name) { |  | ||||||
|             Entry::Vacant(entry) => { |  | ||||||
|                 entry.insert(1); |  | ||||||
|                 return VerilogIdentifier { unescaped_name }; |  | ||||||
|             } |  | ||||||
|             Entry::Occupied(entry) => entry.get() + 1, |  | ||||||
|         }; |  | ||||||
|         let mut disambiguated_name = String::from(&*unescaped_name); |  | ||||||
|         for disambiguator in next_disambiguator.. { |  | ||||||
|             disambiguated_name.truncate(unescaped_name.len()); |  | ||||||
|             write!(disambiguated_name, "_{disambiguator}").expect("can't fail"); |  | ||||||
|             if let Entry::Vacant(entry) = self.last_inserted.entry((*disambiguated_name).intern()) { |  | ||||||
|                 let retval = VerilogIdentifier { |  | ||||||
|                     unescaped_name: *entry.key(), |  | ||||||
|                 }; |  | ||||||
|                 entry.insert(1); |  | ||||||
|                 // speed up future searches
 |  | ||||||
|                 self.last_inserted.insert(unescaped_name, disambiguator); |  | ||||||
|                 return retval; |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|         panic!("too many names"); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| pub struct VcdWriterDecls<W: io::Write + 'static> { | pub struct VcdWriterDecls<W: io::Write + 'static> { | ||||||
|     writer: W, |     writer: W, | ||||||
|     timescale: SimDuration, |     timescale: SimDuration, | ||||||
|  | @ -159,20 +97,14 @@ impl<W: io::Write> fmt::Debug for VcdWriterDecls<W> { | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /// pass in scope to ensure it's not available in child scope
 |  | ||||||
| fn write_vcd_scope<W: io::Write, R>( | fn write_vcd_scope<W: io::Write, R>( | ||||||
|     writer: &mut W, |     writer: &mut W, | ||||||
|     scope_type: &str, |     scope_type: &str, | ||||||
|     scope_name: Interned<str>, |     scope_name: &str, | ||||||
|     scope: &mut Scope, |     f: impl FnOnce(&mut W) -> io::Result<R>, | ||||||
|     f: impl FnOnce(&mut W, &mut Scope) -> io::Result<R>, |  | ||||||
| ) -> io::Result<R> { | ) -> io::Result<R> { | ||||||
|     writeln!( |     writeln!(writer, "$scope {scope_type} {scope_name} $end")?; | ||||||
|         writer, |     let retval = f(writer)?; | ||||||
|         "$scope {scope_type} {} $end", |  | ||||||
|         scope.new_identifier(scope_name), |  | ||||||
|     )?; |  | ||||||
|     let retval = f(writer, &mut Scope::default())?; |  | ||||||
|     writeln!(writer, "$upscope $end")?; |     writeln!(writer, "$upscope $end")?; | ||||||
|     Ok(retval) |     Ok(retval) | ||||||
| } | } | ||||||
|  | @ -211,28 +143,24 @@ trait_arg! { | ||||||
| 
 | 
 | ||||||
| struct ArgModule<'a> { | struct ArgModule<'a> { | ||||||
|     properties: &'a mut VcdWriterProperties, |     properties: &'a mut VcdWriterProperties, | ||||||
|     scope: &'a mut Scope, |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl<'a> ArgModule<'a> { | impl<'a> ArgModule<'a> { | ||||||
|     fn reborrow(&mut self) -> ArgModule<'_> { |     fn reborrow(&mut self) -> ArgModule<'_> { | ||||||
|         ArgModule { |         ArgModule { | ||||||
|             properties: self.properties, |             properties: self.properties, | ||||||
|             scope: self.scope, |  | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| struct ArgModuleBody<'a> { | struct ArgModuleBody<'a> { | ||||||
|     properties: &'a mut VcdWriterProperties, |     properties: &'a mut VcdWriterProperties, | ||||||
|     scope: &'a mut Scope, |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl<'a> ArgModuleBody<'a> { | impl<'a> ArgModuleBody<'a> { | ||||||
|     fn reborrow(&mut self) -> ArgModuleBody<'_> { |     fn reborrow(&mut self) -> ArgModuleBody<'_> { | ||||||
|         ArgModuleBody { |         ArgModuleBody { | ||||||
|             properties: self.properties, |             properties: self.properties, | ||||||
|             scope: self.scope, |  | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | @ -242,7 +170,6 @@ struct ArgInType<'a> { | ||||||
|     sink_var_type: &'static str, |     sink_var_type: &'static str, | ||||||
|     duplex_var_type: &'static str, |     duplex_var_type: &'static str, | ||||||
|     properties: &'a mut VcdWriterProperties, |     properties: &'a mut VcdWriterProperties, | ||||||
|     scope: &'a mut Scope, |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl<'a> ArgInType<'a> { | impl<'a> ArgInType<'a> { | ||||||
|  | @ -252,7 +179,6 @@ impl<'a> ArgInType<'a> { | ||||||
|             sink_var_type: self.sink_var_type, |             sink_var_type: self.sink_var_type, | ||||||
|             duplex_var_type: self.duplex_var_type, |             duplex_var_type: self.duplex_var_type, | ||||||
|             properties: self.properties, |             properties: self.properties, | ||||||
|             scope: self.scope, |  | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | @ -300,42 +226,55 @@ fn write_vcd_id<W: io::Write>(writer: &mut W, mut id: usize) -> io::Result<()> { | ||||||
|     Ok(()) |     Ok(()) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| struct Escaped<T: fmt::Display>(T); | fn write_escaped<W: io::Write>(writer: &mut W, value: impl fmt::Display) -> io::Result<()> { | ||||||
| 
 |     // escaping rules from function GTKWave uses to decode VCD strings:
 | ||||||
| impl<T: fmt::Display> fmt::Display for Escaped<T> { |     // https://github.com/gtkwave/gtkwave/blob/491f24d7e8619cfc1fcc65704ee5c967d1083c18/lib/libfst/fstapi.c#L7090
 | ||||||
|     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |     struct Wrapper<W>(W); | ||||||
|         // escaping rules from function GTKWave uses to decode VCD strings:
 |     impl<W: io::Write> io::Write for Wrapper<W> { | ||||||
|         // https://github.com/gtkwave/gtkwave/blob/491f24d7e8619cfc1fcc65704ee5c967d1083c18/lib/libfst/fstapi.c#L7090
 |         fn write(&mut self, buf: &[u8]) -> io::Result<usize> { | ||||||
|         struct Wrapper<W>(W); |             if buf.is_empty() { | ||||||
|         impl<W: fmt::Write> fmt::Write for Wrapper<W> { |                 return self.0.write(buf); | ||||||
|             fn write_str(&mut self, s: &str) -> fmt::Result { |             } | ||||||
|                 for byte in s.bytes() { |             let mut retval = 0; | ||||||
|                     match byte { |             for &byte in buf { | ||||||
|                         b'\\' | b'\'' | b'"' | b'?' => { |                 match byte { | ||||||
|                             self.0.write_str("\\")?; |                     b'\\' | b'\'' | b'"' | b'?' => self.0.write_all(&[b'\\', byte])?, | ||||||
|                             self.0.write_char(byte as char)?; |                     b'\n' => self.0.write_all(br"\n")?, | ||||||
|                         } |                     b'\r' => self.0.write_all(br"\r")?, | ||||||
|                         b'\n' => self.0.write_str(r"\n")?, |                     b'\t' => self.0.write_all(br"\t")?, | ||||||
|                         b'\r' => self.0.write_str(r"\r")?, |                     0x7 => self.0.write_all(br"\a")?, | ||||||
|                         b'\t' => self.0.write_str(r"\t")?, |                     0x8 => self.0.write_all(br"\b")?, | ||||||
|                         0x7 => self.0.write_str(r"\a")?, |                     0xC => self.0.write_all(br"\f")?, | ||||||
|                         0x8 => self.0.write_str(r"\b")?, |                     0xB => self.0.write_all(br"\v")?, | ||||||
|                         0xC => self.0.write_str(r"\f")?, |                     _ => { | ||||||
|                         0xB => self.0.write_str(r"\v")?, |                         if byte.is_ascii_graphic() { | ||||||
|                         _ => { |                             self.0.write_all(&[byte])?; | ||||||
|                             if byte.is_ascii_graphic() { |                         } else { | ||||||
|                                 self.0.write_char(byte as char)?; |                             write!(self.0, r"\x{byte:02x}")?; | ||||||
|                             } else { |  | ||||||
|                                 write!(self.0, r"\x{byte:02x}")?; |  | ||||||
|                             } |  | ||||||
|                         } |                         } | ||||||
|                     } |                     } | ||||||
|                 } |                 } | ||||||
|                 Ok(()) |                 retval += 1; | ||||||
|             } |             } | ||||||
|  |             Ok(retval) | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         fn flush(&mut self) -> io::Result<()> { | ||||||
|  |             self.0.flush() | ||||||
|         } |         } | ||||||
|         write!(Wrapper(f), "{}", self.0) |  | ||||||
|     } |     } | ||||||
|  |     write!(Wrapper(writer), "{value}") | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | fn is_unescaped_verilog_identifier(ident: &str) -> bool { | ||||||
|  |     // we only allow ascii, so we can just check bytes
 | ||||||
|  |     let Some((&first, rest)) = ident.as_bytes().split_first() else { | ||||||
|  |         return false; // empty string is not an identifier
 | ||||||
|  |     }; | ||||||
|  |     (first.is_ascii_alphabetic() || first == b'_') | ||||||
|  |         && rest | ||||||
|  |             .iter() | ||||||
|  |             .all(|&ch| ch.is_ascii_alphanumeric() || ch == b'_' || ch == b'$') | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| fn write_vcd_var<W: io::Write>( | fn write_vcd_var<W: io::Write>( | ||||||
|  | @ -345,7 +284,7 @@ fn write_vcd_var<W: io::Write>( | ||||||
|     var_type: &str, |     var_type: &str, | ||||||
|     size: usize, |     size: usize, | ||||||
|     location: TraceLocation, |     location: TraceLocation, | ||||||
|     name: VerilogIdentifier, |     name: &str, | ||||||
| ) -> io::Result<()> { | ) -> io::Result<()> { | ||||||
|     let id = match location { |     let id = match location { | ||||||
|         TraceLocation::Scalar(id) => id.as_usize(), |         TraceLocation::Scalar(id) => id.as_usize(), | ||||||
|  | @ -380,7 +319,12 @@ fn write_vcd_var<W: io::Write>( | ||||||
|     }; |     }; | ||||||
|     write!(writer, "$var {var_type} {size} ")?; |     write!(writer, "$var {var_type} {size} ")?; | ||||||
|     write_vcd_id(writer, id)?; |     write_vcd_id(writer, id)?; | ||||||
|     writeln!(writer, " {name} $end") |     writer.write_all(b" ")?; | ||||||
|  |     if !is_unescaped_verilog_identifier(name) { | ||||||
|  |         writer.write_all(b"\\")?; | ||||||
|  |     } | ||||||
|  |     write_escaped(writer, name)?; | ||||||
|  |     writer.write_all(b" $end\n") | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl WriteTrace for TraceUInt { | impl WriteTrace for TraceUInt { | ||||||
|  | @ -390,7 +334,6 @@ impl WriteTrace for TraceUInt { | ||||||
|             sink_var_type, |             sink_var_type, | ||||||
|             duplex_var_type, |             duplex_var_type, | ||||||
|             properties, |             properties, | ||||||
|             scope, |  | ||||||
|         } = arg.in_type(); |         } = arg.in_type(); | ||||||
|         let Self { |         let Self { | ||||||
|             location, |             location, | ||||||
|  | @ -413,7 +356,7 @@ impl WriteTrace for TraceUInt { | ||||||
|             var_type, |             var_type, | ||||||
|             ty.width(), |             ty.width(), | ||||||
|             location, |             location, | ||||||
|             scope.new_identifier(name), |             &name, | ||||||
|         ) |         ) | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | @ -478,7 +421,6 @@ impl WriteTrace for TraceEnumDiscriminant { | ||||||
|             sink_var_type: _, |             sink_var_type: _, | ||||||
|             duplex_var_type: _, |             duplex_var_type: _, | ||||||
|             properties, |             properties, | ||||||
|             scope, |  | ||||||
|         } = arg.in_type(); |         } = arg.in_type(); | ||||||
|         let Self { |         let Self { | ||||||
|             location, |             location, | ||||||
|  | @ -493,7 +435,7 @@ impl WriteTrace for TraceEnumDiscriminant { | ||||||
|             "string", |             "string", | ||||||
|             1, |             1, | ||||||
|             location, |             location, | ||||||
|             scope.new_identifier(name), |             &name, | ||||||
|         ) |         ) | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | @ -565,11 +507,11 @@ impl WriteTrace for TraceScope { | ||||||
| 
 | 
 | ||||||
| impl WriteTrace for TraceModule { | impl WriteTrace for TraceModule { | ||||||
|     fn write_trace<W: io::Write, A: Arg>(self, writer: &mut W, mut arg: A) -> io::Result<()> { |     fn write_trace<W: io::Write, A: Arg>(self, writer: &mut W, mut arg: A) -> io::Result<()> { | ||||||
|         let ArgModule { properties, scope } = arg.module(); |         let ArgModule { properties } = arg.module(); | ||||||
|         let Self { name, children } = self; |         let Self { name, children } = self; | ||||||
|         write_vcd_scope(writer, "module", name, scope, |writer, scope| { |         write_vcd_scope(writer, "module", &name, |writer| { | ||||||
|             for child in children { |             for child in children { | ||||||
|                 child.write_trace(writer, ArgModuleBody { properties, scope })?; |                 child.write_trace(writer, ArgModuleBody { properties })?; | ||||||
|             } |             } | ||||||
|             Ok(()) |             Ok(()) | ||||||
|         }) |         }) | ||||||
|  | @ -578,7 +520,7 @@ impl WriteTrace for TraceModule { | ||||||
| 
 | 
 | ||||||
| impl WriteTrace for TraceInstance { | impl WriteTrace for TraceInstance { | ||||||
|     fn write_trace<W: io::Write, A: Arg>(self, writer: &mut W, mut arg: A) -> io::Result<()> { |     fn write_trace<W: io::Write, A: Arg>(self, writer: &mut W, mut arg: A) -> io::Result<()> { | ||||||
|         let ArgModuleBody { properties, scope } = arg.module_body(); |         let ArgModuleBody { properties } = arg.module_body(); | ||||||
|         let Self { |         let Self { | ||||||
|             name: _, |             name: _, | ||||||
|             instance_io, |             instance_io, | ||||||
|  | @ -592,16 +534,15 @@ impl WriteTrace for TraceInstance { | ||||||
|                 sink_var_type: "wire", |                 sink_var_type: "wire", | ||||||
|                 duplex_var_type: "wire", |                 duplex_var_type: "wire", | ||||||
|                 properties, |                 properties, | ||||||
|                 scope, |  | ||||||
|             }, |             }, | ||||||
|         )?; |         )?; | ||||||
|         module.write_trace(writer, ArgModule { properties, scope }) |         module.write_trace(writer, ArgModule { properties }) | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl WriteTrace for TraceMem { | impl WriteTrace for TraceMem { | ||||||
|     fn write_trace<W: io::Write, A: Arg>(self, writer: &mut W, mut arg: A) -> io::Result<()> { |     fn write_trace<W: io::Write, A: Arg>(self, writer: &mut W, mut arg: A) -> io::Result<()> { | ||||||
|         let ArgModuleBody { properties, scope } = arg.module_body(); |         let ArgModuleBody { properties } = arg.module_body(); | ||||||
|         let Self { |         let Self { | ||||||
|             id, |             id, | ||||||
|             name, |             name, | ||||||
|  | @ -610,41 +551,27 @@ impl WriteTrace for TraceMem { | ||||||
|             ports, |             ports, | ||||||
|             array_type, |             array_type, | ||||||
|         } = self; |         } = self; | ||||||
|         write_vcd_scope(writer, "struct", name, scope, |writer, scope| { |         write_vcd_scope(writer, "struct", &*name, |writer| { | ||||||
|             write_vcd_scope( |             write_vcd_scope(writer, "struct", "contents", |writer| { | ||||||
|                 writer, |                 for element_index in 0..array_type.len() { | ||||||
|                 "struct", |                     write_vcd_scope(writer, "struct", &format!("[{element_index}]"), |writer| { | ||||||
|                 "contents".intern(), |                         properties.memory_properties[id.as_usize()].element_index = element_index; | ||||||
|                 scope, |                         properties.memory_properties[id.as_usize()].element_part_index = 0; | ||||||
|                 |writer, scope| { |                         element_type.write_trace( | ||||||
|                     for element_index in 0..array_type.len() { |  | ||||||
|                         write_vcd_scope( |  | ||||||
|                             writer, |                             writer, | ||||||
|                             "struct", |                             ArgInType { | ||||||
|                             Intern::intern_owned(format!("[{element_index}]")), |                                 source_var_type: "reg", | ||||||
|                             scope, |                                 sink_var_type: "reg", | ||||||
|                             |writer, scope| { |                                 duplex_var_type: "reg", | ||||||
|                                 properties.memory_properties[id.as_usize()].element_index = |                                 properties, | ||||||
|                                     element_index; |  | ||||||
|                                 properties.memory_properties[id.as_usize()].element_part_index = 0; |  | ||||||
|                                 element_type.write_trace( |  | ||||||
|                                     writer, |  | ||||||
|                                     ArgInType { |  | ||||||
|                                         source_var_type: "reg", |  | ||||||
|                                         sink_var_type: "reg", |  | ||||||
|                                         duplex_var_type: "reg", |  | ||||||
|                                         properties, |  | ||||||
|                                         scope, |  | ||||||
|                                     }, |  | ||||||
|                                 ) |  | ||||||
|                             }, |                             }, | ||||||
|                         )?; |                         ) | ||||||
|                     } |                     })?; | ||||||
|                     Ok(()) |                 } | ||||||
|                 }, |                 Ok(()) | ||||||
|             )?; |             })?; | ||||||
|             for port in ports { |             for port in ports { | ||||||
|                 port.write_trace(writer, ArgModuleBody { properties, scope })?; |                 port.write_trace(writer, ArgModuleBody { properties })?; | ||||||
|             } |             } | ||||||
|             Ok(()) |             Ok(()) | ||||||
|         }) |         }) | ||||||
|  | @ -653,7 +580,7 @@ impl WriteTrace for TraceMem { | ||||||
| 
 | 
 | ||||||
| impl WriteTrace for TraceMemPort { | impl WriteTrace for TraceMemPort { | ||||||
|     fn write_trace<W: io::Write, A: Arg>(self, writer: &mut W, mut arg: A) -> io::Result<()> { |     fn write_trace<W: io::Write, A: Arg>(self, writer: &mut W, mut arg: A) -> io::Result<()> { | ||||||
|         let ArgModuleBody { properties, scope } = arg.module_body(); |         let ArgModuleBody { properties } = arg.module_body(); | ||||||
|         let Self { |         let Self { | ||||||
|             name: _, |             name: _, | ||||||
|             bundle, |             bundle, | ||||||
|  | @ -666,7 +593,6 @@ impl WriteTrace for TraceMemPort { | ||||||
|                 sink_var_type: "wire", |                 sink_var_type: "wire", | ||||||
|                 duplex_var_type: "wire", |                 duplex_var_type: "wire", | ||||||
|                 properties, |                 properties, | ||||||
|                 scope, |  | ||||||
|             }, |             }, | ||||||
|         ) |         ) | ||||||
|     } |     } | ||||||
|  | @ -674,7 +600,7 @@ impl WriteTrace for TraceMemPort { | ||||||
| 
 | 
 | ||||||
| impl WriteTrace for TraceWire { | impl WriteTrace for TraceWire { | ||||||
|     fn write_trace<W: io::Write, A: Arg>(self, writer: &mut W, mut arg: A) -> io::Result<()> { |     fn write_trace<W: io::Write, A: Arg>(self, writer: &mut W, mut arg: A) -> io::Result<()> { | ||||||
|         let ArgModuleBody { properties, scope } = arg.module_body(); |         let ArgModuleBody { properties } = arg.module_body(); | ||||||
|         let Self { |         let Self { | ||||||
|             name: _, |             name: _, | ||||||
|             child, |             child, | ||||||
|  | @ -687,7 +613,6 @@ impl WriteTrace for TraceWire { | ||||||
|                 sink_var_type: "wire", |                 sink_var_type: "wire", | ||||||
|                 duplex_var_type: "wire", |                 duplex_var_type: "wire", | ||||||
|                 properties, |                 properties, | ||||||
|                 scope, |  | ||||||
|             }, |             }, | ||||||
|         ) |         ) | ||||||
|     } |     } | ||||||
|  | @ -695,7 +620,7 @@ impl WriteTrace for TraceWire { | ||||||
| 
 | 
 | ||||||
| impl WriteTrace for TraceReg { | impl WriteTrace for TraceReg { | ||||||
|     fn write_trace<W: io::Write, A: Arg>(self, writer: &mut W, mut arg: A) -> io::Result<()> { |     fn write_trace<W: io::Write, A: Arg>(self, writer: &mut W, mut arg: A) -> io::Result<()> { | ||||||
|         let ArgModuleBody { properties, scope } = arg.module_body(); |         let ArgModuleBody { properties } = arg.module_body(); | ||||||
|         let Self { |         let Self { | ||||||
|             name: _, |             name: _, | ||||||
|             child, |             child, | ||||||
|  | @ -708,7 +633,6 @@ impl WriteTrace for TraceReg { | ||||||
|                 sink_var_type: "reg", |                 sink_var_type: "reg", | ||||||
|                 duplex_var_type: "reg", |                 duplex_var_type: "reg", | ||||||
|                 properties, |                 properties, | ||||||
|                 scope, |  | ||||||
|             }, |             }, | ||||||
|         ) |         ) | ||||||
|     } |     } | ||||||
|  | @ -716,7 +640,7 @@ impl WriteTrace for TraceReg { | ||||||
| 
 | 
 | ||||||
| impl WriteTrace for TraceModuleIO { | impl WriteTrace for TraceModuleIO { | ||||||
|     fn write_trace<W: io::Write, A: Arg>(self, writer: &mut W, mut arg: A) -> io::Result<()> { |     fn write_trace<W: io::Write, A: Arg>(self, writer: &mut W, mut arg: A) -> io::Result<()> { | ||||||
|         let ArgModuleBody { properties, scope } = arg.module_body(); |         let ArgModuleBody { properties } = arg.module_body(); | ||||||
|         let Self { |         let Self { | ||||||
|             name: _, |             name: _, | ||||||
|             child, |             child, | ||||||
|  | @ -730,7 +654,6 @@ impl WriteTrace for TraceModuleIO { | ||||||
|                 sink_var_type: "wire", |                 sink_var_type: "wire", | ||||||
|                 duplex_var_type: "wire", |                 duplex_var_type: "wire", | ||||||
|                 properties, |                 properties, | ||||||
|                 scope, |  | ||||||
|             }, |             }, | ||||||
|         ) |         ) | ||||||
|     } |     } | ||||||
|  | @ -738,31 +661,16 @@ impl WriteTrace for TraceModuleIO { | ||||||
| 
 | 
 | ||||||
| impl WriteTrace for TraceBundle { | impl WriteTrace for TraceBundle { | ||||||
|     fn write_trace<W: io::Write, A: Arg>(self, writer: &mut W, mut arg: A) -> io::Result<()> { |     fn write_trace<W: io::Write, A: Arg>(self, writer: &mut W, mut arg: A) -> io::Result<()> { | ||||||
|         let ArgInType { |         let mut arg = arg.in_type(); | ||||||
|             source_var_type, |  | ||||||
|             sink_var_type, |  | ||||||
|             duplex_var_type, |  | ||||||
|             properties, |  | ||||||
|             scope, |  | ||||||
|         } = arg.in_type(); |  | ||||||
|         let Self { |         let Self { | ||||||
|             name, |             name, | ||||||
|             fields, |             fields, | ||||||
|             ty: _, |             ty: _, | ||||||
|             flow: _, |             flow: _, | ||||||
|         } = self; |         } = self; | ||||||
|         write_vcd_scope(writer, "struct", name, scope, |writer, scope| { |         write_vcd_scope(writer, "struct", &name, |writer| { | ||||||
|             for field in fields { |             for field in fields { | ||||||
|                 field.write_trace( |                 field.write_trace(writer, arg.reborrow())?; | ||||||
|                     writer, |  | ||||||
|                     ArgInType { |  | ||||||
|                         source_var_type, |  | ||||||
|                         sink_var_type, |  | ||||||
|                         duplex_var_type, |  | ||||||
|                         properties, |  | ||||||
|                         scope, |  | ||||||
|                     }, |  | ||||||
|                 )?; |  | ||||||
|             } |             } | ||||||
|             Ok(()) |             Ok(()) | ||||||
|         }) |         }) | ||||||
|  | @ -771,31 +679,16 @@ impl WriteTrace for TraceBundle { | ||||||
| 
 | 
 | ||||||
| impl WriteTrace for TraceArray { | impl WriteTrace for TraceArray { | ||||||
|     fn write_trace<W: io::Write, A: Arg>(self, writer: &mut W, mut arg: A) -> io::Result<()> { |     fn write_trace<W: io::Write, A: Arg>(self, writer: &mut W, mut arg: A) -> io::Result<()> { | ||||||
|         let ArgInType { |         let mut arg = arg.in_type(); | ||||||
|             source_var_type, |  | ||||||
|             sink_var_type, |  | ||||||
|             duplex_var_type, |  | ||||||
|             properties, |  | ||||||
|             scope, |  | ||||||
|         } = arg.in_type(); |  | ||||||
|         let Self { |         let Self { | ||||||
|             name, |             name, | ||||||
|             elements, |             elements, | ||||||
|             ty: _, |             ty: _, | ||||||
|             flow: _, |             flow: _, | ||||||
|         } = self; |         } = self; | ||||||
|         write_vcd_scope(writer, "struct", name, scope, |writer, scope| { |         write_vcd_scope(writer, "struct", &name, |writer| { | ||||||
|             for element in elements { |             for element in elements { | ||||||
|                 element.write_trace( |                 element.write_trace(writer, arg.reborrow())?; | ||||||
|                     writer, |  | ||||||
|                     ArgInType { |  | ||||||
|                         source_var_type, |  | ||||||
|                         sink_var_type, |  | ||||||
|                         duplex_var_type, |  | ||||||
|                         properties, |  | ||||||
|                         scope, |  | ||||||
|                     }, |  | ||||||
|                 )?; |  | ||||||
|             } |             } | ||||||
|             Ok(()) |             Ok(()) | ||||||
|         }) |         }) | ||||||
|  | @ -804,13 +697,7 @@ impl WriteTrace for TraceArray { | ||||||
| 
 | 
 | ||||||
| impl WriteTrace for TraceEnumWithFields { | impl WriteTrace for TraceEnumWithFields { | ||||||
|     fn write_trace<W: io::Write, A: Arg>(self, writer: &mut W, mut arg: A) -> io::Result<()> { |     fn write_trace<W: io::Write, A: Arg>(self, writer: &mut W, mut arg: A) -> io::Result<()> { | ||||||
|         let ArgInType { |         let mut arg = arg.in_type(); | ||||||
|             source_var_type, |  | ||||||
|             sink_var_type, |  | ||||||
|             duplex_var_type, |  | ||||||
|             properties, |  | ||||||
|             scope, |  | ||||||
|         } = arg.in_type(); |  | ||||||
|         let Self { |         let Self { | ||||||
|             name, |             name, | ||||||
|             discriminant, |             discriminant, | ||||||
|  | @ -818,28 +705,10 @@ impl WriteTrace for TraceEnumWithFields { | ||||||
|             ty: _, |             ty: _, | ||||||
|             flow: _, |             flow: _, | ||||||
|         } = self; |         } = self; | ||||||
|         write_vcd_scope(writer, "struct", name, scope, |writer, scope| { |         write_vcd_scope(writer, "struct", &name, |writer| { | ||||||
|             discriminant.write_trace( |             discriminant.write_trace(writer, arg.reborrow())?; | ||||||
|                 writer, |  | ||||||
|                 ArgInType { |  | ||||||
|                     source_var_type, |  | ||||||
|                     sink_var_type, |  | ||||||
|                     duplex_var_type, |  | ||||||
|                     properties, |  | ||||||
|                     scope, |  | ||||||
|                 }, |  | ||||||
|             )?; |  | ||||||
|             for field in non_empty_fields { |             for field in non_empty_fields { | ||||||
|                 field.write_trace( |                 field.write_trace(writer, arg.reborrow())?; | ||||||
|                     writer, |  | ||||||
|                     ArgInType { |  | ||||||
|                         source_var_type, |  | ||||||
|                         sink_var_type, |  | ||||||
|                         duplex_var_type, |  | ||||||
|                         properties, |  | ||||||
|                         scope, |  | ||||||
|                     }, |  | ||||||
|                 )?; |  | ||||||
|             } |             } | ||||||
|             Ok(()) |             Ok(()) | ||||||
|         }) |         }) | ||||||
|  | @ -875,7 +744,6 @@ impl<W: io::Write> TraceWriterDecls for VcdWriterDecls<W> { | ||||||
|             &mut writer, |             &mut writer, | ||||||
|             ArgModule { |             ArgModule { | ||||||
|                 properties: &mut properties, |                 properties: &mut properties, | ||||||
|                 scope: &mut Scope::default(), |  | ||||||
|             }, |             }, | ||||||
|         )?; |         )?; | ||||||
|         writeln!(writer, "$enddefinitions $end")?; |         writeln!(writer, "$enddefinitions $end")?; | ||||||
|  | @ -930,7 +798,9 @@ fn write_string_value_change( | ||||||
|     value: impl fmt::Display, |     value: impl fmt::Display, | ||||||
|     id: usize, |     id: usize, | ||||||
| ) -> io::Result<()> { | ) -> io::Result<()> { | ||||||
|     write!(writer, "s{} ", Escaped(value))?; |     writer.write_all(b"s")?; | ||||||
|  |     write_escaped(writer, value)?; | ||||||
|  |     writer.write_all(b" ")?; | ||||||
|     write_vcd_id(writer, id)?; |     write_vcd_id(writer, id)?; | ||||||
|     writer.write_all(b"\n") |     writer.write_all(b"\n") | ||||||
| } | } | ||||||
|  | @ -1076,49 +946,3 @@ impl<W: io::Write> fmt::Debug for VcdWriter<W> { | ||||||
|             .finish_non_exhaustive() |             .finish_non_exhaustive() | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 |  | ||||||
| #[cfg(test)] |  | ||||||
| mod tests { |  | ||||||
|     use super::*; |  | ||||||
| 
 |  | ||||||
|     #[test] |  | ||||||
|     fn test_scope() { |  | ||||||
|         let mut scope = Scope::default(); |  | ||||||
|         assert_eq!(&*scope.new_identifier("foo".intern()).unescaped_name, "foo"); |  | ||||||
|         assert_eq!( |  | ||||||
|             &*scope.new_identifier("foo_0".intern()).unescaped_name, |  | ||||||
|             "foo_0" |  | ||||||
|         ); |  | ||||||
|         assert_eq!( |  | ||||||
|             &*scope.new_identifier("foo_1".intern()).unescaped_name, |  | ||||||
|             "foo_1" |  | ||||||
|         ); |  | ||||||
|         assert_eq!( |  | ||||||
|             &*scope.new_identifier("foo_3".intern()).unescaped_name, |  | ||||||
|             "foo_3" |  | ||||||
|         ); |  | ||||||
|         assert_eq!( |  | ||||||
|             &*scope.new_identifier("foo".intern()).unescaped_name, |  | ||||||
|             "foo_2" |  | ||||||
|         ); |  | ||||||
|         assert_eq!( |  | ||||||
|             &*scope.new_identifier("foo".intern()).unescaped_name, |  | ||||||
|             "foo_4" |  | ||||||
|         ); |  | ||||||
|         assert_eq!( |  | ||||||
|             &*scope.new_identifier("foo_0".intern()).unescaped_name, |  | ||||||
|             "foo_0_2" |  | ||||||
|         ); |  | ||||||
|         assert_eq!( |  | ||||||
|             &*scope.new_identifier("foo_1".intern()).unescaped_name, |  | ||||||
|             "foo_1_2" |  | ||||||
|         ); |  | ||||||
|         for i in 5..1000u64 { |  | ||||||
|             // verify it actually picks the next available identifier with no skips or duplicates
 |  | ||||||
|             assert_eq!( |  | ||||||
|                 *scope.new_identifier("foo".intern()).unescaped_name, |  | ||||||
|                 format!("foo_{i}"), |  | ||||||
|             ); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
|  | @ -49,18 +49,6 @@ impl<T: Type> ReadyValid<T> { | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /// This debug port is only meant to assist the formal proof of the queue.
 |  | ||||||
| #[cfg(test)] |  | ||||||
| #[doc(hidden)] |  | ||||||
| #[hdl] |  | ||||||
| pub struct QueueDebugPort<Element, Index> { |  | ||||||
|     #[hdl(flip)] |  | ||||||
|     index_to_check: Index, |  | ||||||
|     stored: Element, |  | ||||||
|     inp_index: Index, |  | ||||||
|     out_index: Index, |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| #[hdl_module] | #[hdl_module] | ||||||
| pub fn queue<T: Type>( | pub fn queue<T: Type>( | ||||||
|     ty: T, |     ty: T, | ||||||
|  | @ -190,22 +178,6 @@ pub fn queue<T: Type>( | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|     // These debug ports expose some internal state during the Induction phase
 |  | ||||||
|     // of Formal Verification. They are not present in normal use.
 |  | ||||||
|     #[cfg(test)] |  | ||||||
|     { |  | ||||||
|         #[hdl] |  | ||||||
|         let dbg: QueueDebugPort<T, UInt> = m.output(QueueDebugPort[ty][index_ty]); |  | ||||||
|         // read the memory word currently stored at some fixed index
 |  | ||||||
|         let debug_port = mem.new_read_port(); |  | ||||||
|         connect(debug_port.addr, dbg.index_to_check); |  | ||||||
|         connect(debug_port.en, true); |  | ||||||
|         connect(debug_port.clk, cd.clk); |  | ||||||
|         connect(dbg.stored, debug_port.data); |  | ||||||
|         // also expose the current read and write indices
 |  | ||||||
|         connect(dbg.inp_index, inp_index_reg); |  | ||||||
|         connect(dbg.out_index, out_index_reg); |  | ||||||
|     } |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #[cfg(test)] | #[cfg(test)] | ||||||
|  | @ -224,23 +196,13 @@ mod tests { | ||||||
|             format_args!("test_queue_{capacity}_{inp_ready_is_comb}_{out_valid_is_comb}"), |             format_args!("test_queue_{capacity}_{inp_ready_is_comb}_{out_valid_is_comb}"), | ||||||
|             queue_test(capacity, inp_ready_is_comb, out_valid_is_comb), |             queue_test(capacity, inp_ready_is_comb, out_valid_is_comb), | ||||||
|             FormalMode::Prove, |             FormalMode::Prove, | ||||||
|             2, |             14, | ||||||
|             None, |             None, | ||||||
|             ExportOptions { |             ExportOptions { | ||||||
|                 simplify_enums: Some(SimplifyEnumsKind::ReplaceWithBundleOfUInts), |                 simplify_enums: Some(SimplifyEnumsKind::ReplaceWithBundleOfUInts), | ||||||
|                 ..ExportOptions::default() |                 ..ExportOptions::default() | ||||||
|             }, |             }, | ||||||
|         ); |         ); | ||||||
|         /// Formal verification of the FIFO queue
 |  | ||||||
|         ///
 |  | ||||||
|         /// The strategy derives from the observation that, if we filter its
 |  | ||||||
|         /// input and output streams to consider just one in every N reads and
 |  | ||||||
|         /// writes (where N is the FIFO capacity), then the FIFO effectively
 |  | ||||||
|         /// behaves as a one-entry FIFO.
 |  | ||||||
|         ///
 |  | ||||||
|         /// In particular, any counterexample of the full FIFO behaving badly
 |  | ||||||
|         /// will also be caught by one of the filtered versions (one which
 |  | ||||||
|         /// happens to be in phase with the offending input or output).
 |  | ||||||
|         #[hdl_module] |         #[hdl_module] | ||||||
|         fn queue_test(capacity: NonZeroUsize, inp_ready_is_comb: bool, out_valid_is_comb: bool) { |         fn queue_test(capacity: NonZeroUsize, inp_ready_is_comb: bool, out_valid_is_comb: bool) { | ||||||
|             #[hdl] |             #[hdl] | ||||||
|  | @ -255,8 +217,6 @@ mod tests { | ||||||
|                     rst: formal_reset().to_reset(), |                     rst: formal_reset().to_reset(), | ||||||
|                 }, |                 }, | ||||||
|             ); |             ); | ||||||
| 
 |  | ||||||
|             // random input data
 |  | ||||||
|             #[hdl] |             #[hdl] | ||||||
|             let inp_data: HdlOption<UInt<8>> = wire(); |             let inp_data: HdlOption<UInt<8>> = wire(); | ||||||
|             #[hdl] |             #[hdl] | ||||||
|  | @ -265,26 +225,16 @@ mod tests { | ||||||
|             } else { |             } else { | ||||||
|                 connect(inp_data, HdlNone()); |                 connect(inp_data, HdlNone()); | ||||||
|             } |             } | ||||||
| 
 |  | ||||||
|             // assert output ready at random
 |  | ||||||
|             #[hdl] |             #[hdl] | ||||||
|             let out_ready: Bool = wire(); |             let out_ready: Bool = wire(); | ||||||
|             connect(out_ready, any_seq(Bool)); |             connect(out_ready, any_seq(Bool)); | ||||||
| 
 |             let index_ty: UInt<32> = UInt::TYPE; | ||||||
|             // The current number of elements in the FIFO ranges from zero to
 |  | ||||||
|             // maximum capacity, inclusive.
 |  | ||||||
|             let count_ty = UInt::range_inclusive(0..=capacity.get()); |  | ||||||
|             // type for counters that wrap around at the FIFO capacity
 |  | ||||||
|             let index_ty = UInt::range(0..capacity.get()); |  | ||||||
| 
 |  | ||||||
|             // among all entries of the FIFO internal circular memory, choose
 |  | ||||||
|             // one at random to check
 |  | ||||||
|             #[hdl] |             #[hdl] | ||||||
|             let index_to_check = wire(index_ty); |             let index_to_check = wire(); | ||||||
|             connect(index_to_check, any_const(index_ty)); |             connect(index_to_check, any_const(index_ty)); | ||||||
|             hdl_assume(clk, index_to_check.cmp_lt(capacity.get()), ""); |             let index_max = !index_ty.zero(); | ||||||
| 
 |             // we saturate at index_max, so only check indexes where we properly maintain position
 | ||||||
|             // instantiate and connect the queue
 |             hdl_assume(clk, index_to_check.cmp_ne(index_max), ""); | ||||||
|             #[hdl] |             #[hdl] | ||||||
|             let dut = instance(queue( |             let dut = instance(queue( | ||||||
|                 UInt[ConstUsize::<8>], |                 UInt[ConstUsize::<8>], | ||||||
|  | @ -295,172 +245,109 @@ mod tests { | ||||||
|             connect(dut.cd, cd); |             connect(dut.cd, cd); | ||||||
|             connect(dut.inp.data, inp_data); |             connect(dut.inp.data, inp_data); | ||||||
|             connect(dut.out.ready, out_ready); |             connect(dut.out.ready, out_ready); | ||||||
|  |             hdl_assume( | ||||||
|  |                 clk, | ||||||
|  |                 index_to_check.cmp_ne(!Expr::ty(index_to_check).zero()), | ||||||
|  |                 "", | ||||||
|  |             ); | ||||||
| 
 | 
 | ||||||
|             // Keep an independent count of words in the FIFO. Ensure that
 |  | ||||||
|             // it's always correct, and never overflows.
 |  | ||||||
|             #[hdl] |             #[hdl] | ||||||
|             let expected_count_reg = reg_builder().clock_domain(cd).reset(count_ty.zero()); |             let expected_count_reg = reg_builder().clock_domain(cd).reset(0u32); | ||||||
|  |             #[hdl] | ||||||
|  |             let next_expected_count = wire(); | ||||||
|  |             connect(next_expected_count, expected_count_reg); | ||||||
|  |             connect(expected_count_reg, next_expected_count); | ||||||
|             #[hdl] |             #[hdl] | ||||||
|             if ReadyValid::firing(dut.inp) & !ReadyValid::firing(dut.out) { |             if ReadyValid::firing(dut.inp) & !ReadyValid::firing(dut.out) { | ||||||
|                 hdl_assert(clk, expected_count_reg.cmp_ne(capacity.get()), ""); |                 connect_any(next_expected_count, expected_count_reg + 1u8); | ||||||
|                 connect_any(expected_count_reg, expected_count_reg + 1u8); |  | ||||||
|             } else if !ReadyValid::firing(dut.inp) & ReadyValid::firing(dut.out) { |             } else if !ReadyValid::firing(dut.inp) & ReadyValid::firing(dut.out) { | ||||||
|                 hdl_assert(clk, expected_count_reg.cmp_ne(count_ty.zero()), ""); |                 connect_any(next_expected_count, expected_count_reg - 1u8); | ||||||
|                 connect_any(expected_count_reg, expected_count_reg - 1u8); |  | ||||||
|             } |             } | ||||||
|             hdl_assert(clk, expected_count_reg.cmp_eq(dut.count), ""); |             hdl_assert(cd.clk, expected_count_reg.cmp_eq(dut.count), ""); | ||||||
|  | 
 | ||||||
|  |             #[hdl] | ||||||
|  |             let prev_out_ready_reg = reg_builder().clock_domain(cd).reset(!0_hdl_u3); | ||||||
|  |             connect_any( | ||||||
|  |                 prev_out_ready_reg, | ||||||
|  |                 (prev_out_ready_reg << 1) | out_ready.cast_to(UInt[1]), | ||||||
|  |             ); | ||||||
|  |             #[hdl] | ||||||
|  |             let prev_inp_valid_reg = reg_builder().clock_domain(cd).reset(!0_hdl_u3); | ||||||
|  |             connect_any( | ||||||
|  |                 prev_inp_valid_reg, | ||||||
|  |                 (prev_inp_valid_reg << 1) | HdlOption::is_some(inp_data).cast_to(UInt[1]), | ||||||
|  |             ); | ||||||
|  |             hdl_assume( | ||||||
|  |                 clk, | ||||||
|  |                 (prev_out_ready_reg & prev_inp_valid_reg).cmp_ne(0u8), | ||||||
|  |                 "", | ||||||
|  |             ); | ||||||
| 
 | 
 | ||||||
|             // keep an independent write index into the FIFO's circular buffer
 |  | ||||||
|             #[hdl] |             #[hdl] | ||||||
|             let inp_index_reg = reg_builder().clock_domain(cd).reset(index_ty.zero()); |             let inp_index_reg = reg_builder().clock_domain(cd).reset(index_ty.zero()); | ||||||
|             #[hdl] |             #[hdl] | ||||||
|             if ReadyValid::firing(dut.inp) { |             let stored_inp_data_reg = reg_builder().clock_domain(cd).reset(0u8); | ||||||
|  | 
 | ||||||
|  |             #[hdl] | ||||||
|  |             if let HdlSome(data) = ReadyValid::firing_data(dut.inp) { | ||||||
|                 #[hdl] |                 #[hdl] | ||||||
|                 if inp_index_reg.cmp_ne(capacity.get() - 1) { |                 if inp_index_reg.cmp_lt(index_max) { | ||||||
|                     connect_any(inp_index_reg, inp_index_reg + 1u8); |                     connect_any(inp_index_reg, inp_index_reg + 1u8); | ||||||
|                 } else { |                     #[hdl] | ||||||
|                     connect_any(inp_index_reg, 0_hdl_u0); |                     if inp_index_reg.cmp_eq(index_to_check) { | ||||||
|  |                         connect(stored_inp_data_reg, data); | ||||||
|  |                     } | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             // keep an independent read index into the FIFO's circular buffer
 |             #[hdl] | ||||||
|  |             if inp_index_reg.cmp_lt(index_to_check) { | ||||||
|  |                 hdl_assert(clk, stored_inp_data_reg.cmp_eq(0u8), ""); | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|             #[hdl] |             #[hdl] | ||||||
|             let out_index_reg = reg_builder().clock_domain(cd).reset(index_ty.zero()); |             let out_index_reg = reg_builder().clock_domain(cd).reset(index_ty.zero()); | ||||||
|             #[hdl] |             #[hdl] | ||||||
|             if ReadyValid::firing(dut.out) { |             let stored_out_data_reg = reg_builder().clock_domain(cd).reset(0u8); | ||||||
|  | 
 | ||||||
|  |             #[hdl] | ||||||
|  |             if let HdlSome(data) = ReadyValid::firing_data(dut.out) { | ||||||
|                 #[hdl] |                 #[hdl] | ||||||
|                 if out_index_reg.cmp_ne(capacity.get() - 1) { |                 if out_index_reg.cmp_lt(index_max) { | ||||||
|                     connect_any(out_index_reg, out_index_reg + 1u8); |                     connect_any(out_index_reg, out_index_reg + 1u8); | ||||||
|                 } else { |  | ||||||
|                     connect_any(out_index_reg, 0_hdl_u0); |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             // filter the input data stream, predicated by the read index
 |  | ||||||
|             // matching the chosen position in the FIFO's circular buffer
 |  | ||||||
|             #[hdl] |  | ||||||
|             let inp_index_matches = wire(); |  | ||||||
|             connect(inp_index_matches, inp_index_reg.cmp_eq(index_to_check)); |  | ||||||
|             #[hdl] |  | ||||||
|             let inp_firing_data = wire(); |  | ||||||
|             connect(inp_firing_data, HdlNone()); |  | ||||||
|             #[hdl] |  | ||||||
|             if inp_index_matches { |  | ||||||
|                 connect(inp_firing_data, ReadyValid::firing_data(dut.inp)); |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             // filter the output data stream, predicated by the write index
 |  | ||||||
|             // matching the chosen position in the FIFO's circular buffer
 |  | ||||||
|             #[hdl] |  | ||||||
|             let out_index_matches = wire(); |  | ||||||
|             connect(out_index_matches, out_index_reg.cmp_eq(index_to_check)); |  | ||||||
|             #[hdl] |  | ||||||
|             let out_firing_data = wire(); |  | ||||||
|             connect(out_firing_data, HdlNone()); |  | ||||||
|             #[hdl] |  | ||||||
|             if out_index_matches { |  | ||||||
|                 connect(out_firing_data, ReadyValid::firing_data(dut.out)); |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             // Implement a one-entry FIFO and ensure its equivalence to the
 |  | ||||||
|             // filtered FIFO.
 |  | ||||||
|             //
 |  | ||||||
|             // the holding register for our one-entry FIFO
 |  | ||||||
|             #[hdl] |  | ||||||
|             let stored_reg = reg_builder().clock_domain(cd).reset(HdlNone()); |  | ||||||
|             #[hdl] |  | ||||||
|             match stored_reg { |  | ||||||
|                 // If the holding register is empty...
 |  | ||||||
|                 HdlNone => { |  | ||||||
|                     #[hdl] |                     #[hdl] | ||||||
|                     match inp_firing_data { |                     if out_index_reg.cmp_eq(index_to_check) { | ||||||
|                         // ... and we are not receiving data, then we must not
 |                         connect(stored_out_data_reg, data); | ||||||
|                         // transmit any data.
 |  | ||||||
|                         HdlNone => hdl_assert(clk, HdlOption::is_none(out_firing_data), ""), |  | ||||||
|                         // If we are indeed receiving some data...
 |  | ||||||
|                         HdlSome(data_in) => { |  | ||||||
|                             #[hdl] |  | ||||||
|                             match out_firing_data { |  | ||||||
|                                 // ... and transmitting at the same time, we
 |  | ||||||
|                                 // must be transmitting the input data itself,
 |  | ||||||
|                                 // since the holding register is empty.
 |  | ||||||
|                                 HdlSome(data_out) => hdl_assert(clk, data_out.cmp_eq(data_in), ""), |  | ||||||
|                                 // If we are receiving, but not transmitting,
 |  | ||||||
|                                 // store the received data in the holding
 |  | ||||||
|                                 // register.
 |  | ||||||
|                                 HdlNone => connect(stored_reg, HdlSome(data_in)), |  | ||||||
|                             } |  | ||||||
|                         } |  | ||||||
|                     } |  | ||||||
|                 } |  | ||||||
|                 // If there is some value stored in the holding register...
 |  | ||||||
|                 HdlSome(stored) => { |  | ||||||
|                     #[hdl] |  | ||||||
|                     match out_firing_data { |  | ||||||
|                         // ... and we are not transmitting it, we cannot
 |  | ||||||
|                         // receive any more data.
 |  | ||||||
|                         HdlNone => hdl_assert(clk, HdlOption::is_none(inp_firing_data), ""), |  | ||||||
|                         // If we are transmitting a previously stored value...
 |  | ||||||
|                         HdlSome(data_out) => { |  | ||||||
|                             // ... it must be the same data we stored earlier.
 |  | ||||||
|                             hdl_assert(clk, data_out.cmp_eq(stored), ""); |  | ||||||
|                             // Also, accept new data, if any. Otherwise,
 |  | ||||||
|                             // let the holding register become empty.
 |  | ||||||
|                             connect(stored_reg, inp_firing_data); |  | ||||||
|                         } |  | ||||||
|                     } |                     } | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             // from now on, some extra assertions in order to pass induction
 |  | ||||||
| 
 |  | ||||||
|             // sync the holding register, when it's occupied, to the
 |  | ||||||
|             // corresponding entry in the FIFO's circular buffer
 |  | ||||||
|             connect(dut.dbg.index_to_check, index_to_check); |  | ||||||
|             #[hdl] |             #[hdl] | ||||||
|             if let HdlSome(stored) = stored_reg { |             if out_index_reg.cmp_lt(index_to_check) { | ||||||
|                 hdl_assert(clk, stored.cmp_eq(dut.dbg.stored), ""); |                 hdl_assert(clk, stored_out_data_reg.cmp_eq(0u8), ""); | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             // sync the read and write indices
 |             hdl_assert(clk, inp_index_reg.cmp_ge(out_index_reg), ""); | ||||||
|             hdl_assert(clk, inp_index_reg.cmp_eq(dut.dbg.inp_index), ""); |  | ||||||
|             hdl_assert(clk, out_index_reg.cmp_eq(dut.dbg.out_index), ""); |  | ||||||
| 
 | 
 | ||||||
|             // the indices should never go past the capacity, but induction
 |  | ||||||
|             // doesn't know that...
 |  | ||||||
|             hdl_assert(clk, inp_index_reg.cmp_lt(capacity.get()), ""); |  | ||||||
|             hdl_assert(clk, out_index_reg.cmp_lt(capacity.get()), ""); |  | ||||||
| 
 |  | ||||||
|             // strongly constrain the state of the holding register
 |  | ||||||
|             //
 |  | ||||||
|             // The holding register is full if and only if the corresponding
 |  | ||||||
|             // FIFO entry was written to and not yet read. In other words, if
 |  | ||||||
|             // the number of pending reads until the chosen entry is read out
 |  | ||||||
|             // is greater than the current FIFO count, then the entry couldn't
 |  | ||||||
|             // be in the FIFO in the first place.
 |  | ||||||
|             #[hdl] |             #[hdl] | ||||||
|             let pending_reads: UInt = wire(index_ty); |             if inp_index_reg.cmp_lt(index_max) & out_index_reg.cmp_lt(index_max) { | ||||||
|             // take care of wrap-around when subtracting indices, add the
 |                 hdl_assert( | ||||||
|             // capacity amount to keep the result positive if necessary
 |                     clk, | ||||||
|             #[hdl] |                     expected_count_reg.cmp_eq(inp_index_reg - out_index_reg), | ||||||
|             if index_to_check.cmp_ge(out_index_reg) { |                     "", | ||||||
|                 connect(pending_reads, index_to_check - out_index_reg); |                 ); | ||||||
|             } else { |             } else { | ||||||
|                 connect( |                 hdl_assert( | ||||||
|                     pending_reads, |                     clk, | ||||||
|                     index_to_check + capacity.get() - out_index_reg, |                     expected_count_reg.cmp_ge(inp_index_reg - out_index_reg), | ||||||
|  |                     "", | ||||||
|                 ); |                 ); | ||||||
|             } |             } | ||||||
|             // check whether the chosen entry is in the FIFO
 | 
 | ||||||
|             #[hdl] |             #[hdl] | ||||||
|             let expected_stored: Bool = wire(); |             if inp_index_reg.cmp_gt(index_to_check) & out_index_reg.cmp_gt(index_to_check) { | ||||||
|             connect(expected_stored, pending_reads.cmp_lt(dut.count)); |                 hdl_assert(clk, stored_inp_data_reg.cmp_eq(stored_out_data_reg), ""); | ||||||
|             // sync with the state of the holding register
 |             } | ||||||
|             hdl_assert( |  | ||||||
|                 clk, |  | ||||||
|                 expected_stored.cmp_eq(HdlOption::is_some(stored_reg)), |  | ||||||
|                 "", |  | ||||||
|             ); |  | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | @ -543,24 +430,4 @@ mod tests { | ||||||
|     fn test_4_true_true() { |     fn test_4_true_true() { | ||||||
|         test_queue(NonZero::new(4).unwrap(), true, true); |         test_queue(NonZero::new(4).unwrap(), true, true); | ||||||
|     } |     } | ||||||
| 
 |  | ||||||
|     #[test] |  | ||||||
|     fn test_many_false_false() { |  | ||||||
|         test_queue(NonZero::new((2 << 16) - 5).unwrap(), false, false); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     #[test] |  | ||||||
|     fn test_many_false_true() { |  | ||||||
|         test_queue(NonZero::new((2 << 16) - 5).unwrap(), false, true); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     #[test] |  | ||||||
|     fn test_many_true_false() { |  | ||||||
|         test_queue(NonZero::new((2 << 16) - 5).unwrap(), true, false); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     #[test] |  | ||||||
|     fn test_many_true_true() { |  | ||||||
|         test_queue(NonZero::new((2 << 16) - 5).unwrap(), true, true); |  | ||||||
|     } |  | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -4287,137 +4287,3 @@ circuit check_deduce_resets: | ||||||
| ",
 | ",
 | ||||||
|     }; |     }; | ||||||
| } | } | ||||||
| 
 |  | ||||||
| // intentionally not outline_generated to ensure we get correct macro hygiene
 |  | ||||||
| #[hdl_module] |  | ||||||
| pub fn check_cfgs<#[cfg(cfg_false_for_tests)] A: Type, #[cfg(cfg_true_for_tests)] B: Type>( |  | ||||||
|     #[cfg(cfg_false_for_tests)] a: A, |  | ||||||
|     #[cfg(cfg_true_for_tests)] b: B, |  | ||||||
| ) { |  | ||||||
|     #[hdl] |  | ||||||
|     struct S<#[cfg(cfg_false_for_tests)] A, #[cfg(cfg_true_for_tests)] B> { |  | ||||||
|         #[cfg(cfg_false_for_tests)] |  | ||||||
|         a: A, |  | ||||||
|         #[cfg(cfg_true_for_tests)] |  | ||||||
|         b: B, |  | ||||||
|     } |  | ||||||
|     #[hdl] |  | ||||||
|     #[cfg(cfg_false_for_tests)] |  | ||||||
|     let i_a: A = m.input(a); |  | ||||||
|     #[hdl] |  | ||||||
|     #[cfg(cfg_true_for_tests)] |  | ||||||
|     let i_b: B = m.input(b); |  | ||||||
|     #[hdl] |  | ||||||
|     let w: S<UInt<8>> = wire(); |  | ||||||
|     #[cfg(cfg_false_for_tests)] |  | ||||||
|     { |  | ||||||
|         #[hdl] |  | ||||||
|         let o_a: A = m.output(a); |  | ||||||
|         connect(o_a, w.a.cast_bits_to(a)); |  | ||||||
|         connect_any(w.a, i_a.cast_to_bits()); |  | ||||||
|     } |  | ||||||
|     #[cfg(cfg_true_for_tests)] |  | ||||||
|     { |  | ||||||
|         #[hdl] |  | ||||||
|         let o_b: B = m.output(b); |  | ||||||
|         connect(o_b, w.b.cast_bits_to(b)); |  | ||||||
|         connect_any(w.b, i_b.cast_to_bits()); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| #[test] |  | ||||||
| fn test_cfgs() { |  | ||||||
|     let _n = SourceLocation::normalize_files_for_tests(); |  | ||||||
|     let m = check_cfgs(UInt[8]); |  | ||||||
|     dbg!(m); |  | ||||||
|     #[rustfmt::skip] // work around https://github.com/rust-lang/rustfmt/issues/6161
 |  | ||||||
|     assert_export_firrtl! { |  | ||||||
|         m => |  | ||||||
|         "/test/check_cfgs.fir": r"FIRRTL version 3.2.0
 |  | ||||||
| circuit check_cfgs: |  | ||||||
|     type Ty0 = {b: UInt<8>} |  | ||||||
|     module check_cfgs: @[the_test_file.rs 9962:1] |  | ||||||
|         input i_b: UInt<8> @[the_test_file.rs 9979:20] |  | ||||||
|         output o_b: UInt<8> @[the_test_file.rs 9992:24] |  | ||||||
|         wire w: Ty0 @[the_test_file.rs 9981:25] |  | ||||||
|         connect o_b, w.b @[the_test_file.rs 9993:9] |  | ||||||
|         connect w.b, i_b @[the_test_file.rs 9994:9] |  | ||||||
| ",
 |  | ||||||
|     }; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| #[hdl_module(outline_generated)] |  | ||||||
| pub fn check_let_patterns() { |  | ||||||
|     #[hdl] |  | ||||||
|     let tuple_in: (UInt<1>, SInt<1>, Bool) = m.input(); |  | ||||||
|     #[hdl] |  | ||||||
|     let (tuple_0, tuple_1, tuple_2) = tuple_in; |  | ||||||
|     #[hdl] |  | ||||||
|     let tuple_0_out: UInt<1> = m.output(); |  | ||||||
|     connect(tuple_0_out, tuple_0); |  | ||||||
|     #[hdl] |  | ||||||
|     let tuple_1_out: SInt<1> = m.output(); |  | ||||||
|     connect(tuple_1_out, tuple_1); |  | ||||||
|     #[hdl] |  | ||||||
|     let tuple_2_out: Bool = m.output(); |  | ||||||
|     connect(tuple_2_out, tuple_2); |  | ||||||
| 
 |  | ||||||
|     #[hdl] |  | ||||||
|     let test_struct_in: TestStruct<SInt<8>> = m.input(); |  | ||||||
|     #[hdl] |  | ||||||
|     let TestStruct::<_> { a, b } = test_struct_in; |  | ||||||
|     #[hdl] |  | ||||||
|     let test_struct_a_out: SInt<8> = m.output(); |  | ||||||
|     connect(test_struct_a_out, a); |  | ||||||
|     #[hdl] |  | ||||||
|     let test_struct_b_out: UInt<8> = m.output(); |  | ||||||
|     connect(test_struct_b_out, b); |  | ||||||
| 
 |  | ||||||
|     #[hdl] |  | ||||||
|     let test_struct_2_in: TestStruct2 = m.input(); |  | ||||||
|     #[hdl] |  | ||||||
|     let TestStruct2 { v } = test_struct_2_in; |  | ||||||
|     #[hdl] |  | ||||||
|     let test_struct_2_v_out: UInt<8> = m.output(); |  | ||||||
|     connect(test_struct_2_v_out, v); |  | ||||||
| 
 |  | ||||||
|     #[hdl] |  | ||||||
|     let test_struct_3_in: TestStruct3 = m.input(); |  | ||||||
|     #[hdl] |  | ||||||
|     let TestStruct3 {} = test_struct_3_in; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| #[test] |  | ||||||
| fn test_let_patterns() { |  | ||||||
|     let _n = SourceLocation::normalize_files_for_tests(); |  | ||||||
|     let m = check_let_patterns(); |  | ||||||
|     dbg!(m); |  | ||||||
|     #[rustfmt::skip] // work around https://github.com/rust-lang/rustfmt/issues/6161
 |  | ||||||
|     assert_export_firrtl! { |  | ||||||
|         m => |  | ||||||
|         "/test/check_let_patterns.fir": r"FIRRTL version 3.2.0
 |  | ||||||
| circuit check_let_patterns: |  | ||||||
|     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_let_patterns: @[module-XXXXXXXXXX.rs 1:1] |  | ||||||
|         input tuple_in: Ty0 @[module-XXXXXXXXXX.rs 2:1] |  | ||||||
|         output tuple_0_out: UInt<1> @[module-XXXXXXXXXX.rs 4:1] |  | ||||||
|         output tuple_1_out: SInt<1> @[module-XXXXXXXXXX.rs 6:1] |  | ||||||
|         output tuple_2_out: UInt<1> @[module-XXXXXXXXXX.rs 8:1] |  | ||||||
|         input test_struct_in: Ty1 @[module-XXXXXXXXXX.rs 10:1] |  | ||||||
|         output test_struct_a_out: SInt<8> @[module-XXXXXXXXXX.rs 12:1] |  | ||||||
|         output test_struct_b_out: UInt<8> @[module-XXXXXXXXXX.rs 14:1] |  | ||||||
|         input test_struct_2_in: Ty2 @[module-XXXXXXXXXX.rs 16:1] |  | ||||||
|         output test_struct_2_v_out: UInt<8> @[module-XXXXXXXXXX.rs 18:1] |  | ||||||
|         input test_struct_3_in: Ty3 @[module-XXXXXXXXXX.rs 20:1] |  | ||||||
|         connect tuple_0_out, tuple_in.`0` @[module-XXXXXXXXXX.rs 5:1] |  | ||||||
|         connect tuple_1_out, tuple_in.`1` @[module-XXXXXXXXXX.rs 7:1] |  | ||||||
|         connect tuple_2_out, tuple_in.`2` @[module-XXXXXXXXXX.rs 9:1] |  | ||||||
|         connect test_struct_a_out, test_struct_in.a @[module-XXXXXXXXXX.rs 13:1] |  | ||||||
|         connect test_struct_b_out, test_struct_in.b @[module-XXXXXXXXXX.rs 15:1] |  | ||||||
|         connect test_struct_2_v_out, test_struct_2_in.v @[module-XXXXXXXXXX.rs 19:1] |  | ||||||
| ",
 |  | ||||||
|     }; |  | ||||||
| } |  | ||||||
|  |  | ||||||
|  | @ -1246,200 +1246,3 @@ fn test_memories3() { | ||||||
|         panic!(); |         panic!(); | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 |  | ||||||
| #[hdl_module(outline_generated)] |  | ||||||
| pub fn duplicate_names() { |  | ||||||
|     #[hdl] |  | ||||||
|     let w = wire(); |  | ||||||
|     connect(w, 5u8); |  | ||||||
|     #[hdl] |  | ||||||
|     let w = wire(); |  | ||||||
|     connect(w, 6u8); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| #[test] |  | ||||||
| fn test_duplicate_names() { |  | ||||||
|     let _n = SourceLocation::normalize_files_for_tests(); |  | ||||||
|     let mut sim = Simulation::new(duplicate_names()); |  | ||||||
|     let mut writer = RcWriter::default(); |  | ||||||
|     sim.add_trace_writer(VcdWriterDecls::new(writer.clone())); |  | ||||||
|     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/duplicate_names.vcd") { |  | ||||||
|         panic!(); |  | ||||||
|     } |  | ||||||
|     let sim_debug = format!("{sim:#?}"); |  | ||||||
|     println!("#######\n{sim_debug}\n#######"); |  | ||||||
|     if sim_debug != include_str!("sim/expected/duplicate_names.txt") { |  | ||||||
|         panic!(); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| #[hdl_module(outline_generated)] |  | ||||||
| pub fn array_rw() { |  | ||||||
|     #[hdl] |  | ||||||
|     let array_in: Array<UInt<8>, 16> = m.input(); |  | ||||||
|     #[hdl] |  | ||||||
|     let array_out: Array<UInt<8>, 16> = m.output(); |  | ||||||
|     #[hdl] |  | ||||||
|     let read_index: UInt<8> = m.input(); |  | ||||||
|     #[hdl] |  | ||||||
|     let read_data: UInt<8> = m.output(); |  | ||||||
|     #[hdl] |  | ||||||
|     let write_index: UInt<8> = m.input(); |  | ||||||
|     #[hdl] |  | ||||||
|     let write_data: UInt<8> = m.input(); |  | ||||||
|     #[hdl] |  | ||||||
|     let write_en: Bool = m.input(); |  | ||||||
|     #[hdl] |  | ||||||
|     let array_wire = wire(); |  | ||||||
|     connect(array_wire, array_in); |  | ||||||
|     connect(array_out, array_wire); |  | ||||||
|     #[hdl] |  | ||||||
|     if write_en { |  | ||||||
|         connect(array_wire[write_index], write_data); |  | ||||||
|     } |  | ||||||
|     connect(read_data, array_wire[read_index]); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| #[test] |  | ||||||
| fn test_array_rw() { |  | ||||||
|     let _n = SourceLocation::normalize_files_for_tests(); |  | ||||||
|     let mut sim = Simulation::new(array_rw()); |  | ||||||
|     let mut writer = RcWriter::default(); |  | ||||||
|     sim.add_trace_writer(VcdWriterDecls::new(writer.clone())); |  | ||||||
|     #[derive(Debug, PartialEq)] |  | ||||||
|     struct State { |  | ||||||
|         array_in: [u8; 16], |  | ||||||
|         array_out: [u8; 16], |  | ||||||
|         read_index: u8, |  | ||||||
|         read_data: u8, |  | ||||||
|         write_index: u8, |  | ||||||
|         write_data: u8, |  | ||||||
|         write_en: bool, |  | ||||||
|     } |  | ||||||
|     let mut states = Vec::new(); |  | ||||||
|     let array_in = [ |  | ||||||
|         0xFFu8, 0x7F, 0x3F, 0x1F, 0x0F, 0x07, 0x03, 0x01, //
 |  | ||||||
|         0x00u8, 0x80, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC, 0xFE, |  | ||||||
|     ]; |  | ||||||
|     for i in 0..=16 { |  | ||||||
|         states.push(State { |  | ||||||
|             array_in, |  | ||||||
|             array_out: array_in, |  | ||||||
|             read_index: i, |  | ||||||
|             read_data: array_in.get(i as usize).copied().unwrap_or(0), |  | ||||||
|             write_index: 0, |  | ||||||
|             write_data: 0, |  | ||||||
|             write_en: false, |  | ||||||
|         }); |  | ||||||
|     } |  | ||||||
|     for i in 0..=16u8 { |  | ||||||
|         let mut array_out = array_in; |  | ||||||
|         let write_data = i.wrapping_mul(i); |  | ||||||
|         if let Some(v) = array_out.get_mut(i as usize) { |  | ||||||
|             *v = write_data; |  | ||||||
|         } |  | ||||||
|         states.push(State { |  | ||||||
|             array_in, |  | ||||||
|             array_out, |  | ||||||
|             read_index: 0, |  | ||||||
|             read_data: array_out[0], |  | ||||||
|             write_index: i, |  | ||||||
|             write_data, |  | ||||||
|             write_en: true, |  | ||||||
|         }); |  | ||||||
|     } |  | ||||||
|     for (cycle, expected) in states.into_iter().enumerate() { |  | ||||||
|         let State { |  | ||||||
|             array_in, |  | ||||||
|             array_out: _, |  | ||||||
|             read_index, |  | ||||||
|             read_data: _, |  | ||||||
|             write_index, |  | ||||||
|             write_data, |  | ||||||
|             write_en, |  | ||||||
|         } = expected; |  | ||||||
|         sim.write(sim.io().array_in, array_in); |  | ||||||
|         sim.write(sim.io().read_index, read_index); |  | ||||||
|         sim.write(sim.io().write_index, write_index); |  | ||||||
|         sim.write(sim.io().write_data, write_data); |  | ||||||
|         sim.write(sim.io().write_en, write_en); |  | ||||||
|         sim.advance_time(SimDuration::from_micros(1)); |  | ||||||
|         let array_out = std::array::from_fn(|index| { |  | ||||||
|             sim.read_bool_or_int(sim.io().array_out[index]) |  | ||||||
|                 .to_bigint() |  | ||||||
|                 .try_into() |  | ||||||
|                 .expect("known to be in range") |  | ||||||
|         }); |  | ||||||
|         let read_data = sim |  | ||||||
|             .read_bool_or_int(sim.io().read_data) |  | ||||||
|             .to_bigint() |  | ||||||
|             .try_into() |  | ||||||
|             .expect("known to be in range"); |  | ||||||
|         let state = State { |  | ||||||
|             array_in, |  | ||||||
|             array_out, |  | ||||||
|             read_index, |  | ||||||
|             read_data, |  | ||||||
|             write_index, |  | ||||||
|             write_data, |  | ||||||
|             write_en, |  | ||||||
|         }; |  | ||||||
|         assert_eq!( |  | ||||||
|             state, |  | ||||||
|             expected, |  | ||||||
|             "vcd:\n{}\ncycle: {cycle}", |  | ||||||
|             String::from_utf8(writer.take()).unwrap(), |  | ||||||
|         ); |  | ||||||
|     } |  | ||||||
|     sim.flush_traces().unwrap(); |  | ||||||
|     let vcd = String::from_utf8(writer.take()).unwrap(); |  | ||||||
|     println!("####### VCD:\n{vcd}\n#######"); |  | ||||||
|     if vcd != include_str!("sim/expected/array_rw.vcd") { |  | ||||||
|         panic!(); |  | ||||||
|     } |  | ||||||
|     let sim_debug = format!("{sim:#?}"); |  | ||||||
|     println!("#######\n{sim_debug}\n#######"); |  | ||||||
|     if sim_debug != include_str!("sim/expected/array_rw.txt") { |  | ||||||
|         panic!(); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| #[hdl_module(outline_generated)] |  | ||||||
| pub fn conditional_assignment_last() { |  | ||||||
|     #[hdl] |  | ||||||
|     let i: Bool = m.input(); |  | ||||||
|     #[hdl] |  | ||||||
|     let w = wire(); |  | ||||||
|     connect(w, true); |  | ||||||
|     #[hdl] |  | ||||||
|     if i { |  | ||||||
|         connect(w, false); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| #[test] |  | ||||||
| fn test_conditional_assignment_last() { |  | ||||||
|     let _n = SourceLocation::normalize_files_for_tests(); |  | ||||||
|     let mut sim = Simulation::new(conditional_assignment_last()); |  | ||||||
|     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(1)); |  | ||||||
|     sim.write(sim.io().i, 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/conditional_assignment_last.vcd") { |  | ||||||
|         panic!(); |  | ||||||
|     } |  | ||||||
|     let sim_debug = format!("{sim:#?}"); |  | ||||||
|     println!("#######\n{sim_debug}\n#######"); |  | ||||||
|     if sim_debug != include_str!("sim/expected/conditional_assignment_last.txt") { |  | ||||||
|         panic!(); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							|  | @ -1,283 +0,0 @@ | ||||||
| $timescale 1 ps $end |  | ||||||
| $scope module array_rw $end |  | ||||||
| $scope struct array_in $end |  | ||||||
| $var wire 8 ! \[0] $end |  | ||||||
| $var wire 8 " \[1] $end |  | ||||||
| $var wire 8 # \[2] $end |  | ||||||
| $var wire 8 $ \[3] $end |  | ||||||
| $var wire 8 % \[4] $end |  | ||||||
| $var wire 8 & \[5] $end |  | ||||||
| $var wire 8 ' \[6] $end |  | ||||||
| $var wire 8 ( \[7] $end |  | ||||||
| $var wire 8 ) \[8] $end |  | ||||||
| $var wire 8 * \[9] $end |  | ||||||
| $var wire 8 + \[10] $end |  | ||||||
| $var wire 8 , \[11] $end |  | ||||||
| $var wire 8 - \[12] $end |  | ||||||
| $var wire 8 . \[13] $end |  | ||||||
| $var wire 8 / \[14] $end |  | ||||||
| $var wire 8 0 \[15] $end |  | ||||||
| $upscope $end |  | ||||||
| $scope struct array_out $end |  | ||||||
| $var wire 8 1 \[0] $end |  | ||||||
| $var wire 8 2 \[1] $end |  | ||||||
| $var wire 8 3 \[2] $end |  | ||||||
| $var wire 8 4 \[3] $end |  | ||||||
| $var wire 8 5 \[4] $end |  | ||||||
| $var wire 8 6 \[5] $end |  | ||||||
| $var wire 8 7 \[6] $end |  | ||||||
| $var wire 8 8 \[7] $end |  | ||||||
| $var wire 8 9 \[8] $end |  | ||||||
| $var wire 8 : \[9] $end |  | ||||||
| $var wire 8 ; \[10] $end |  | ||||||
| $var wire 8 < \[11] $end |  | ||||||
| $var wire 8 = \[12] $end |  | ||||||
| $var wire 8 > \[13] $end |  | ||||||
| $var wire 8 ? \[14] $end |  | ||||||
| $var wire 8 @ \[15] $end |  | ||||||
| $upscope $end |  | ||||||
| $var wire 8 A read_index $end |  | ||||||
| $var wire 8 B read_data $end |  | ||||||
| $var wire 8 C write_index $end |  | ||||||
| $var wire 8 D write_data $end |  | ||||||
| $var wire 1 E write_en $end |  | ||||||
| $scope struct array_wire $end |  | ||||||
| $var wire 8 F \[0] $end |  | ||||||
| $var wire 8 G \[1] $end |  | ||||||
| $var wire 8 H \[2] $end |  | ||||||
| $var wire 8 I \[3] $end |  | ||||||
| $var wire 8 J \[4] $end |  | ||||||
| $var wire 8 K \[5] $end |  | ||||||
| $var wire 8 L \[6] $end |  | ||||||
| $var wire 8 M \[7] $end |  | ||||||
| $var wire 8 N \[8] $end |  | ||||||
| $var wire 8 O \[9] $end |  | ||||||
| $var wire 8 P \[10] $end |  | ||||||
| $var wire 8 Q \[11] $end |  | ||||||
| $var wire 8 R \[12] $end |  | ||||||
| $var wire 8 S \[13] $end |  | ||||||
| $var wire 8 T \[14] $end |  | ||||||
| $var wire 8 U \[15] $end |  | ||||||
| $upscope $end |  | ||||||
| $upscope $end |  | ||||||
| $enddefinitions $end |  | ||||||
| $dumpvars |  | ||||||
| b11111111 ! |  | ||||||
| b1111111 " |  | ||||||
| b111111 # |  | ||||||
| b11111 $ |  | ||||||
| b1111 % |  | ||||||
| b111 & |  | ||||||
| b11 ' |  | ||||||
| b1 ( |  | ||||||
| b0 ) |  | ||||||
| b10000000 * |  | ||||||
| b11000000 + |  | ||||||
| b11100000 , |  | ||||||
| b11110000 - |  | ||||||
| b11111000 . |  | ||||||
| b11111100 / |  | ||||||
| b11111110 0 |  | ||||||
| b11111111 1 |  | ||||||
| b1111111 2 |  | ||||||
| b111111 3 |  | ||||||
| b11111 4 |  | ||||||
| b1111 5 |  | ||||||
| b111 6 |  | ||||||
| b11 7 |  | ||||||
| b1 8 |  | ||||||
| b0 9 |  | ||||||
| b10000000 : |  | ||||||
| b11000000 ; |  | ||||||
| b11100000 < |  | ||||||
| b11110000 = |  | ||||||
| b11111000 > |  | ||||||
| b11111100 ? |  | ||||||
| b11111110 @ |  | ||||||
| b0 A |  | ||||||
| b11111111 B |  | ||||||
| b0 C |  | ||||||
| b0 D |  | ||||||
| 0E |  | ||||||
| b11111111 F |  | ||||||
| b1111111 G |  | ||||||
| b111111 H |  | ||||||
| b11111 I |  | ||||||
| b1111 J |  | ||||||
| b111 K |  | ||||||
| b11 L |  | ||||||
| b1 M |  | ||||||
| b0 N |  | ||||||
| b10000000 O |  | ||||||
| b11000000 P |  | ||||||
| b11100000 Q |  | ||||||
| b11110000 R |  | ||||||
| b11111000 S |  | ||||||
| b11111100 T |  | ||||||
| b11111110 U |  | ||||||
| $end |  | ||||||
| #1000000 |  | ||||||
| b1 A |  | ||||||
| b1111111 B |  | ||||||
| #2000000 |  | ||||||
| b10 A |  | ||||||
| b111111 B |  | ||||||
| #3000000 |  | ||||||
| b11 A |  | ||||||
| b11111 B |  | ||||||
| #4000000 |  | ||||||
| b100 A |  | ||||||
| b1111 B |  | ||||||
| #5000000 |  | ||||||
| b101 A |  | ||||||
| b111 B |  | ||||||
| #6000000 |  | ||||||
| b110 A |  | ||||||
| b11 B |  | ||||||
| #7000000 |  | ||||||
| b111 A |  | ||||||
| b1 B |  | ||||||
| #8000000 |  | ||||||
| b1000 A |  | ||||||
| b0 B |  | ||||||
| #9000000 |  | ||||||
| b1001 A |  | ||||||
| b10000000 B |  | ||||||
| #10000000 |  | ||||||
| b1010 A |  | ||||||
| b11000000 B |  | ||||||
| #11000000 |  | ||||||
| b1011 A |  | ||||||
| b11100000 B |  | ||||||
| #12000000 |  | ||||||
| b1100 A |  | ||||||
| b11110000 B |  | ||||||
| #13000000 |  | ||||||
| b1101 A |  | ||||||
| b11111000 B |  | ||||||
| #14000000 |  | ||||||
| b1110 A |  | ||||||
| b11111100 B |  | ||||||
| #15000000 |  | ||||||
| b1111 A |  | ||||||
| b11111110 B |  | ||||||
| #16000000 |  | ||||||
| b10000 A |  | ||||||
| b0 B |  | ||||||
| #17000000 |  | ||||||
| b0 1 |  | ||||||
| b0 A |  | ||||||
| 1E |  | ||||||
| b0 F |  | ||||||
| #18000000 |  | ||||||
| b11111111 1 |  | ||||||
| b1 2 |  | ||||||
| b11111111 B |  | ||||||
| b1 C |  | ||||||
| b1 D |  | ||||||
| b11111111 F |  | ||||||
| b1 G |  | ||||||
| #19000000 |  | ||||||
| b1111111 2 |  | ||||||
| b100 3 |  | ||||||
| b10 C |  | ||||||
| b100 D |  | ||||||
| b1111111 G |  | ||||||
| b100 H |  | ||||||
| #20000000 |  | ||||||
| b111111 3 |  | ||||||
| b1001 4 |  | ||||||
| b11 C |  | ||||||
| b1001 D |  | ||||||
| b111111 H |  | ||||||
| b1001 I |  | ||||||
| #21000000 |  | ||||||
| b11111 4 |  | ||||||
| b10000 5 |  | ||||||
| b100 C |  | ||||||
| b10000 D |  | ||||||
| b11111 I |  | ||||||
| b10000 J |  | ||||||
| #22000000 |  | ||||||
| b1111 5 |  | ||||||
| b11001 6 |  | ||||||
| b101 C |  | ||||||
| b11001 D |  | ||||||
| b1111 J |  | ||||||
| b11001 K |  | ||||||
| #23000000 |  | ||||||
| b111 6 |  | ||||||
| b100100 7 |  | ||||||
| b110 C |  | ||||||
| b100100 D |  | ||||||
| b111 K |  | ||||||
| b100100 L |  | ||||||
| #24000000 |  | ||||||
| b11 7 |  | ||||||
| b110001 8 |  | ||||||
| b111 C |  | ||||||
| b110001 D |  | ||||||
| b11 L |  | ||||||
| b110001 M |  | ||||||
| #25000000 |  | ||||||
| b1 8 |  | ||||||
| b1000000 9 |  | ||||||
| b1000 C |  | ||||||
| b1000000 D |  | ||||||
| b1 M |  | ||||||
| b1000000 N |  | ||||||
| #26000000 |  | ||||||
| b0 9 |  | ||||||
| b1010001 : |  | ||||||
| b1001 C |  | ||||||
| b1010001 D |  | ||||||
| b0 N |  | ||||||
| b1010001 O |  | ||||||
| #27000000 |  | ||||||
| b10000000 : |  | ||||||
| b1100100 ; |  | ||||||
| b1010 C |  | ||||||
| b1100100 D |  | ||||||
| b10000000 O |  | ||||||
| b1100100 P |  | ||||||
| #28000000 |  | ||||||
| b11000000 ; |  | ||||||
| b1111001 < |  | ||||||
| b1011 C |  | ||||||
| b1111001 D |  | ||||||
| b11000000 P |  | ||||||
| b1111001 Q |  | ||||||
| #29000000 |  | ||||||
| b11100000 < |  | ||||||
| b10010000 = |  | ||||||
| b1100 C |  | ||||||
| b10010000 D |  | ||||||
| b11100000 Q |  | ||||||
| b10010000 R |  | ||||||
| #30000000 |  | ||||||
| b11110000 = |  | ||||||
| b10101001 > |  | ||||||
| b1101 C |  | ||||||
| b10101001 D |  | ||||||
| b11110000 R |  | ||||||
| b10101001 S |  | ||||||
| #31000000 |  | ||||||
| b11111000 > |  | ||||||
| b11000100 ? |  | ||||||
| b1110 C |  | ||||||
| b11000100 D |  | ||||||
| b11111000 S |  | ||||||
| b11000100 T |  | ||||||
| #32000000 |  | ||||||
| b11111100 ? |  | ||||||
| b11100001 @ |  | ||||||
| b1111 C |  | ||||||
| b11100001 D |  | ||||||
| b11111100 T |  | ||||||
| b11100001 U |  | ||||||
| #33000000 |  | ||||||
| b11111110 @ |  | ||||||
| b10000 C |  | ||||||
| b0 D |  | ||||||
| b11111110 U |  | ||||||
| #34000000 |  | ||||||
|  | @ -1,189 +0,0 @@ | ||||||
| Simulation { |  | ||||||
|     state: State { |  | ||||||
|         insns: Insns { |  | ||||||
|             state_layout: StateLayout { |  | ||||||
|                 ty: TypeLayout { |  | ||||||
|                     small_slots: StatePartLayout<SmallSlots> { |  | ||||||
|                         len: 0, |  | ||||||
|                         debug_data: [], |  | ||||||
|                         .. |  | ||||||
|                     }, |  | ||||||
|                     big_slots: StatePartLayout<BigSlots> { |  | ||||||
|                         len: 4, |  | ||||||
|                         debug_data: [ |  | ||||||
|                             SlotDebugData { |  | ||||||
|                                 name: "InstantiatedModule(conditional_assignment_last: conditional_assignment_last).conditional_assignment_last::i", |  | ||||||
|                                 ty: Bool, |  | ||||||
|                             }, |  | ||||||
|                             SlotDebugData { |  | ||||||
|                                 name: "InstantiatedModule(conditional_assignment_last: conditional_assignment_last).conditional_assignment_last::w", |  | ||||||
|                                 ty: Bool, |  | ||||||
|                             }, |  | ||||||
|                             SlotDebugData { |  | ||||||
|                                 name: "", |  | ||||||
|                                 ty: Bool, |  | ||||||
|                             }, |  | ||||||
|                             SlotDebugData { |  | ||||||
|                                 name: "", |  | ||||||
|                                 ty: Bool, |  | ||||||
|                             }, |  | ||||||
|                         ], |  | ||||||
|                         .. |  | ||||||
|                     }, |  | ||||||
|                 }, |  | ||||||
|                 memories: StatePartLayout<Memories> { |  | ||||||
|                     len: 0, |  | ||||||
|                     debug_data: [], |  | ||||||
|                     layout_data: [], |  | ||||||
|                     .. |  | ||||||
|                 }, |  | ||||||
|             }, |  | ||||||
|             insns: [ |  | ||||||
|                 // at: module-XXXXXXXXXX.rs:1:1 |  | ||||||
|                 0: Const { |  | ||||||
|                     dest: StatePartIndex<BigSlots>(3), // (0x0) SlotDebugData { name: "", ty: Bool }, |  | ||||||
|                     value: 0x0, |  | ||||||
|                 }, |  | ||||||
|                 1: Const { |  | ||||||
|                     dest: StatePartIndex<BigSlots>(2), // (0x1) SlotDebugData { name: "", ty: Bool }, |  | ||||||
|                     value: 0x1, |  | ||||||
|                 }, |  | ||||||
|                 // at: module-XXXXXXXXXX.rs:4:1 |  | ||||||
|                 2: Copy { |  | ||||||
|                     dest: StatePartIndex<BigSlots>(1), // (0x0) SlotDebugData { name: "InstantiatedModule(conditional_assignment_last: conditional_assignment_last).conditional_assignment_last::w", ty: Bool }, |  | ||||||
|                     src: StatePartIndex<BigSlots>(2), // (0x1) SlotDebugData { name: "", ty: Bool }, |  | ||||||
|                 }, |  | ||||||
|                 // at: module-XXXXXXXXXX.rs:5:1 |  | ||||||
|                 3: BranchIfZero { |  | ||||||
|                     target: 5, |  | ||||||
|                     value: StatePartIndex<BigSlots>(0), // (0x1) SlotDebugData { name: "InstantiatedModule(conditional_assignment_last: conditional_assignment_last).conditional_assignment_last::i", ty: Bool }, |  | ||||||
|                 }, |  | ||||||
|                 // at: module-XXXXXXXXXX.rs:6:1 |  | ||||||
|                 4: Copy { |  | ||||||
|                     dest: StatePartIndex<BigSlots>(1), // (0x0) SlotDebugData { name: "InstantiatedModule(conditional_assignment_last: conditional_assignment_last).conditional_assignment_last::w", ty: Bool }, |  | ||||||
|                     src: StatePartIndex<BigSlots>(3), // (0x0) SlotDebugData { name: "", ty: Bool }, |  | ||||||
|                 }, |  | ||||||
|                 // at: module-XXXXXXXXXX.rs:1:1 |  | ||||||
|                 5: Return, |  | ||||||
|             ], |  | ||||||
|             .. |  | ||||||
|         }, |  | ||||||
|         pc: 5, |  | ||||||
|         memory_write_log: [], |  | ||||||
|         memories: StatePart { |  | ||||||
|             value: [], |  | ||||||
|         }, |  | ||||||
|         small_slots: StatePart { |  | ||||||
|             value: [], |  | ||||||
|         }, |  | ||||||
|         big_slots: StatePart { |  | ||||||
|             value: [ |  | ||||||
|                 1, |  | ||||||
|                 0, |  | ||||||
|                 1, |  | ||||||
|                 0, |  | ||||||
|             ], |  | ||||||
|         }, |  | ||||||
|     }, |  | ||||||
|     io: Instance { |  | ||||||
|         name: <simulator>::conditional_assignment_last, |  | ||||||
|         instantiated: Module { |  | ||||||
|             name: conditional_assignment_last, |  | ||||||
|             .. |  | ||||||
|         }, |  | ||||||
|     }, |  | ||||||
|     uninitialized_inputs: {}, |  | ||||||
|     io_targets: { |  | ||||||
|         Instance { |  | ||||||
|             name: <simulator>::conditional_assignment_last, |  | ||||||
|             instantiated: Module { |  | ||||||
|                 name: conditional_assignment_last, |  | ||||||
|                 .. |  | ||||||
|             }, |  | ||||||
|         }.i: CompiledValue { |  | ||||||
|             layout: CompiledTypeLayout { |  | ||||||
|                 ty: Bool, |  | ||||||
|                 layout: TypeLayout { |  | ||||||
|                     small_slots: StatePartLayout<SmallSlots> { |  | ||||||
|                         len: 0, |  | ||||||
|                         debug_data: [], |  | ||||||
|                         .. |  | ||||||
|                     }, |  | ||||||
|                     big_slots: StatePartLayout<BigSlots> { |  | ||||||
|                         len: 1, |  | ||||||
|                         debug_data: [ |  | ||||||
|                             SlotDebugData { |  | ||||||
|                                 name: "InstantiatedModule(conditional_assignment_last: conditional_assignment_last).conditional_assignment_last::i", |  | ||||||
|                                 ty: Bool, |  | ||||||
|                             }, |  | ||||||
|                         ], |  | ||||||
|                         .. |  | ||||||
|                     }, |  | ||||||
|                 }, |  | ||||||
|                 body: Scalar, |  | ||||||
|             }, |  | ||||||
|             range: TypeIndexRange { |  | ||||||
|                 small_slots: StatePartIndexRange<SmallSlots> { start: 0, len: 0 }, |  | ||||||
|                 big_slots: StatePartIndexRange<BigSlots> { start: 0, len: 1 }, |  | ||||||
|             }, |  | ||||||
|             write: None, |  | ||||||
|         }, |  | ||||||
|     }, |  | ||||||
|     made_initial_step: true, |  | ||||||
|     needs_settle: false, |  | ||||||
|     trace_decls: TraceModule { |  | ||||||
|         name: "conditional_assignment_last", |  | ||||||
|         children: [ |  | ||||||
|             TraceModuleIO { |  | ||||||
|                 name: "i", |  | ||||||
|                 child: TraceBool { |  | ||||||
|                     location: TraceScalarId(0), |  | ||||||
|                     name: "i", |  | ||||||
|                     flow: Source, |  | ||||||
|                 }, |  | ||||||
|                 ty: Bool, |  | ||||||
|                 flow: Source, |  | ||||||
|             }, |  | ||||||
|             TraceWire { |  | ||||||
|                 name: "w", |  | ||||||
|                 child: TraceBool { |  | ||||||
|                     location: TraceScalarId(1), |  | ||||||
|                     name: "w", |  | ||||||
|                     flow: Duplex, |  | ||||||
|                 }, |  | ||||||
|                 ty: Bool, |  | ||||||
|             }, |  | ||||||
|         ], |  | ||||||
|     }, |  | ||||||
|     traces: [ |  | ||||||
|         SimTrace { |  | ||||||
|             id: TraceScalarId(0), |  | ||||||
|             kind: BigBool { |  | ||||||
|                 index: StatePartIndex<BigSlots>(0), |  | ||||||
|             }, |  | ||||||
|             state: 0x1, |  | ||||||
|             last_state: 0x0, |  | ||||||
|         }, |  | ||||||
|         SimTrace { |  | ||||||
|             id: TraceScalarId(1), |  | ||||||
|             kind: BigBool { |  | ||||||
|                 index: StatePartIndex<BigSlots>(1), |  | ||||||
|             }, |  | ||||||
|             state: 0x0, |  | ||||||
|             last_state: 0x1, |  | ||||||
|         }, |  | ||||||
|     ], |  | ||||||
|     trace_memories: {}, |  | ||||||
|     trace_writers: [ |  | ||||||
|         Running( |  | ||||||
|             VcdWriter { |  | ||||||
|                 finished_init: true, |  | ||||||
|                 timescale: 1 ps, |  | ||||||
|                 .. |  | ||||||
|             }, |  | ||||||
|         ), |  | ||||||
|     ], |  | ||||||
|     instant: 2 μs, |  | ||||||
|     clocks_triggered: [], |  | ||||||
|     .. |  | ||||||
| } |  | ||||||
|  | @ -1,14 +0,0 @@ | ||||||
| $timescale 1 ps $end |  | ||||||
| $scope module conditional_assignment_last $end |  | ||||||
| $var wire 1 ! i $end |  | ||||||
| $var wire 1 " w $end |  | ||||||
| $upscope $end |  | ||||||
| $enddefinitions $end |  | ||||||
| $dumpvars |  | ||||||
| 0! |  | ||||||
| 1" |  | ||||||
| $end |  | ||||||
| #1000000 |  | ||||||
| 1! |  | ||||||
| 0" |  | ||||||
| #2000000 |  | ||||||
|  | @ -1,153 +0,0 @@ | ||||||
| Simulation { |  | ||||||
|     state: State { |  | ||||||
|         insns: Insns { |  | ||||||
|             state_layout: StateLayout { |  | ||||||
|                 ty: TypeLayout { |  | ||||||
|                     small_slots: StatePartLayout<SmallSlots> { |  | ||||||
|                         len: 0, |  | ||||||
|                         debug_data: [], |  | ||||||
|                         .. |  | ||||||
|                     }, |  | ||||||
|                     big_slots: StatePartLayout<BigSlots> { |  | ||||||
|                         len: 4, |  | ||||||
|                         debug_data: [ |  | ||||||
|                             SlotDebugData { |  | ||||||
|                                 name: "InstantiatedModule(duplicate_names: duplicate_names).duplicate_names::w", |  | ||||||
|                                 ty: UInt<8>, |  | ||||||
|                             }, |  | ||||||
|                             SlotDebugData { |  | ||||||
|                                 name: "", |  | ||||||
|                                 ty: UInt<8>, |  | ||||||
|                             }, |  | ||||||
|                             SlotDebugData { |  | ||||||
|                                 name: "InstantiatedModule(duplicate_names: duplicate_names).duplicate_names::w", |  | ||||||
|                                 ty: UInt<8>, |  | ||||||
|                             }, |  | ||||||
|                             SlotDebugData { |  | ||||||
|                                 name: "", |  | ||||||
|                                 ty: UInt<8>, |  | ||||||
|                             }, |  | ||||||
|                         ], |  | ||||||
|                         .. |  | ||||||
|                     }, |  | ||||||
|                 }, |  | ||||||
|                 memories: StatePartLayout<Memories> { |  | ||||||
|                     len: 0, |  | ||||||
|                     debug_data: [], |  | ||||||
|                     layout_data: [], |  | ||||||
|                     .. |  | ||||||
|                 }, |  | ||||||
|             }, |  | ||||||
|             insns: [ |  | ||||||
|                 // at: module-XXXXXXXXXX.rs:1:1 |  | ||||||
|                 0: Const { |  | ||||||
|                     dest: StatePartIndex<BigSlots>(3), // (0x6) SlotDebugData { name: "", ty: UInt<8> }, |  | ||||||
|                     value: 0x6, |  | ||||||
|                 }, |  | ||||||
|                 // at: module-XXXXXXXXXX.rs:5:1 |  | ||||||
|                 1: Copy { |  | ||||||
|                     dest: StatePartIndex<BigSlots>(2), // (0x6) SlotDebugData { name: "InstantiatedModule(duplicate_names: duplicate_names).duplicate_names::w", ty: UInt<8> }, |  | ||||||
|                     src: StatePartIndex<BigSlots>(3), // (0x6) SlotDebugData { name: "", ty: UInt<8> }, |  | ||||||
|                 }, |  | ||||||
|                 // at: module-XXXXXXXXXX.rs:1:1 |  | ||||||
|                 2: Const { |  | ||||||
|                     dest: StatePartIndex<BigSlots>(1), // (0x5) SlotDebugData { name: "", ty: UInt<8> }, |  | ||||||
|                     value: 0x5, |  | ||||||
|                 }, |  | ||||||
|                 // at: module-XXXXXXXXXX.rs:3:1 |  | ||||||
|                 3: Copy { |  | ||||||
|                     dest: StatePartIndex<BigSlots>(0), // (0x5) SlotDebugData { name: "InstantiatedModule(duplicate_names: duplicate_names).duplicate_names::w", ty: UInt<8> }, |  | ||||||
|                     src: StatePartIndex<BigSlots>(1), // (0x5) SlotDebugData { name: "", ty: UInt<8> }, |  | ||||||
|                 }, |  | ||||||
|                 // at: module-XXXXXXXXXX.rs:1:1 |  | ||||||
|                 4: Return, |  | ||||||
|             ], |  | ||||||
|             .. |  | ||||||
|         }, |  | ||||||
|         pc: 4, |  | ||||||
|         memory_write_log: [], |  | ||||||
|         memories: StatePart { |  | ||||||
|             value: [], |  | ||||||
|         }, |  | ||||||
|         small_slots: StatePart { |  | ||||||
|             value: [], |  | ||||||
|         }, |  | ||||||
|         big_slots: StatePart { |  | ||||||
|             value: [ |  | ||||||
|                 5, |  | ||||||
|                 5, |  | ||||||
|                 6, |  | ||||||
|                 6, |  | ||||||
|             ], |  | ||||||
|         }, |  | ||||||
|     }, |  | ||||||
|     io: Instance { |  | ||||||
|         name: <simulator>::duplicate_names, |  | ||||||
|         instantiated: Module { |  | ||||||
|             name: duplicate_names, |  | ||||||
|             .. |  | ||||||
|         }, |  | ||||||
|     }, |  | ||||||
|     uninitialized_inputs: {}, |  | ||||||
|     io_targets: {}, |  | ||||||
|     made_initial_step: true, |  | ||||||
|     needs_settle: false, |  | ||||||
|     trace_decls: TraceModule { |  | ||||||
|         name: "duplicate_names", |  | ||||||
|         children: [ |  | ||||||
|             TraceWire { |  | ||||||
|                 name: "w", |  | ||||||
|                 child: TraceUInt { |  | ||||||
|                     location: TraceScalarId(0), |  | ||||||
|                     name: "w", |  | ||||||
|                     ty: UInt<8>, |  | ||||||
|                     flow: Duplex, |  | ||||||
|                 }, |  | ||||||
|                 ty: UInt<8>, |  | ||||||
|             }, |  | ||||||
|             TraceWire { |  | ||||||
|                 name: "w", |  | ||||||
|                 child: TraceUInt { |  | ||||||
|                     location: TraceScalarId(1), |  | ||||||
|                     name: "w", |  | ||||||
|                     ty: UInt<8>, |  | ||||||
|                     flow: Duplex, |  | ||||||
|                 }, |  | ||||||
|                 ty: UInt<8>, |  | ||||||
|             }, |  | ||||||
|         ], |  | ||||||
|     }, |  | ||||||
|     traces: [ |  | ||||||
|         SimTrace { |  | ||||||
|             id: TraceScalarId(0), |  | ||||||
|             kind: BigUInt { |  | ||||||
|                 index: StatePartIndex<BigSlots>(0), |  | ||||||
|                 ty: UInt<8>, |  | ||||||
|             }, |  | ||||||
|             state: 0x05, |  | ||||||
|             last_state: 0x05, |  | ||||||
|         }, |  | ||||||
|         SimTrace { |  | ||||||
|             id: TraceScalarId(1), |  | ||||||
|             kind: BigUInt { |  | ||||||
|                 index: StatePartIndex<BigSlots>(2), |  | ||||||
|                 ty: UInt<8>, |  | ||||||
|             }, |  | ||||||
|             state: 0x06, |  | ||||||
|             last_state: 0x06, |  | ||||||
|         }, |  | ||||||
|     ], |  | ||||||
|     trace_memories: {}, |  | ||||||
|     trace_writers: [ |  | ||||||
|         Running( |  | ||||||
|             VcdWriter { |  | ||||||
|                 finished_init: true, |  | ||||||
|                 timescale: 1 ps, |  | ||||||
|                 .. |  | ||||||
|             }, |  | ||||||
|         ), |  | ||||||
|     ], |  | ||||||
|     instant: 1 μs, |  | ||||||
|     clocks_triggered: [], |  | ||||||
|     .. |  | ||||||
| } |  | ||||||
|  | @ -1,11 +0,0 @@ | ||||||
| $timescale 1 ps $end |  | ||||||
| $scope module duplicate_names $end |  | ||||||
| $var wire 8 ! w $end |  | ||||||
| $var wire 8 " w_2 $end |  | ||||||
| $upscope $end |  | ||||||
| $enddefinitions $end |  | ||||||
| $dumpvars |  | ||||||
| b101 ! |  | ||||||
| b110 " |  | ||||||
| $end |  | ||||||
| #1000000 |  | ||||||
|  | @ -24,97 +24,97 @@ $upscope $end | ||||||
| $upscope $end | $upscope $end | ||||||
| $scope struct mem $end | $scope struct mem $end | ||||||
| $scope struct contents $end | $scope struct contents $end | ||||||
| $scope struct \[0] $end | $scope struct [0] $end | ||||||
| $scope struct mem $end | $scope struct mem $end | ||||||
| $var reg 8 9 \0 $end | $var reg 8 9 \0 $end | ||||||
| $var reg 8 I \1 $end | $var reg 8 I \1 $end | ||||||
| $upscope $end | $upscope $end | ||||||
| $upscope $end | $upscope $end | ||||||
| $scope struct \[1] $end | $scope struct [1] $end | ||||||
| $scope struct mem $end | $scope struct mem $end | ||||||
| $var reg 8 : \0 $end | $var reg 8 : \0 $end | ||||||
| $var reg 8 J \1 $end | $var reg 8 J \1 $end | ||||||
| $upscope $end | $upscope $end | ||||||
| $upscope $end | $upscope $end | ||||||
| $scope struct \[2] $end | $scope struct [2] $end | ||||||
| $scope struct mem $end | $scope struct mem $end | ||||||
| $var reg 8 ; \0 $end | $var reg 8 ; \0 $end | ||||||
| $var reg 8 K \1 $end | $var reg 8 K \1 $end | ||||||
| $upscope $end | $upscope $end | ||||||
| $upscope $end | $upscope $end | ||||||
| $scope struct \[3] $end | $scope struct [3] $end | ||||||
| $scope struct mem $end | $scope struct mem $end | ||||||
| $var reg 8 < \0 $end | $var reg 8 < \0 $end | ||||||
| $var reg 8 L \1 $end | $var reg 8 L \1 $end | ||||||
| $upscope $end | $upscope $end | ||||||
| $upscope $end | $upscope $end | ||||||
| $scope struct \[4] $end | $scope struct [4] $end | ||||||
| $scope struct mem $end | $scope struct mem $end | ||||||
| $var reg 8 = \0 $end | $var reg 8 = \0 $end | ||||||
| $var reg 8 M \1 $end | $var reg 8 M \1 $end | ||||||
| $upscope $end | $upscope $end | ||||||
| $upscope $end | $upscope $end | ||||||
| $scope struct \[5] $end | $scope struct [5] $end | ||||||
| $scope struct mem $end | $scope struct mem $end | ||||||
| $var reg 8 > \0 $end | $var reg 8 > \0 $end | ||||||
| $var reg 8 N \1 $end | $var reg 8 N \1 $end | ||||||
| $upscope $end | $upscope $end | ||||||
| $upscope $end | $upscope $end | ||||||
| $scope struct \[6] $end | $scope struct [6] $end | ||||||
| $scope struct mem $end | $scope struct mem $end | ||||||
| $var reg 8 ? \0 $end | $var reg 8 ? \0 $end | ||||||
| $var reg 8 O \1 $end | $var reg 8 O \1 $end | ||||||
| $upscope $end | $upscope $end | ||||||
| $upscope $end | $upscope $end | ||||||
| $scope struct \[7] $end | $scope struct [7] $end | ||||||
| $scope struct mem $end | $scope struct mem $end | ||||||
| $var reg 8 @ \0 $end | $var reg 8 @ \0 $end | ||||||
| $var reg 8 P \1 $end | $var reg 8 P \1 $end | ||||||
| $upscope $end | $upscope $end | ||||||
| $upscope $end | $upscope $end | ||||||
| $scope struct \[8] $end | $scope struct [8] $end | ||||||
| $scope struct mem $end | $scope struct mem $end | ||||||
| $var reg 8 A \0 $end | $var reg 8 A \0 $end | ||||||
| $var reg 8 Q \1 $end | $var reg 8 Q \1 $end | ||||||
| $upscope $end | $upscope $end | ||||||
| $upscope $end | $upscope $end | ||||||
| $scope struct \[9] $end | $scope struct [9] $end | ||||||
| $scope struct mem $end | $scope struct mem $end | ||||||
| $var reg 8 B \0 $end | $var reg 8 B \0 $end | ||||||
| $var reg 8 R \1 $end | $var reg 8 R \1 $end | ||||||
| $upscope $end | $upscope $end | ||||||
| $upscope $end | $upscope $end | ||||||
| $scope struct \[10] $end | $scope struct [10] $end | ||||||
| $scope struct mem $end | $scope struct mem $end | ||||||
| $var reg 8 C \0 $end | $var reg 8 C \0 $end | ||||||
| $var reg 8 S \1 $end | $var reg 8 S \1 $end | ||||||
| $upscope $end | $upscope $end | ||||||
| $upscope $end | $upscope $end | ||||||
| $scope struct \[11] $end | $scope struct [11] $end | ||||||
| $scope struct mem $end | $scope struct mem $end | ||||||
| $var reg 8 D \0 $end | $var reg 8 D \0 $end | ||||||
| $var reg 8 T \1 $end | $var reg 8 T \1 $end | ||||||
| $upscope $end | $upscope $end | ||||||
| $upscope $end | $upscope $end | ||||||
| $scope struct \[12] $end | $scope struct [12] $end | ||||||
| $scope struct mem $end | $scope struct mem $end | ||||||
| $var reg 8 E \0 $end | $var reg 8 E \0 $end | ||||||
| $var reg 8 U \1 $end | $var reg 8 U \1 $end | ||||||
| $upscope $end | $upscope $end | ||||||
| $upscope $end | $upscope $end | ||||||
| $scope struct \[13] $end | $scope struct [13] $end | ||||||
| $scope struct mem $end | $scope struct mem $end | ||||||
| $var reg 8 F \0 $end | $var reg 8 F \0 $end | ||||||
| $var reg 8 V \1 $end | $var reg 8 V \1 $end | ||||||
| $upscope $end | $upscope $end | ||||||
| $upscope $end | $upscope $end | ||||||
| $scope struct \[14] $end | $scope struct [14] $end | ||||||
| $scope struct mem $end | $scope struct mem $end | ||||||
| $var reg 8 G \0 $end | $var reg 8 G \0 $end | ||||||
| $var reg 8 W \1 $end | $var reg 8 W \1 $end | ||||||
| $upscope $end | $upscope $end | ||||||
| $upscope $end | $upscope $end | ||||||
| $scope struct \[15] $end | $scope struct [15] $end | ||||||
| $scope struct mem $end | $scope struct mem $end | ||||||
| $var reg 8 H \0 $end | $var reg 8 H \0 $end | ||||||
| $var reg 8 X \1 $end | $var reg 8 X \1 $end | ||||||
|  |  | ||||||
|  | @ -11,31 +11,31 @@ $var wire 1 ' wmask $end | ||||||
| $upscope $end | $upscope $end | ||||||
| $scope struct mem $end | $scope struct mem $end | ||||||
| $scope struct contents $end | $scope struct contents $end | ||||||
| $scope struct \[0] $end | $scope struct [0] $end | ||||||
| $scope struct mem $end | $scope struct mem $end | ||||||
| $var string 1 1 \$tag $end | $var string 1 1 \$tag $end | ||||||
| $var reg 1 6 HdlSome $end | $var reg 1 6 HdlSome $end | ||||||
| $upscope $end | $upscope $end | ||||||
| $upscope $end | $upscope $end | ||||||
| $scope struct \[1] $end | $scope struct [1] $end | ||||||
| $scope struct mem $end | $scope struct mem $end | ||||||
| $var string 1 2 \$tag $end | $var string 1 2 \$tag $end | ||||||
| $var reg 1 7 HdlSome $end | $var reg 1 7 HdlSome $end | ||||||
| $upscope $end | $upscope $end | ||||||
| $upscope $end | $upscope $end | ||||||
| $scope struct \[2] $end | $scope struct [2] $end | ||||||
| $scope struct mem $end | $scope struct mem $end | ||||||
| $var string 1 3 \$tag $end | $var string 1 3 \$tag $end | ||||||
| $var reg 1 8 HdlSome $end | $var reg 1 8 HdlSome $end | ||||||
| $upscope $end | $upscope $end | ||||||
| $upscope $end | $upscope $end | ||||||
| $scope struct \[3] $end | $scope struct [3] $end | ||||||
| $scope struct mem $end | $scope struct mem $end | ||||||
| $var string 1 4 \$tag $end | $var string 1 4 \$tag $end | ||||||
| $var reg 1 9 HdlSome $end | $var reg 1 9 HdlSome $end | ||||||
| $upscope $end | $upscope $end | ||||||
| $upscope $end | $upscope $end | ||||||
| $scope struct \[4] $end | $scope struct [4] $end | ||||||
| $scope struct mem $end | $scope struct mem $end | ||||||
| $var string 1 5 \$tag $end | $var string 1 5 \$tag $end | ||||||
| $var reg 1 : HdlSome $end | $var reg 1 : HdlSome $end | ||||||
|  |  | ||||||
|  | @ -42,7 +42,7 @@ $upscope $end | ||||||
| $upscope $end | $upscope $end | ||||||
| $scope struct mem $end | $scope struct mem $end | ||||||
| $scope struct contents $end | $scope struct contents $end | ||||||
| $scope struct \[0] $end | $scope struct [0] $end | ||||||
| $scope struct mem $end | $scope struct mem $end | ||||||
| $var reg 8 ] \[0] $end | $var reg 8 ] \[0] $end | ||||||
| $var reg 8 e \[1] $end | $var reg 8 e \[1] $end | ||||||
|  | @ -54,7 +54,7 @@ $var reg 8 /" \[6] $end | ||||||
| $var reg 8 7" \[7] $end | $var reg 8 7" \[7] $end | ||||||
| $upscope $end | $upscope $end | ||||||
| $upscope $end | $upscope $end | ||||||
| $scope struct \[1] $end | $scope struct [1] $end | ||||||
| $scope struct mem $end | $scope struct mem $end | ||||||
| $var reg 8 ^ \[0] $end | $var reg 8 ^ \[0] $end | ||||||
| $var reg 8 f \[1] $end | $var reg 8 f \[1] $end | ||||||
|  | @ -66,7 +66,7 @@ $var reg 8 0" \[6] $end | ||||||
| $var reg 8 8" \[7] $end | $var reg 8 8" \[7] $end | ||||||
| $upscope $end | $upscope $end | ||||||
| $upscope $end | $upscope $end | ||||||
| $scope struct \[2] $end | $scope struct [2] $end | ||||||
| $scope struct mem $end | $scope struct mem $end | ||||||
| $var reg 8 _ \[0] $end | $var reg 8 _ \[0] $end | ||||||
| $var reg 8 g \[1] $end | $var reg 8 g \[1] $end | ||||||
|  | @ -78,7 +78,7 @@ $var reg 8 1" \[6] $end | ||||||
| $var reg 8 9" \[7] $end | $var reg 8 9" \[7] $end | ||||||
| $upscope $end | $upscope $end | ||||||
| $upscope $end | $upscope $end | ||||||
| $scope struct \[3] $end | $scope struct [3] $end | ||||||
| $scope struct mem $end | $scope struct mem $end | ||||||
| $var reg 8 ` \[0] $end | $var reg 8 ` \[0] $end | ||||||
| $var reg 8 h \[1] $end | $var reg 8 h \[1] $end | ||||||
|  | @ -90,7 +90,7 @@ $var reg 8 2" \[6] $end | ||||||
| $var reg 8 :" \[7] $end | $var reg 8 :" \[7] $end | ||||||
| $upscope $end | $upscope $end | ||||||
| $upscope $end | $upscope $end | ||||||
| $scope struct \[4] $end | $scope struct [4] $end | ||||||
| $scope struct mem $end | $scope struct mem $end | ||||||
| $var reg 8 a \[0] $end | $var reg 8 a \[0] $end | ||||||
| $var reg 8 i \[1] $end | $var reg 8 i \[1] $end | ||||||
|  | @ -102,7 +102,7 @@ $var reg 8 3" \[6] $end | ||||||
| $var reg 8 ;" \[7] $end | $var reg 8 ;" \[7] $end | ||||||
| $upscope $end | $upscope $end | ||||||
| $upscope $end | $upscope $end | ||||||
| $scope struct \[5] $end | $scope struct [5] $end | ||||||
| $scope struct mem $end | $scope struct mem $end | ||||||
| $var reg 8 b \[0] $end | $var reg 8 b \[0] $end | ||||||
| $var reg 8 j \[1] $end | $var reg 8 j \[1] $end | ||||||
|  | @ -114,7 +114,7 @@ $var reg 8 4" \[6] $end | ||||||
| $var reg 8 <" \[7] $end | $var reg 8 <" \[7] $end | ||||||
| $upscope $end | $upscope $end | ||||||
| $upscope $end | $upscope $end | ||||||
| $scope struct \[6] $end | $scope struct [6] $end | ||||||
| $scope struct mem $end | $scope struct mem $end | ||||||
| $var reg 8 c \[0] $end | $var reg 8 c \[0] $end | ||||||
| $var reg 8 k \[1] $end | $var reg 8 k \[1] $end | ||||||
|  | @ -126,7 +126,7 @@ $var reg 8 5" \[6] $end | ||||||
| $var reg 8 =" \[7] $end | $var reg 8 =" \[7] $end | ||||||
| $upscope $end | $upscope $end | ||||||
| $upscope $end | $upscope $end | ||||||
| $scope struct \[7] $end | $scope struct [7] $end | ||||||
| $scope struct mem $end | $scope struct mem $end | ||||||
| $var reg 8 d \[0] $end | $var reg 8 d \[0] $end | ||||||
| $var reg 8 l \[1] $end | $var reg 8 l \[1] $end | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue