forked from libre-chip/fayalite
		
	Compare commits
	
		
			11 commits
		
	
	
		
			903ca1bf30
			...
			c16726cee6
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| c16726cee6 | |||
| b63676d0ca | |||
| 7005fa3330 | |||
| 2ab8428062 | |||
| 9b06019bf5 | |||
| 36bad52978 | |||
| 21c73051ec | |||
| 304d8da0e8 | |||
| 2af38de900 | |||
| c756aeec70 | |||
|  | 2e7d685dc7 | 
					 17 changed files with 13276 additions and 513 deletions
				
			
		
							
								
								
									
										8
									
								
								Cargo.lock
									
										
									
										generated
									
									
									
								
							
							
						
						
									
										8
									
								
								Cargo.lock
									
										
									
										generated
									
									
									
								
							|  | @ -543,9 +543,9 @@ dependencies = [ | |||
| 
 | ||||
| [[package]] | ||||
| name = "proc-macro2" | ||||
| version = "1.0.83" | ||||
| version = "1.0.92" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "0b33eb56c327dec362a9e55b3ad14f9d2f0904fb5a5b03b513ab5465399e9f43" | ||||
| checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0" | ||||
| dependencies = [ | ||||
|  "unicode-ident", | ||||
| ] | ||||
|  | @ -647,9 +647,9 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" | |||
| 
 | ||||
| [[package]] | ||||
| name = "syn" | ||||
| version = "2.0.66" | ||||
| version = "2.0.93" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "c42f3f41a2de00b01c0aaad383c5a45241efc8b2d1eda5661812fda5f3cdcff5" | ||||
| checksum = "9c786062daee0d6db1132800e623df74274a0a87322d8e183338e01b3d98d058" | ||||
| dependencies = [ | ||||
|  "proc-macro2", | ||||
|  "quote", | ||||
|  |  | |||
|  | @ -37,7 +37,7 @@ quote = "1.0.36" | |||
| serde = { version = "1.0.202", features = ["derive"] } | ||||
| serde_json = { version = "1.0.117", features = ["preserve_order"] } | ||||
| sha2 = "0.10.8" | ||||
| syn = { version = "2.0.66", features = ["full", "fold", "visit", "extra-traits"] } | ||||
| syn = { version = "2.0.93", features = ["full", "fold", "visit", "extra-traits"] } | ||||
| tempfile = "3.10.1" | ||||
| thiserror = "1.0.61" | ||||
| trybuild = "1.0" | ||||
|  |  | |||
|  | @ -3,14 +3,20 @@ | |||
| #![cfg_attr(test, recursion_limit = "512")] | ||||
| use proc_macro2::{Span, TokenStream}; | ||||
| use quote::{quote, ToTokens}; | ||||
| use std::io::{ErrorKind, Write}; | ||||
| use std::{ | ||||
|     collections::{hash_map::Entry, HashMap}, | ||||
|     io::{ErrorKind, Write}, | ||||
| }; | ||||
| use syn::{ | ||||
|     bracketed, parenthesized, | ||||
|     bracketed, | ||||
|     ext::IdentExt, | ||||
|     parenthesized, | ||||
|     parse::{Parse, ParseStream, Parser}, | ||||
|     parse_quote, | ||||
|     punctuated::Pair, | ||||
|     punctuated::{Pair, Punctuated}, | ||||
|     spanned::Spanned, | ||||
|     AttrStyle, Attribute, Error, Item, ItemFn, Token, | ||||
|     token::{Bracket, Paren}, | ||||
|     AttrStyle, Attribute, Error, Ident, Item, ItemFn, LitBool, LitStr, Meta, Token, | ||||
| }; | ||||
| 
 | ||||
| mod fold; | ||||
|  | @ -19,6 +25,7 @@ mod hdl_enum; | |||
| mod hdl_type_alias; | ||||
| mod hdl_type_common; | ||||
| mod module; | ||||
| mod process_cfg; | ||||
| 
 | ||||
| pub(crate) trait CustomToken: | ||||
|     Copy | ||||
|  | @ -59,6 +66,11 @@ mod kw { | |||
|         }; | ||||
|     } | ||||
| 
 | ||||
|     custom_keyword!(__evaluated_cfgs); | ||||
|     custom_keyword!(all); | ||||
|     custom_keyword!(any); | ||||
|     custom_keyword!(cfg); | ||||
|     custom_keyword!(cfg_attr); | ||||
|     custom_keyword!(clock_domain); | ||||
|     custom_keyword!(connect_inexact); | ||||
|     custom_keyword!(custom_bounds); | ||||
|  | @ -75,6 +87,7 @@ mod kw { | |||
|     custom_keyword!(no_reset); | ||||
|     custom_keyword!(no_runtime_generics); | ||||
|     custom_keyword!(no_static); | ||||
|     custom_keyword!(not); | ||||
|     custom_keyword!(outline_generated); | ||||
|     custom_keyword!(output); | ||||
|     custom_keyword!(reg_builder); | ||||
|  | @ -901,15 +914,346 @@ fn hdl_module_impl(item: ItemFn) -> syn::Result<TokenStream> { | |||
|     Ok(contents) | ||||
| } | ||||
| 
 | ||||
| pub fn hdl_module(attr: TokenStream, item: TokenStream) -> syn::Result<TokenStream> { | ||||
|     let kw = kw::hdl_module::default(); | ||||
|     hdl_module_impl(syn::parse2(quote! { #[#kw(#attr)] #item })?) | ||||
| #[derive(Clone, PartialEq, Eq, Hash, Debug)] | ||||
| pub(crate) enum CfgExpr { | ||||
|     Option { | ||||
|         ident: Ident, | ||||
|         value: Option<(Token![=], LitStr)>, | ||||
|     }, | ||||
|     All { | ||||
|         all: kw::all, | ||||
|         paren: Paren, | ||||
|         exprs: Punctuated<CfgExpr, Token![,]>, | ||||
|     }, | ||||
|     Any { | ||||
|         any: kw::any, | ||||
|         paren: Paren, | ||||
|         exprs: Punctuated<CfgExpr, Token![,]>, | ||||
|     }, | ||||
|     Not { | ||||
|         not: kw::not, | ||||
|         paren: Paren, | ||||
|         expr: Box<CfgExpr>, | ||||
|         trailing_comma: Option<Token![,]>, | ||||
|     }, | ||||
| } | ||||
| 
 | ||||
| pub fn hdl_attr(attr: TokenStream, item: TokenStream) -> syn::Result<TokenStream> { | ||||
|     let kw = kw::hdl::default(); | ||||
|     let item = quote! { #[#kw(#attr)] #item }; | ||||
|     let item = syn::parse2::<Item>(item)?; | ||||
| impl Parse for CfgExpr { | ||||
|     fn parse(input: ParseStream) -> syn::Result<Self> { | ||||
|         match input.cursor().ident() { | ||||
|             Some((_, cursor)) if cursor.eof() => { | ||||
|                 return Ok(CfgExpr::Option { | ||||
|                     ident: input.call(Ident::parse_any)?, | ||||
|                     value: None, | ||||
|                 }); | ||||
|             } | ||||
|             _ => {} | ||||
|         } | ||||
|         if input.peek(Ident::peek_any) && input.peek2(Token![=]) { | ||||
|             return Ok(CfgExpr::Option { | ||||
|                 ident: input.call(Ident::parse_any)?, | ||||
|                 value: Some((input.parse()?, input.parse()?)), | ||||
|             }); | ||||
|         } | ||||
|         let contents; | ||||
|         if input.peek(kw::all) { | ||||
|             Ok(CfgExpr::All { | ||||
|                 all: input.parse()?, | ||||
|                 paren: parenthesized!(contents in input), | ||||
|                 exprs: contents.call(Punctuated::parse_terminated)?, | ||||
|             }) | ||||
|         } else if input.peek(kw::any) { | ||||
|             Ok(CfgExpr::Any { | ||||
|                 any: input.parse()?, | ||||
|                 paren: parenthesized!(contents in input), | ||||
|                 exprs: contents.call(Punctuated::parse_terminated)?, | ||||
|             }) | ||||
|         } else if input.peek(kw::not) { | ||||
|             Ok(CfgExpr::Not { | ||||
|                 not: input.parse()?, | ||||
|                 paren: parenthesized!(contents in input), | ||||
|                 expr: contents.parse()?, | ||||
|                 trailing_comma: contents.parse()?, | ||||
|             }) | ||||
|         } else { | ||||
|             Err(input.error("expected cfg-pattern")) | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl ToTokens for CfgExpr { | ||||
|     fn to_tokens(&self, tokens: &mut TokenStream) { | ||||
|         match self { | ||||
|             CfgExpr::Option { ident, value } => { | ||||
|                 ident.to_tokens(tokens); | ||||
|                 if let Some((eq, value)) = value { | ||||
|                     eq.to_tokens(tokens); | ||||
|                     value.to_tokens(tokens); | ||||
|                 } | ||||
|             } | ||||
|             CfgExpr::All { all, paren, exprs } => { | ||||
|                 all.to_tokens(tokens); | ||||
|                 paren.surround(tokens, |tokens| exprs.to_tokens(tokens)); | ||||
|             } | ||||
|             CfgExpr::Any { any, paren, exprs } => { | ||||
|                 any.to_tokens(tokens); | ||||
|                 paren.surround(tokens, |tokens| exprs.to_tokens(tokens)); | ||||
|             } | ||||
|             CfgExpr::Not { | ||||
|                 not, | ||||
|                 paren, | ||||
|                 expr, | ||||
|                 trailing_comma, | ||||
|             } => { | ||||
|                 not.to_tokens(tokens); | ||||
|                 paren.surround(tokens, |tokens| { | ||||
|                     expr.to_tokens(tokens); | ||||
|                     trailing_comma.to_tokens(tokens); | ||||
|                 }); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[derive(Clone, PartialEq, Eq, Hash, Debug)] | ||||
| pub(crate) struct Cfg { | ||||
|     cfg: kw::cfg, | ||||
|     paren: Paren, | ||||
|     expr: CfgExpr, | ||||
|     trailing_comma: Option<Token![,]>, | ||||
| } | ||||
| 
 | ||||
| impl Cfg { | ||||
|     fn parse_meta(meta: &Meta) -> syn::Result<Self> { | ||||
|         syn::parse2(meta.to_token_stream()) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl ToTokens for Cfg { | ||||
|     fn to_tokens(&self, tokens: &mut TokenStream) { | ||||
|         let Self { | ||||
|             cfg, | ||||
|             paren, | ||||
|             expr, | ||||
|             trailing_comma, | ||||
|         } = self; | ||||
|         cfg.to_tokens(tokens); | ||||
|         paren.surround(tokens, |tokens| { | ||||
|             expr.to_tokens(tokens); | ||||
|             trailing_comma.to_tokens(tokens); | ||||
|         }); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl Parse for Cfg { | ||||
|     fn parse(input: ParseStream) -> syn::Result<Self> { | ||||
|         let contents; | ||||
|         Ok(Self { | ||||
|             cfg: input.parse()?, | ||||
|             paren: parenthesized!(contents in input), | ||||
|             expr: contents.parse()?, | ||||
|             trailing_comma: contents.parse()?, | ||||
|         }) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[derive(Clone, PartialEq, Eq, Hash, Debug)] | ||||
| pub(crate) struct CfgAttr { | ||||
|     cfg_attr: kw::cfg_attr, | ||||
|     paren: Paren, | ||||
|     expr: CfgExpr, | ||||
|     comma: Token![,], | ||||
|     attrs: Punctuated<Meta, Token![,]>, | ||||
| } | ||||
| 
 | ||||
| impl CfgAttr { | ||||
|     pub(crate) fn to_cfg(&self) -> Cfg { | ||||
|         Cfg { | ||||
|             cfg: kw::cfg(self.cfg_attr.span), | ||||
|             paren: self.paren, | ||||
|             expr: self.expr.clone(), | ||||
|             trailing_comma: None, | ||||
|         } | ||||
|     } | ||||
|     fn parse_meta(meta: &Meta) -> syn::Result<Self> { | ||||
|         syn::parse2(meta.to_token_stream()) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl Parse for CfgAttr { | ||||
|     fn parse(input: ParseStream) -> syn::Result<Self> { | ||||
|         let contents; | ||||
|         Ok(Self { | ||||
|             cfg_attr: input.parse()?, | ||||
|             paren: parenthesized!(contents in input), | ||||
|             expr: contents.parse()?, | ||||
|             comma: contents.parse()?, | ||||
|             attrs: contents.call(Punctuated::parse_terminated)?, | ||||
|         }) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| pub(crate) struct CfgAndValue { | ||||
|     cfg: Cfg, | ||||
|     eq_token: Token![=], | ||||
|     value: LitBool, | ||||
| } | ||||
| 
 | ||||
| impl Parse for CfgAndValue { | ||||
|     fn parse(input: ParseStream) -> syn::Result<Self> { | ||||
|         Ok(Self { | ||||
|             cfg: input.parse()?, | ||||
|             eq_token: input.parse()?, | ||||
|             value: input.parse()?, | ||||
|         }) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| pub(crate) struct Cfgs<T> { | ||||
|     pub(crate) bracket: Bracket, | ||||
|     pub(crate) cfgs_map: HashMap<Cfg, T>, | ||||
|     pub(crate) cfgs_list: Vec<Cfg>, | ||||
| } | ||||
| 
 | ||||
| impl<T> Default for Cfgs<T> { | ||||
|     fn default() -> Self { | ||||
|         Self { | ||||
|             bracket: Default::default(), | ||||
|             cfgs_map: Default::default(), | ||||
|             cfgs_list: Default::default(), | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<T> Cfgs<T> { | ||||
|     fn insert_cfg(&mut self, cfg: Cfg, value: T) { | ||||
|         match self.cfgs_map.entry(cfg) { | ||||
|             Entry::Occupied(_) => {} | ||||
|             Entry::Vacant(entry) => { | ||||
|                 self.cfgs_list.push(entry.key().clone()); | ||||
|                 entry.insert(value); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl Parse for Cfgs<bool> { | ||||
|     fn parse(input: ParseStream) -> syn::Result<Self> { | ||||
|         let contents; | ||||
|         let bracket = bracketed!(contents in input); | ||||
|         let mut cfgs_map = HashMap::new(); | ||||
|         let mut cfgs_list = Vec::new(); | ||||
|         for CfgAndValue { | ||||
|             cfg, | ||||
|             eq_token, | ||||
|             value, | ||||
|         } in contents.call(Punctuated::<CfgAndValue, Token![,]>::parse_terminated)? | ||||
|         { | ||||
|             let _ = eq_token; | ||||
|             match cfgs_map.entry(cfg) { | ||||
|                 Entry::Occupied(_) => {} | ||||
|                 Entry::Vacant(entry) => { | ||||
|                     cfgs_list.push(entry.key().clone()); | ||||
|                     entry.insert(value.value); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         Ok(Self { | ||||
|             bracket, | ||||
|             cfgs_map, | ||||
|             cfgs_list, | ||||
|         }) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl Parse for Cfgs<()> { | ||||
|     fn parse(input: ParseStream) -> syn::Result<Self> { | ||||
|         let contents; | ||||
|         let bracket = bracketed!(contents in input); | ||||
|         let mut cfgs_map = HashMap::new(); | ||||
|         let mut cfgs_list = Vec::new(); | ||||
|         for cfg in contents.call(Punctuated::<Cfg, Token![,]>::parse_terminated)? { | ||||
|             match cfgs_map.entry(cfg) { | ||||
|                 Entry::Occupied(_) => {} | ||||
|                 Entry::Vacant(entry) => { | ||||
|                     cfgs_list.push(entry.key().clone()); | ||||
|                     entry.insert(()); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         Ok(Self { | ||||
|             bracket, | ||||
|             cfgs_map, | ||||
|             cfgs_list, | ||||
|         }) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl ToTokens for Cfgs<()> { | ||||
|     fn to_tokens(&self, tokens: &mut TokenStream) { | ||||
|         let Self { | ||||
|             bracket, | ||||
|             cfgs_map: _, | ||||
|             cfgs_list, | ||||
|         } = self; | ||||
|         bracket.surround(tokens, |tokens| { | ||||
|             for cfg in cfgs_list { | ||||
|                 cfg.to_tokens(tokens); | ||||
|                 <Token![,]>::default().to_tokens(tokens); | ||||
|             } | ||||
|         }); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| fn hdl_main( | ||||
|     kw: impl CustomToken, | ||||
|     attr: TokenStream, | ||||
|     item: TokenStream, | ||||
| ) -> syn::Result<TokenStream> { | ||||
|     fn parse_evaluated_cfgs_attr<R>( | ||||
|         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| { | ||||
|             let peek = input.fork(); | ||||
|             if parse_evaluated_cfgs_attr(&peek, |_| Ok(())).is_ok() { | ||||
|                 let evaluated_cfgs = parse_evaluated_cfgs_attr(input, Cfgs::<bool>::parse)?; | ||||
|                 Ok((Some(evaluated_cfgs), input.parse()?)) | ||||
|             } else { | ||||
|                 Ok((None, input.parse()?)) | ||||
|             } | ||||
|         }, | ||||
|         item, | ||||
|     )?; | ||||
|     let cfgs = if let Some(cfgs) = evaluated_cfgs { | ||||
|         cfgs | ||||
|     } else { | ||||
|         let cfgs = process_cfg::collect_cfgs(syn::parse2(item.clone())?)?; | ||||
|         if cfgs.cfgs_list.is_empty() { | ||||
|             Cfgs::default() | ||||
|         } else { | ||||
|             return Ok(quote! { | ||||
|                 ::fayalite::__cfg_expansion_helper! { | ||||
|                     [] | ||||
|                     #cfgs | ||||
|                     {#[::fayalite::#kw(#attr)]} { #item } | ||||
|                 } | ||||
|             }); | ||||
|         } | ||||
|     }; | ||||
|     let item = syn::parse2(quote! { #[#kw(#attr)] #item })?; | ||||
|     let Some(item) = process_cfg::process_cfgs(item, cfgs)? else { | ||||
|         return Ok(TokenStream::new()); | ||||
|     }; | ||||
|     match item { | ||||
|         Item::Enum(item) => hdl_enum::hdl_enum(item), | ||||
|         Item::Struct(item) => hdl_bundle::hdl_bundle(item), | ||||
|  | @ -921,3 +1265,11 @@ pub fn hdl_attr(attr: TokenStream, item: TokenStream) -> syn::Result<TokenStream | |||
|         )), | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| pub fn hdl_module(attr: TokenStream, item: TokenStream) -> syn::Result<TokenStream> { | ||||
|     hdl_main(kw::hdl_module::default(), attr, item) | ||||
| } | ||||
| 
 | ||||
| pub fn hdl_attr(attr: TokenStream, item: TokenStream) -> syn::Result<TokenStream> { | ||||
|     hdl_main(kw::hdl::default(), attr, item) | ||||
| } | ||||
|  |  | |||
							
								
								
									
										2527
									
								
								crates/fayalite-proc-macros-impl/src/process_cfg.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										2527
									
								
								crates/fayalite-proc-macros-impl/src/process_cfg.rs
									
										
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							|  | @ -5,6 +5,9 @@ use std::{env, fs, path::Path}; | |||
| 
 | ||||
| fn main() { | ||||
|     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"; | ||||
|     println!("cargo::rerun-if-changed={path}"); | ||||
|     println!("cargo::rerun-if-changed=build.rs"); | ||||
|  |  | |||
|  | @ -4,12 +4,14 @@ | |||
| use crate::{ | ||||
|     expr::{ops::BundleLiteral, Expr, ToExpr}, | ||||
|     intern::{Intern, Interned}, | ||||
|     sim::{SimValue, ToSimValue}, | ||||
|     source_location::SourceLocation, | ||||
|     ty::{ | ||||
|         impl_match_variant_as_self, CanonicalType, MatchVariantWithoutScope, StaticType, Type, | ||||
|         TypeProperties, TypeWithDeref, | ||||
|     }, | ||||
| }; | ||||
| use bitvec::vec::BitVec; | ||||
| use hashbrown::HashMap; | ||||
| use std::{fmt, marker::PhantomData}; | ||||
| 
 | ||||
|  | @ -323,7 +325,7 @@ macro_rules! impl_tuple_builder_fields { | |||
| } | ||||
| 
 | ||||
| macro_rules! impl_tuples { | ||||
|     ([$({#[num = $num:literal, field = $field:ident] $var:ident: $T:ident})*] []) => { | ||||
|     ([$({#[num = $num:literal, field = $field:ident, ty = $ty_var:ident: $Ty:ident] $var:ident: $T:ident})*] []) => { | ||||
|         impl_tuple_builder_fields! { | ||||
|             {} | ||||
|             [$({ | ||||
|  | @ -423,6 +425,79 @@ macro_rules! impl_tuples { | |||
|                 BundleLiteral::new(ty, field_values[..].intern()).to_expr() | ||||
|             } | ||||
|         } | ||||
|         impl<$($T: ToSimValue<CanonicalType>,)*> ToSimValue<CanonicalType> for ($($T,)*) { | ||||
|             #[track_caller] | ||||
|             fn to_sim_value(&self, ty: CanonicalType) -> SimValue<CanonicalType> { | ||||
|                 ToSimValue::<Bundle>::to_sim_value(self, Bundle::from_canonical(ty)).into_canonical() | ||||
|             } | ||||
|             #[track_caller] | ||||
|             fn into_sim_value(self, ty: CanonicalType) -> SimValue<CanonicalType> | ||||
|             { | ||||
|                 ToSimValue::<Bundle>::into_sim_value(self, Bundle::from_canonical(ty)).into_canonical() | ||||
|             } | ||||
|             #[track_caller] | ||||
|             fn box_into_sim_value(self: Box<Self>, ty: CanonicalType) -> SimValue<CanonicalType> { | ||||
|                 ToSimValue::<Bundle>::box_into_sim_value(self, Bundle::from_canonical(ty)).into_canonical() | ||||
|             } | ||||
|         } | ||||
|         impl<$($T: ToSimValue<CanonicalType>,)*> ToSimValue<Bundle> for ($($T,)*) { | ||||
|             #[track_caller] | ||||
|             fn to_sim_value(&self, ty: Bundle) -> SimValue<Bundle> { | ||||
|                 let ($($var,)*) = self; | ||||
|                 let [$($ty_var,)*] = *ty.fields() else { | ||||
|                     panic!("bundle has wrong number of fields"); | ||||
|                 }; | ||||
|                 $(let $var = $var.to_sim_value($ty_var.ty);)* | ||||
|                 ToSimValue::into_sim_value(($($var,)*), ty) | ||||
|             } | ||||
|             #[track_caller] | ||||
|             fn into_sim_value(self, ty: Bundle) -> SimValue<Bundle> { | ||||
|                 #![allow(unused_mut)] | ||||
|                 #![allow(clippy::unused_unit)] | ||||
|                 let ($($var,)*) = self; | ||||
|                 let [$($ty_var,)*] = *ty.fields() else { | ||||
|                     panic!("bundle has wrong number of fields"); | ||||
|                 }; | ||||
|                 let mut bits: Option<BitVec> = None; | ||||
|                 $(let $var = $var.into_sim_value($ty_var.ty); | ||||
|                 assert_eq!($var.ty(), $ty_var.ty); | ||||
|                 if !$var.bits().is_empty() { | ||||
|                     if let Some(bits) = &mut bits { | ||||
|                         bits.extend_from_bitslice($var.bits()); | ||||
|                     } else { | ||||
|                         let mut $var = $var.into_bits(); | ||||
|                         $var.reserve(ty.type_properties().bit_width - $var.len()); | ||||
|                         bits = Some($var); | ||||
|                     } | ||||
|                 } | ||||
|                 )* | ||||
|                 bits.unwrap_or_else(BitVec::new).into_sim_value(ty) | ||||
|             } | ||||
|             #[track_caller] | ||||
|             fn box_into_sim_value(self: Box<Self>, ty: Bundle) -> SimValue<Bundle> { | ||||
|                 Self::into_sim_value(*self, ty) | ||||
|             } | ||||
|         } | ||||
|         impl<$($T: ToSimValue<$Ty>, $Ty: Type,)*> ToSimValue<($($Ty,)*)> for ($($T,)*) { | ||||
|             #[track_caller] | ||||
|             fn to_sim_value(&self, ty: ($($Ty,)*)) -> SimValue<($($Ty,)*)> { | ||||
|                 let ($($var,)*) = self; | ||||
|                 let ($($ty_var,)*) = ty; | ||||
|                 $(let $var = $var.to_sim_value($ty_var).into_canonical();)* | ||||
|                 SimValue::from_canonical(ToSimValue::into_sim_value(($($var,)*), ty.canonical())) | ||||
|             } | ||||
|             #[track_caller] | ||||
|             fn into_sim_value(self, ty: ($($Ty,)*)) -> SimValue<($($Ty,)*)> { | ||||
|                 let ($($var,)*) = self; | ||||
|                 let ($($ty_var,)*) = ty; | ||||
|                 $(let $var = $var.into_sim_value($ty_var).into_canonical();)* | ||||
|                 SimValue::from_canonical(ToSimValue::into_sim_value(($($var,)*), ty.canonical())) | ||||
|             } | ||||
|             #[track_caller] | ||||
|             fn box_into_sim_value(self: Box<Self>, ty: ($($Ty,)*)) -> SimValue<($($Ty,)*)> { | ||||
|                 Self::into_sim_value(*self, ty) | ||||
|             } | ||||
|         } | ||||
|     }; | ||||
|     ([$($lhs:tt)*] [$rhs_first:tt $($rhs:tt)*]) => { | ||||
|         impl_tuples!([$($lhs)*] []); | ||||
|  | @ -432,18 +507,18 @@ macro_rules! impl_tuples { | |||
| 
 | ||||
| impl_tuples! { | ||||
|     [] [ | ||||
|         {#[num = 0, field = field_0] v0: T0} | ||||
|         {#[num = 1, field = field_1] v1: T1} | ||||
|         {#[num = 2, field = field_2] v2: T2} | ||||
|         {#[num = 3, field = field_3] v3: T3} | ||||
|         {#[num = 4, field = field_4] v4: T4} | ||||
|         {#[num = 5, field = field_5] v5: T5} | ||||
|         {#[num = 6, field = field_6] v6: T6} | ||||
|         {#[num = 7, field = field_7] v7: T7} | ||||
|         {#[num = 8, field = field_8] v8: T8} | ||||
|         {#[num = 9, field = field_9] v9: T9} | ||||
|         {#[num = 10, field = field_10] v10: T10} | ||||
|         {#[num = 11, field = field_11] v11: T11} | ||||
|         {#[num = 0, field = field_0, ty = ty0: Ty0] v0: T0} | ||||
|         {#[num = 1, field = field_1, ty = ty1: Ty1] v1: T1} | ||||
|         {#[num = 2, field = field_2, ty = ty2: Ty2] v2: T2} | ||||
|         {#[num = 3, field = field_3, ty = ty3: Ty3] v3: T3} | ||||
|         {#[num = 4, field = field_4, ty = ty4: Ty4] v4: T4} | ||||
|         {#[num = 5, field = field_5, ty = ty5: Ty5] v5: T5} | ||||
|         {#[num = 6, field = field_6, ty = ty6: Ty6] v6: T6} | ||||
|         {#[num = 7, field = field_7, ty = ty7: Ty7] v7: T7} | ||||
|         {#[num = 8, field = field_8, ty = ty8: Ty8] v8: T8} | ||||
|         {#[num = 9, field = field_9, ty = ty9: Ty9] v9: T9} | ||||
|         {#[num = 10, field = field_10, ty = ty10: Ty10] v10: T10} | ||||
|         {#[num = 11, field = field_11, ty = ty11: Ty11] v11: T11} | ||||
|     ] | ||||
| } | ||||
| 
 | ||||
|  | @ -528,3 +603,27 @@ impl<T: ?Sized + Send + Sync + 'static> ToExpr for PhantomData<T> { | |||
|         BundleLiteral::new(PhantomData, Interned::default()).to_expr() | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<T: ?Sized + Send + Sync + 'static> ToSimValue<Self> for PhantomData<T> { | ||||
|     #[track_caller] | ||||
|     fn to_sim_value(&self, ty: Self) -> SimValue<Self> { | ||||
|         ToSimValue::into_sim_value(BitVec::new(), ty) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<T: ?Sized> ToSimValue<Bundle> for PhantomData<T> { | ||||
|     #[track_caller] | ||||
|     fn to_sim_value(&self, ty: Bundle) -> SimValue<Bundle> { | ||||
|         assert!(ty.fields().is_empty()); | ||||
|         ToSimValue::into_sim_value(BitVec::new(), ty) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<T: ?Sized> ToSimValue<CanonicalType> for PhantomData<T> { | ||||
|     #[track_caller] | ||||
|     fn to_sim_value(&self, ty: CanonicalType) -> SimValue<CanonicalType> { | ||||
|         let ty = Bundle::from_canonical(ty); | ||||
|         assert!(ty.fields().is_empty()); | ||||
|         ToSimValue::into_sim_value(BitVec::new(), ty).into_canonical() | ||||
|     } | ||||
| } | ||||
|  |  | |||
|  | @ -11,6 +11,59 @@ extern crate self as fayalite; | |||
| #[doc(hidden)] | ||||
| pub use std as __std; | ||||
| 
 | ||||
| #[doc(hidden)] | ||||
| #[macro_export] | ||||
| macro_rules! __cfg_expansion_helper { | ||||
|     ( | ||||
|         [ | ||||
|             $($evaluated_cfgs:ident($($evaluated_exprs:tt)*) = $evaluated_results:ident,)* | ||||
|         ] | ||||
|         [ | ||||
|             $cfg:ident($($expr:tt)*), | ||||
|             $($unevaluated_cfgs:ident($($unevaluated_exprs:tt)*),)* | ||||
|         ] | ||||
|         // pass as tt so we get right span for attribute
 | ||||
|         $after_evaluation_attr:tt $after_evaluation_body:tt | ||||
|     ) => { | ||||
|         #[$cfg($($expr)*)] | ||||
|         $crate::__cfg_expansion_helper! { | ||||
|             [ | ||||
|                 $($evaluated_cfgs($($evaluated_exprs)*) = $evaluated_results,)* | ||||
|                 $cfg($($expr)*) = true, | ||||
|             ] | ||||
|             [ | ||||
|                 $($unevaluated_cfgs($($unevaluated_exprs)*),)* | ||||
|             ] | ||||
|             $after_evaluation_attr $after_evaluation_body | ||||
|         } | ||||
|         #[$cfg(not($($expr)*))] | ||||
|         $crate::__cfg_expansion_helper! { | ||||
|             [ | ||||
|                 $($evaluated_cfgs($($evaluated_exprs)*) = $evaluated_results,)* | ||||
|                 $cfg($($expr)*) = false, | ||||
|             ] | ||||
|             [ | ||||
|                 $($unevaluated_cfgs($($unevaluated_exprs)*),)* | ||||
|             ] | ||||
|             $after_evaluation_attr $after_evaluation_body | ||||
|         } | ||||
|     }; | ||||
|     ( | ||||
|         [ | ||||
|             $($evaluated_cfgs:ident($($evaluated_exprs:tt)*) = $evaluated_results:ident,)* | ||||
|         ] | ||||
|         [] | ||||
|         // don't use #[...] so we get right span for `#` and `[]` of attribute
 | ||||
|         {$($after_evaluation_attr:tt)*} {$($after_evaluation_body:tt)*} | ||||
|     ) => { | ||||
|         $($after_evaluation_attr)* | ||||
|         #[__evaluated_cfgs([ | ||||
|             $($evaluated_cfgs($($evaluated_exprs)*) = $evaluated_results,)* | ||||
|         ])] | ||||
|         $($after_evaluation_body)* | ||||
|     }; | ||||
| } | ||||
| 
 | ||||
| #[doc(inline)] | ||||
| /// The `#[hdl_module]` attribute is applied to a Rust function so that that function creates
 | ||||
| /// a [`Module`][`::fayalite::module::Module`] when called.
 | ||||
|  |  | |||
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							|  | @ -7,12 +7,13 @@ use fayalite::{ | |||
|     clock::{Clock, ClockDomain}, | ||||
|     expr::{CastTo, HdlPartialEq}, | ||||
|     firrtl::ExportOptions, | ||||
|     formal::{any_seq, formal_reset, hdl_assert, hdl_assume}, | ||||
|     hdl_module, | ||||
|     int::{Bool, UInt}, | ||||
|     module::{connect, connect_any, reg_builder, wire}, | ||||
|     formal::{any_const, any_seq, formal_reset, hdl_assert, hdl_assume}, | ||||
|     hdl, hdl_module, | ||||
|     int::{Bool, DynSize, Size, UInt, UIntType}, | ||||
|     module::{connect, connect_any, instance, memory, reg_builder, wire}, | ||||
|     reset::ToReset, | ||||
|     testing::assert_formal, | ||||
|     ty::StaticType, | ||||
| }; | ||||
| 
 | ||||
| /// Test hidden state
 | ||||
|  | @ -131,3 +132,164 @@ mod hidden_state { | |||
|         ); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| /// Formal verification of designs containing memories
 | ||||
| ///
 | ||||
| /// There is a trick for memories, described in the [Zipcpu blog].
 | ||||
| /// First, select a fixed but arbitrary memory address, monitoring all reads
 | ||||
| /// and writes made to it. Then, assert that anything read from that location
 | ||||
| /// matches the last stored value.
 | ||||
| ///
 | ||||
| /// A difficulty for induction is that the memory represents [hidden_state]. A
 | ||||
| /// solution is to include an additional read port to the memory and assert
 | ||||
| /// that the memory location effectively contains the last stored value.
 | ||||
| /// This additional debug port is present only to assist the proof and is
 | ||||
| /// unused (optimized out) in actual use.
 | ||||
| ///
 | ||||
| /// [Zipcpu blog]: <https://zipcpu.com/zipcpu/2018/07/13/memories.html>
 | ||||
| mod memory { | ||||
|     use super::*; | ||||
| 
 | ||||
|     /// Test a simple 8-bit SRAM model
 | ||||
|     #[test] | ||||
|     fn test_sram() { | ||||
|         #[hdl] | ||||
|         struct WritePort<AddrWidth: Size> { | ||||
|             addr: UIntType<AddrWidth>, | ||||
|             data: UInt<8>, | ||||
|             en: Bool, | ||||
|         } | ||||
|         #[hdl] | ||||
|         struct ReadPort<AddrWidth: Size> { | ||||
|             addr: UIntType<AddrWidth>, | ||||
|             #[hdl(flip)] | ||||
|             data: UInt<8>, | ||||
|         } | ||||
|         /// This debug port is only meant to assist the proof.
 | ||||
|         /// For normal use in a design, a wrapper could be provided,
 | ||||
|         /// omitting this port.
 | ||||
|         /// The implementation is forbidden to use any information
 | ||||
|         /// provided on this port in its internal workings.
 | ||||
|         #[hdl] | ||||
|         struct DebugPort<AddrWidth: Size> { | ||||
|             selected: UIntType<AddrWidth>, | ||||
|             stored: UInt<8>, | ||||
|             wrote: Bool, | ||||
|         } | ||||
|         /// simple 1R1W SRAM model (one asynchronous read port and one
 | ||||
|         /// independent write port) with `n`-bit address width
 | ||||
|         #[hdl_module] | ||||
|         fn example_sram(n: usize) { | ||||
|             #[hdl] | ||||
|             let wr: WritePort<DynSize> = m.input(WritePort[n]); | ||||
|             #[hdl] | ||||
|             let rd: ReadPort<DynSize> = m.input(ReadPort[n]); | ||||
|             #[hdl] | ||||
|             let cd: ClockDomain = m.input(); | ||||
| 
 | ||||
|             // declare and connect the backing memory
 | ||||
|             #[hdl] | ||||
|             let mut mem = memory(); | ||||
|             mem.depth(1 << n); | ||||
|             let read_port = mem.new_read_port(); | ||||
|             let write_port = mem.new_write_port(); | ||||
|             connect(write_port.clk, cd.clk); | ||||
|             connect(write_port.addr, wr.addr); | ||||
|             connect(write_port.en, wr.en); | ||||
|             connect(write_port.data, wr.data); | ||||
|             connect(write_port.mask, true); | ||||
|             connect(read_port.clk, cd.clk); | ||||
|             connect(read_port.addr, rd.addr); | ||||
|             connect(read_port.en, true); | ||||
|             connect(rd.data, read_port.data); | ||||
| 
 | ||||
|             // To assist with induction, ensure that the chosen memory location
 | ||||
|             // really contains, always, the last value written to it.
 | ||||
|             #[hdl] | ||||
|             let dbg: DebugPort<DynSize> = m.input(DebugPort[n]); | ||||
|             let debug_port = mem.new_read_port(); | ||||
|             connect(debug_port.en, true); | ||||
|             connect(debug_port.clk, cd.clk); | ||||
|             connect(debug_port.addr, dbg.selected); | ||||
|             #[hdl] | ||||
|             if dbg.wrote { | ||||
|                 hdl_assert(cd.clk, debug_port.data.cmp_eq(dbg.stored), ""); | ||||
|                 // Try commenting out the assert above, induction will fail.
 | ||||
|                 // Opening the trace, it can be seen that the memory contents
 | ||||
|                 // and the stored value don't match, which is an unreachable
 | ||||
|                 // state. By asserting the above, it will become invalid
 | ||||
|                 // as well, so induction will skip this kind of situation.
 | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         /// formal verification of the SRAM module, parametrized by the
 | ||||
|         /// address bit-width
 | ||||
|         #[hdl_module] | ||||
|         fn test_module(n: usize) { | ||||
|             #[hdl] | ||||
|             let clk: Clock = m.input(); | ||||
|             let cd = #[hdl] | ||||
|             ClockDomain { | ||||
|                 clk, | ||||
|                 rst: formal_reset().to_reset(), | ||||
|             }; | ||||
| 
 | ||||
|             // instantiate the SRAM model, connecting its inputs to
 | ||||
|             // a random sequence
 | ||||
|             #[hdl] | ||||
|             let rd: ReadPort<DynSize> = wire(ReadPort[n]); | ||||
|             connect(rd.addr, any_seq(UInt[n])); | ||||
|             #[hdl] | ||||
|             let wr: WritePort<DynSize> = wire(WritePort[n]); | ||||
|             connect(wr.addr, any_seq(UInt[n])); | ||||
|             connect(wr.data, any_seq(UInt::<8>::TYPE)); | ||||
|             connect(wr.en, any_seq(Bool)); | ||||
|             #[hdl] | ||||
|             let dut = instance(example_sram(n)); | ||||
|             connect(dut.cd, cd); | ||||
|             connect(dut.rd, rd); | ||||
|             connect(dut.wr, wr); | ||||
| 
 | ||||
|             // select a fixed but arbitrary test address
 | ||||
|             #[hdl] | ||||
|             let selected = wire(UInt[n]); | ||||
|             connect(selected, any_const(UInt[n])); | ||||
|             // store the last value written to that address
 | ||||
|             #[hdl] | ||||
|             let stored: UInt<8> = reg_builder().clock_domain(cd).reset(0u8); | ||||
|             // since memories are not initialized, track whether we wrote to the
 | ||||
|             // memory at least once
 | ||||
|             #[hdl] | ||||
|             let wrote: Bool = reg_builder().clock_domain(cd).reset(false); | ||||
|             // on a write, capture the last written value
 | ||||
|             #[hdl] | ||||
|             if wr.en & wr.addr.cmp_eq(selected) { | ||||
|                 connect(stored, wr.data); | ||||
|                 connect(wrote, true); | ||||
|             } | ||||
|             // on a read, assert that the read value is the same which was stored
 | ||||
|             #[hdl] | ||||
|             if rd.addr.cmp_eq(selected) & wrote { | ||||
|                 hdl_assert(clk, rd.data.cmp_eq(stored), ""); | ||||
|             } | ||||
| 
 | ||||
|             // to assist induction, pass our state to the underlying instance
 | ||||
|             let dbg = #[hdl] | ||||
|             DebugPort { | ||||
|                 selected, | ||||
|                 stored, | ||||
|                 wrote, | ||||
|             }; | ||||
|             connect(dut.dbg, dbg); | ||||
|         } | ||||
| 
 | ||||
|         assert_formal( | ||||
|             "sram", | ||||
|             test_module(8), | ||||
|             FormalMode::Prove, | ||||
|             2, | ||||
|             None, | ||||
|             ExportOptions::default(), | ||||
|         ); | ||||
|     } | ||||
| } | ||||
|  |  | |||
|  | @ -4287,3 +4287,61 @@ 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] | ||||
| ",
 | ||||
|     }; | ||||
| } | ||||
|  |  | |||
|  | @ -5,7 +5,8 @@ use fayalite::{ | |||
|     int::UIntValue, | ||||
|     prelude::*, | ||||
|     reset::ResetType, | ||||
|     sim::{time::SimDuration, vcd::VcdWriterDecls, Simulation}, | ||||
|     sim::{time::SimDuration, vcd::VcdWriterDecls, Simulation, ToSimValue}, | ||||
|     ty::StaticType, | ||||
|     util::RcWriter, | ||||
| }; | ||||
| use std::num::NonZeroUsize; | ||||
|  | @ -254,8 +255,14 @@ fn test_shift_register() { | |||
|     let mut sim = Simulation::new(shift_register()); | ||||
|     let mut writer = RcWriter::default(); | ||||
|     sim.add_trace_writer(VcdWriterDecls::new(writer.clone())); | ||||
|     sim.write_clock(sim.io().cd.clk, false); | ||||
|     sim.write_reset(sim.io().cd.rst, true); | ||||
|     sim.write( | ||||
|         sim.io().cd, | ||||
|         #[hdl] | ||||
|         ClockDomain { | ||||
|             clk: false.to_clock(), | ||||
|             rst: true.to_sync_reset(), | ||||
|         }, | ||||
|     ); | ||||
|     sim.write_bool(sim.io().d, false); | ||||
|     sim.advance_time(SimDuration::from_micros(1)); | ||||
|     sim.write_clock(sim.io().cd.clk, true); | ||||
|  | @ -271,7 +278,7 @@ fn test_shift_register() { | |||
|                 assert_eq!( | ||||
|                     *expected, | ||||
|                     sim.read_bool(sim.io().q), | ||||
|                     "cycle: {cycle}\nvcd:\n{}", | ||||
|                     "vcd:\n{}\ncycle: {cycle}", | ||||
|                     String::from_utf8(writer.take()).unwrap(), | ||||
|                 ); | ||||
|             } | ||||
|  | @ -309,6 +316,8 @@ pub fn enums() { | |||
|     let which_out: UInt<2> = m.output(); | ||||
|     #[hdl] | ||||
|     let data_out: UInt<4> = m.output(); | ||||
|     #[hdl] | ||||
|     let b_out: HdlOption<(UInt<1>, Bool)> = m.output(); | ||||
| 
 | ||||
|     #[hdl] | ||||
|     struct MyStruct<T> { | ||||
|  | @ -348,6 +357,8 @@ pub fn enums() { | |||
|         } | ||||
|     } | ||||
| 
 | ||||
|     connect(b_out, HdlNone()); | ||||
| 
 | ||||
|     #[hdl] | ||||
|     match the_reg { | ||||
|         MyEnum::A => { | ||||
|  | @ -357,6 +368,7 @@ pub fn enums() { | |||
|         MyEnum::B(v) => { | ||||
|             connect(which_out, 1_hdl_u2); | ||||
|             connect_any(data_out, v.0 | (v.1.cast_to_static::<UInt<1>>() << 1)); | ||||
|             connect(b_out, HdlSome(v)); | ||||
|         } | ||||
|         MyEnum::C(v) => { | ||||
|             connect(which_out, 2_hdl_u2); | ||||
|  | @ -382,13 +394,33 @@ fn test_enums() { | |||
|     sim.advance_time(SimDuration::from_nanos(100)); | ||||
|     sim.write_reset(sim.io().cd.rst, false); | ||||
|     sim.advance_time(SimDuration::from_nanos(900)); | ||||
|     #[derive(Debug, PartialEq, Eq)] | ||||
|     type BOutTy = HdlOption<(UInt<1>, Bool)>; | ||||
|     #[derive(Debug)] | ||||
|     struct IO { | ||||
|         en: bool, | ||||
|         which_in: u8, | ||||
|         data_in: u8, | ||||
|         which_out: u8, | ||||
|         data_out: u8, | ||||
|         b_out: Expr<BOutTy>, | ||||
|     } | ||||
|     impl PartialEq for IO { | ||||
|         fn eq(&self, other: &Self) -> bool { | ||||
|             let Self { | ||||
|                 en, | ||||
|                 which_in, | ||||
|                 data_in, | ||||
|                 which_out, | ||||
|                 data_out, | ||||
|                 b_out, | ||||
|             } = *self; | ||||
|             en == other.en | ||||
|                 && which_in == other.which_in | ||||
|                 && data_in == other.data_in | ||||
|                 && which_out == other.which_out | ||||
|                 && data_out == other.data_out | ||||
|                 && b_out.to_sim_value(BOutTy::TYPE) == other.b_out.to_sim_value(BOutTy::TYPE) | ||||
|         } | ||||
|     } | ||||
|     let io_cycles = [ | ||||
|         IO { | ||||
|  | @ -397,6 +429,7 @@ fn test_enums() { | |||
|             data_in: 0, | ||||
|             which_out: 0, | ||||
|             data_out: 0, | ||||
|             b_out: HdlNone(), | ||||
|         }, | ||||
|         IO { | ||||
|             en: true, | ||||
|  | @ -404,6 +437,7 @@ fn test_enums() { | |||
|             data_in: 0, | ||||
|             which_out: 0, | ||||
|             data_out: 0, | ||||
|             b_out: HdlNone(), | ||||
|         }, | ||||
|         IO { | ||||
|             en: false, | ||||
|  | @ -411,6 +445,7 @@ fn test_enums() { | |||
|             data_in: 0, | ||||
|             which_out: 1, | ||||
|             data_out: 0, | ||||
|             b_out: HdlSome((0_hdl_u1, false)), | ||||
|         }, | ||||
|         IO { | ||||
|             en: true, | ||||
|  | @ -418,6 +453,7 @@ fn test_enums() { | |||
|             data_in: 0xF, | ||||
|             which_out: 1, | ||||
|             data_out: 0, | ||||
|             b_out: HdlSome((0_hdl_u1, false)), | ||||
|         }, | ||||
|         IO { | ||||
|             en: true, | ||||
|  | @ -425,6 +461,7 @@ fn test_enums() { | |||
|             data_in: 0xF, | ||||
|             which_out: 1, | ||||
|             data_out: 0x3, | ||||
|             b_out: HdlSome((1_hdl_u1, true)), | ||||
|         }, | ||||
|         IO { | ||||
|             en: true, | ||||
|  | @ -432,6 +469,7 @@ fn test_enums() { | |||
|             data_in: 0xF, | ||||
|             which_out: 1, | ||||
|             data_out: 0x3, | ||||
|             b_out: HdlSome((1_hdl_u1, true)), | ||||
|         }, | ||||
|         IO { | ||||
|             en: true, | ||||
|  | @ -439,6 +477,7 @@ fn test_enums() { | |||
|             data_in: 0xF, | ||||
|             which_out: 2, | ||||
|             data_out: 0xF, | ||||
|             b_out: HdlNone(), | ||||
|         }, | ||||
|     ]; | ||||
|     for ( | ||||
|  | @ -449,6 +488,7 @@ fn test_enums() { | |||
|             data_in, | ||||
|             which_out: _, | ||||
|             data_out: _, | ||||
|             b_out: _, | ||||
|         }, | ||||
|     ) in io_cycles.into_iter().enumerate() | ||||
|     { | ||||
|  | @ -469,11 +509,12 @@ fn test_enums() { | |||
|                 .to_bigint() | ||||
|                 .try_into() | ||||
|                 .expect("known to be in range"), | ||||
|             b_out: sim.read(sim.io().b_out).to_expr(), | ||||
|         }; | ||||
|         assert_eq!( | ||||
|             expected, | ||||
|             io, | ||||
|             "cycle: {cycle}\nvcd:\n{}", | ||||
|             "vcd:\n{}\ncycle: {cycle}", | ||||
|             String::from_utf8(writer.take()).unwrap(), | ||||
|         ); | ||||
|         sim.write_clock(sim.io().cd.clk, false); | ||||
|  | @ -671,7 +712,7 @@ fn test_memories() { | |||
|         assert_eq!( | ||||
|             expected, | ||||
|             io, | ||||
|             "cycle: {cycle}\nvcd:\n{}", | ||||
|             "vcd:\n{}\ncycle: {cycle}", | ||||
|             String::from_utf8(writer.take()).unwrap(), | ||||
|         ); | ||||
|         sim.advance_time(SimDuration::from_micros(1)); | ||||
|  | @ -694,4 +735,514 @@ fn test_memories() { | |||
|     } | ||||
| } | ||||
| 
 | ||||
| // TODO: add more tests for memories
 | ||||
| #[hdl_module(outline_generated)] | ||||
| pub fn memories2() { | ||||
|     #[hdl] | ||||
|     let rw: fayalite::memory::ReadWriteStruct<UInt<2>, ConstUsize<3>> = m.input(); | ||||
|     #[hdl] | ||||
|     let mut mem = memory_with_init([HdlSome(true); 5]); | ||||
|     mem.read_latency(1); | ||||
|     mem.write_latency(NonZeroUsize::new(1).unwrap()); | ||||
|     mem.read_under_write(ReadUnderWrite::New); | ||||
|     let rw_port = mem.new_rw_port(); | ||||
|     connect_any(rw_port.addr, rw.addr); | ||||
|     connect(rw_port.en, rw.en); | ||||
|     connect(rw_port.clk, rw.clk); | ||||
|     connect_any(rw.rdata, rw_port.rdata.cast_to_bits()); | ||||
|     connect(rw_port.wmode, rw.wmode); | ||||
|     connect(rw_port.wdata, HdlNone()); | ||||
|     #[hdl] | ||||
|     if rw.wdata[0] { | ||||
|         connect(rw_port.wdata, HdlSome(rw.wdata[1])); | ||||
|     } | ||||
|     connect(rw_port.wmask, rw.wmask); | ||||
| } | ||||
| 
 | ||||
| #[hdl] | ||||
| #[test] | ||||
| fn test_memories2() { | ||||
|     let _n = SourceLocation::normalize_files_for_tests(); | ||||
|     let mut sim = Simulation::new(memories2()); | ||||
|     let mut writer = RcWriter::default(); | ||||
|     sim.add_trace_writer(VcdWriterDecls::new(writer.clone())); | ||||
|     sim.write_clock(sim.io().rw.clk, false); | ||||
|     #[derive(Debug, PartialEq, Eq)] | ||||
|     struct IO { | ||||
|         addr: u8, | ||||
|         en: bool, | ||||
|         rdata: u8, | ||||
|         wmode: bool, | ||||
|         wdata: u8, | ||||
|         wmask: bool, | ||||
|     } | ||||
|     let io_cycles = [ | ||||
|         IO { | ||||
|             addr: 0, | ||||
|             en: false, | ||||
|             rdata: 0, | ||||
|             wmode: false, | ||||
|             wdata: 0, | ||||
|             wmask: false, | ||||
|         }, | ||||
|         IO { | ||||
|             addr: 0, | ||||
|             en: true, | ||||
|             rdata: 0x3, | ||||
|             wmode: false, | ||||
|             wdata: 0, | ||||
|             wmask: false, | ||||
|         }, | ||||
|         IO { | ||||
|             addr: 0, | ||||
|             en: false, | ||||
|             rdata: 0, | ||||
|             wmode: false, | ||||
|             wdata: 0, | ||||
|             wmask: false, | ||||
|         }, | ||||
|         IO { | ||||
|             addr: 0, | ||||
|             en: true, | ||||
|             rdata: 0, | ||||
|             wmode: true, | ||||
|             wdata: 0, | ||||
|             wmask: true, | ||||
|         }, | ||||
|         IO { | ||||
|             addr: 0, | ||||
|             en: true, | ||||
|             rdata: 0, | ||||
|             wmode: false, | ||||
|             wdata: 0, | ||||
|             wmask: false, | ||||
|         }, | ||||
|         IO { | ||||
|             addr: 0, | ||||
|             en: true, | ||||
|             rdata: 0, | ||||
|             wmode: true, | ||||
|             wdata: 3, | ||||
|             wmask: false, | ||||
|         }, | ||||
|         IO { | ||||
|             addr: 1, | ||||
|             en: true, | ||||
|             rdata: 0, | ||||
|             wmode: true, | ||||
|             wdata: 1, | ||||
|             wmask: true, | ||||
|         }, | ||||
|         IO { | ||||
|             addr: 2, | ||||
|             en: true, | ||||
|             rdata: 0, | ||||
|             wmode: true, | ||||
|             wdata: 2, | ||||
|             wmask: true, | ||||
|         }, | ||||
|         IO { | ||||
|             addr: 3, | ||||
|             en: true, | ||||
|             rdata: 0, | ||||
|             wmode: true, | ||||
|             wdata: 3, | ||||
|             wmask: true, | ||||
|         }, | ||||
|         IO { | ||||
|             addr: 4, | ||||
|             en: true, | ||||
|             rdata: 0, | ||||
|             wmode: true, | ||||
|             wdata: 2, | ||||
|             wmask: true, | ||||
|         }, | ||||
|         IO { | ||||
|             addr: 5, | ||||
|             en: true, | ||||
|             rdata: 0, | ||||
|             wmode: true, | ||||
|             wdata: 1, | ||||
|             wmask: true, | ||||
|         }, | ||||
|         IO { | ||||
|             addr: 6, | ||||
|             en: true, | ||||
|             rdata: 0, | ||||
|             wmode: true, | ||||
|             wdata: 1, | ||||
|             wmask: true, | ||||
|         }, | ||||
|         IO { | ||||
|             addr: 7, | ||||
|             en: true, | ||||
|             rdata: 0, | ||||
|             wmode: true, | ||||
|             wdata: 1, | ||||
|             wmask: true, | ||||
|         }, | ||||
|         IO { | ||||
|             addr: 7, | ||||
|             en: true, | ||||
|             rdata: 0, | ||||
|             wmode: false, | ||||
|             wdata: 0, | ||||
|             wmask: false, | ||||
|         }, | ||||
|         IO { | ||||
|             addr: 6, | ||||
|             en: true, | ||||
|             rdata: 0, | ||||
|             wmode: false, | ||||
|             wdata: 0, | ||||
|             wmask: false, | ||||
|         }, | ||||
|         IO { | ||||
|             addr: 5, | ||||
|             en: true, | ||||
|             rdata: 0, | ||||
|             wmode: false, | ||||
|             wdata: 0, | ||||
|             wmask: false, | ||||
|         }, | ||||
|         IO { | ||||
|             addr: 4, | ||||
|             en: true, | ||||
|             rdata: 0, | ||||
|             wmode: false, | ||||
|             wdata: 0, | ||||
|             wmask: false, | ||||
|         }, | ||||
|         IO { | ||||
|             addr: 3, | ||||
|             en: true, | ||||
|             rdata: 3, | ||||
|             wmode: false, | ||||
|             wdata: 0, | ||||
|             wmask: false, | ||||
|         }, | ||||
|         IO { | ||||
|             addr: 2, | ||||
|             en: true, | ||||
|             rdata: 0, | ||||
|             wmode: false, | ||||
|             wdata: 0, | ||||
|             wmask: false, | ||||
|         }, | ||||
|         IO { | ||||
|             addr: 0, | ||||
|             en: true, | ||||
|             rdata: 0, | ||||
|             wmode: false, | ||||
|             wdata: 0, | ||||
|             wmask: false, | ||||
|         }, | ||||
|         IO { | ||||
|             addr: 1, | ||||
|             en: true, | ||||
|             rdata: 1, | ||||
|             wmode: false, | ||||
|             wdata: 0, | ||||
|             wmask: false, | ||||
|         }, | ||||
|         IO { | ||||
|             addr: 0, | ||||
|             en: false, | ||||
|             rdata: 0, | ||||
|             wmode: false, | ||||
|             wdata: 0, | ||||
|             wmask: false, | ||||
|         }, | ||||
|     ]; | ||||
|     for ( | ||||
|         cycle, | ||||
|         expected @ IO { | ||||
|             addr, | ||||
|             en, | ||||
|             rdata: _, | ||||
|             wmode, | ||||
|             wdata, | ||||
|             wmask, | ||||
|         }, | ||||
|     ) in io_cycles.into_iter().enumerate() | ||||
|     { | ||||
|         sim.write_bool_or_int(sim.io().rw.addr, addr.cast_to_static()); | ||||
|         sim.write_bool(sim.io().rw.en, en); | ||||
|         sim.write_bool(sim.io().rw.wmode, wmode); | ||||
|         sim.write_bool_or_int(sim.io().rw.wdata, wdata.cast_to_static()); | ||||
|         sim.write_bool(sim.io().rw.wmask, wmask); | ||||
|         sim.advance_time(SimDuration::from_nanos(250)); | ||||
|         sim.write_clock(sim.io().rw.clk, true); | ||||
|         sim.advance_time(SimDuration::from_nanos(250)); | ||||
|         let io = IO { | ||||
|             addr, | ||||
|             en, | ||||
|             rdata: sim | ||||
|                 .read_bool_or_int(sim.io().rw.rdata) | ||||
|                 .to_bigint() | ||||
|                 .try_into() | ||||
|                 .expect("known to be in range"), | ||||
|             wmode, | ||||
|             wdata, | ||||
|             wmask, | ||||
|         }; | ||||
|         assert_eq!( | ||||
|             expected, | ||||
|             io, | ||||
|             "vcd:\n{}\ncycle: {cycle}", | ||||
|             String::from_utf8(writer.take()).unwrap(), | ||||
|         ); | ||||
|         sim.advance_time(SimDuration::from_nanos(250)); | ||||
|         sim.write_clock(sim.io().rw.clk, false); | ||||
|         sim.advance_time(SimDuration::from_nanos(250)); | ||||
|     } | ||||
|     sim.flush_traces().unwrap(); | ||||
|     let vcd = String::from_utf8(writer.take()).unwrap(); | ||||
|     println!("####### VCD:\n{vcd}\n#######"); | ||||
|     if vcd != include_str!("sim/expected/memories2.vcd") { | ||||
|         panic!(); | ||||
|     } | ||||
|     let sim_debug = format!("{sim:#?}"); | ||||
|     println!("#######\n{sim_debug}\n#######"); | ||||
|     if sim_debug != include_str!("sim/expected/memories2.txt") { | ||||
|         panic!(); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[hdl_module(outline_generated)] | ||||
| pub fn memories3() { | ||||
|     #[hdl] | ||||
|     let r: fayalite::memory::ReadStruct<Array<UInt<8>, 8>, ConstUsize<3>> = m.input(); | ||||
|     #[hdl] | ||||
|     let w: fayalite::memory::WriteStruct<Array<UInt<8>, 8>, ConstUsize<3>> = m.input(); | ||||
|     #[hdl] | ||||
|     let mut mem: MemBuilder<Array<UInt<8>, 8>> = memory(); | ||||
|     mem.depth(8); | ||||
|     mem.read_latency(2); | ||||
|     mem.write_latency(NonZeroUsize::new(2).unwrap()); | ||||
|     mem.read_under_write(ReadUnderWrite::Old); | ||||
|     connect_any(mem.new_read_port(), r); | ||||
|     connect_any(mem.new_write_port(), w); | ||||
| } | ||||
| 
 | ||||
| #[hdl] | ||||
| #[test] | ||||
| fn test_memories3() { | ||||
|     let _n = SourceLocation::normalize_files_for_tests(); | ||||
|     let mut sim = Simulation::new(memories3()); | ||||
|     let mut writer = RcWriter::default(); | ||||
|     sim.add_trace_writer(VcdWriterDecls::new(writer.clone())); | ||||
|     sim.write_clock(sim.io().r.clk, false); | ||||
|     sim.write_clock(sim.io().w.clk, false); | ||||
|     #[derive(Debug, PartialEq, Eq, Clone, Copy)] | ||||
|     struct IO { | ||||
|         r_addr: u8, | ||||
|         r_en: bool, | ||||
|         r_data: [u8; 8], | ||||
|         w_addr: u8, | ||||
|         w_en: bool, | ||||
|         w_data: [u8; 8], | ||||
|         w_mask: [bool; 8], | ||||
|     } | ||||
|     let io_cycles = [ | ||||
|         IO { | ||||
|             r_addr: 0, | ||||
|             r_en: false, | ||||
|             r_data: [0; 8], | ||||
|             w_addr: 0, | ||||
|             w_en: true, | ||||
|             w_data: [0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0], | ||||
|             w_mask: [false, true, false, true, true, false, false, true], | ||||
|         }, | ||||
|         IO { | ||||
|             r_addr: 0, | ||||
|             r_en: true, | ||||
|             r_data: [0; 8], | ||||
|             w_addr: 1, | ||||
|             w_en: false, | ||||
|             w_data: [0; 8], | ||||
|             w_mask: [false; 8], | ||||
|         }, | ||||
|         IO { | ||||
|             r_addr: 0, | ||||
|             r_en: true, | ||||
|             r_data: [0, 0x34, 0, 0x78, 0x9A, 0, 0, 0xF0], | ||||
|             w_addr: 1, | ||||
|             w_en: false, | ||||
|             w_data: [0; 8], | ||||
|             w_mask: [false; 8], | ||||
|         }, | ||||
|         IO { | ||||
|             r_addr: 0, | ||||
|             r_en: true, | ||||
|             r_data: [0, 0x34, 0, 0x78, 0x9A, 0, 0, 0xF0], | ||||
|             w_addr: 0, | ||||
|             w_en: true, | ||||
|             w_data: [0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10], | ||||
|             w_mask: [true; 8], | ||||
|         }, | ||||
|         IO { | ||||
|             r_addr: 0, | ||||
|             r_en: true, | ||||
|             r_data: [0, 0x34, 0, 0x78, 0x9A, 0, 0, 0xF0], | ||||
|             w_addr: 0, | ||||
|             w_en: true, | ||||
|             w_data: [0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10], | ||||
|             w_mask: [true; 8], | ||||
|         }, | ||||
|         IO { | ||||
|             r_addr: 0, | ||||
|             r_en: true, | ||||
|             r_data: [0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10], | ||||
|             w_addr: 0, | ||||
|             w_en: true, | ||||
|             w_data: [0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10], | ||||
|             w_mask: [true; 8], | ||||
|         }, | ||||
|         IO { | ||||
|             r_addr: 0, | ||||
|             r_en: false, | ||||
|             r_data: [0; 8], | ||||
|             w_addr: 1, | ||||
|             w_en: true, | ||||
|             w_data: [0x13, 0x57, 0x9B, 0xDF, 0x02, 0x46, 0x8A, 0xCE], | ||||
|             w_mask: [true; 8], | ||||
|         }, | ||||
|         IO { | ||||
|             r_addr: 0, | ||||
|             r_en: false, | ||||
|             r_data: [0; 8], | ||||
|             w_addr: 2, | ||||
|             w_en: true, | ||||
|             w_data: *b"testing!", | ||||
|             w_mask: [true; 8], | ||||
|         }, | ||||
|         IO { | ||||
|             r_addr: 0, | ||||
|             r_en: false, | ||||
|             r_data: [0; 8], | ||||
|             w_addr: 3, | ||||
|             w_en: true, | ||||
|             w_data: *b"more tst", | ||||
|             w_mask: [true; 8], | ||||
|         }, | ||||
|         IO { | ||||
|             r_addr: 0, | ||||
|             r_en: true, | ||||
|             r_data: [0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10], | ||||
|             w_addr: 0, | ||||
|             w_en: false, | ||||
|             w_data: [0; 8], | ||||
|             w_mask: [false; 8], | ||||
|         }, | ||||
|         IO { | ||||
|             r_addr: 1, | ||||
|             r_en: true, | ||||
|             r_data: [0x13, 0x57, 0x9B, 0xDF, 0x02, 0x46, 0x8A, 0xCE], | ||||
|             w_addr: 0, | ||||
|             w_en: false, | ||||
|             w_data: [0; 8], | ||||
|             w_mask: [false; 8], | ||||
|         }, | ||||
|         IO { | ||||
|             r_addr: 2, | ||||
|             r_en: true, | ||||
|             r_data: *b"testing!", | ||||
|             w_addr: 0, | ||||
|             w_en: false, | ||||
|             w_data: [0; 8], | ||||
|             w_mask: [false; 8], | ||||
|         }, | ||||
|         IO { | ||||
|             r_addr: 3, | ||||
|             r_en: true, | ||||
|             r_data: *b"more tst", | ||||
|             w_addr: 0, | ||||
|             w_en: false, | ||||
|             w_data: [0; 8], | ||||
|             w_mask: [false; 8], | ||||
|         }, | ||||
|     ]; | ||||
|     for cycle in 0..io_cycles.len() + 2 { | ||||
|         { | ||||
|             let IO { | ||||
|                 r_addr, | ||||
|                 r_en, | ||||
|                 r_data: _, | ||||
|                 w_addr, | ||||
|                 w_en, | ||||
|                 w_data, | ||||
|                 w_mask, | ||||
|             } = io_cycles.get(cycle).copied().unwrap_or(IO { | ||||
|                 r_addr: 0, | ||||
|                 r_en: false, | ||||
|                 r_data: [0; 8], | ||||
|                 w_addr: 0, | ||||
|                 w_en: false, | ||||
|                 w_data: [0; 8], | ||||
|                 w_mask: [false; 8], | ||||
|             }); | ||||
|             sim.write_bool_or_int(sim.io().r.addr, r_addr.cast_to_static()); | ||||
|             sim.write_bool(sim.io().r.en, r_en); | ||||
|             sim.write_bool_or_int(sim.io().w.addr, w_addr.cast_to_static()); | ||||
|             sim.write_bool(sim.io().w.en, w_en); | ||||
|             for (i, v) in w_data.into_iter().enumerate() { | ||||
|                 sim.write_bool_or_int(sim.io().w.data[i], v); | ||||
|             } | ||||
|             for (i, v) in w_mask.into_iter().enumerate() { | ||||
|                 sim.write_bool_or_int(sim.io().w.mask[i], v); | ||||
|             } | ||||
|         } | ||||
|         sim.advance_time(SimDuration::from_nanos(250)); | ||||
|         sim.write_clock(sim.io().r.clk, true); | ||||
|         sim.write_clock(sim.io().w.clk, true); | ||||
|         sim.advance_time(SimDuration::from_nanos(250)); | ||||
|         if let Some( | ||||
|             expected @ IO { | ||||
|                 r_addr, | ||||
|                 r_en, | ||||
|                 r_data: _, | ||||
|                 w_addr, | ||||
|                 w_en, | ||||
|                 w_data, | ||||
|                 w_mask, | ||||
|             }, | ||||
|         ) = cycle.checked_sub(1).and_then(|i| io_cycles.get(i).copied()) | ||||
|         { | ||||
|             let io = IO { | ||||
|                 r_addr, | ||||
|                 r_en, | ||||
|                 r_data: std::array::from_fn(|i| { | ||||
|                     sim.read_bool_or_int(sim.io().r.data[i]) | ||||
|                         .to_bigint() | ||||
|                         .try_into() | ||||
|                         .expect("known to be in range") | ||||
|                 }), | ||||
|                 w_addr, | ||||
|                 w_en, | ||||
|                 w_data, | ||||
|                 w_mask, | ||||
|             }; | ||||
|             assert_eq!( | ||||
|                 expected, | ||||
|                 io, | ||||
|                 "vcd:\n{}\ncycle: {cycle}", | ||||
|                 String::from_utf8(writer.take()).unwrap(), | ||||
|             ); | ||||
|         } | ||||
|         sim.advance_time(SimDuration::from_nanos(250)); | ||||
|         sim.write_clock(sim.io().r.clk, false); | ||||
|         sim.write_clock(sim.io().w.clk, false); | ||||
|         sim.advance_time(SimDuration::from_nanos(250)); | ||||
|     } | ||||
|     sim.flush_traces().unwrap(); | ||||
|     let vcd = String::from_utf8(writer.take()).unwrap(); | ||||
|     println!("####### VCD:\n{vcd}\n#######"); | ||||
|     if vcd != include_str!("sim/expected/memories3.vcd") { | ||||
|         panic!(); | ||||
|     } | ||||
|     let sim_debug = format!("{sim:#?}"); | ||||
|     println!("#######\n{sim_debug}\n#######"); | ||||
|     if sim_debug != include_str!("sim/expected/memories3.txt") { | ||||
|         panic!(); | ||||
|     } | ||||
| } | ||||
|  |  | |||
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							|  | @ -9,18 +9,25 @@ $var wire 2 $ which_in $end | |||
| $var wire 4 % data_in $end | ||||
| $var wire 2 & which_out $end | ||||
| $var wire 4 ' data_out $end | ||||
| $scope struct the_reg $end | ||||
| $scope struct b_out $end | ||||
| $var string 1 ( \$tag $end | ||||
| $scope struct HdlSome $end | ||||
| $var wire 1 ) \0 $end | ||||
| $var wire 1 * \1 $end | ||||
| $upscope $end | ||||
| $upscope $end | ||||
| $scope struct the_reg $end | ||||
| $var string 1 + \$tag $end | ||||
| $scope struct B $end | ||||
| $var reg 1 ) \0 $end | ||||
| $var reg 1 * \1 $end | ||||
| $var reg 1 , \0 $end | ||||
| $var reg 1 - \1 $end | ||||
| $upscope $end | ||||
| $scope struct C $end | ||||
| $scope struct a $end | ||||
| $var reg 1 + \[0] $end | ||||
| $var reg 1 , \[1] $end | ||||
| $var reg 1 . \[0] $end | ||||
| $var reg 1 / \[1] $end | ||||
| $upscope $end | ||||
| $var reg 2 - b $end | ||||
| $var reg 2 0 b $end | ||||
| $upscope $end | ||||
| $upscope $end | ||||
| $upscope $end | ||||
|  | @ -33,12 +40,15 @@ b0 $ | |||
| b0 % | ||||
| b0 & | ||||
| b0 ' | ||||
| sA\x20(0) ( | ||||
| sHdlNone\x20(0) ( | ||||
| 0) | ||||
| 0* | ||||
| 0+ | ||||
| sA\x20(0) + | ||||
| 0, | ||||
| b0 - | ||||
| 0- | ||||
| 0. | ||||
| 0/ | ||||
| b0 0 | ||||
| $end | ||||
| #1000000 | ||||
| 1! | ||||
|  | @ -55,7 +65,8 @@ b1 $ | |||
| #5000000 | ||||
| 1! | ||||
| b1 & | ||||
| sB\x20(1) ( | ||||
| sHdlSome\x20(1) ( | ||||
| sB\x20(1) + | ||||
| #6000000 | ||||
| 0# | ||||
| b0 $ | ||||
|  | @ -72,8 +83,10 @@ b1111 % | |||
| b11 ' | ||||
| 1) | ||||
| 1* | ||||
| 1+ | ||||
| 1, | ||||
| 1- | ||||
| 1. | ||||
| 1/ | ||||
| #10000000 | ||||
| 0! | ||||
| #11000000 | ||||
|  | @ -85,8 +98,11 @@ b10 $ | |||
| 1! | ||||
| b10 & | ||||
| b1111 ' | ||||
| sC\x20(2) ( | ||||
| b11 - | ||||
| sHdlNone\x20(0) ( | ||||
| 0) | ||||
| 0* | ||||
| sC\x20(2) + | ||||
| b11 0 | ||||
| #14000000 | ||||
| 0! | ||||
| #15000000 | ||||
|  |  | |||
							
								
								
									
										1694
									
								
								crates/fayalite/tests/sim/expected/memories2.txt
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										1694
									
								
								crates/fayalite/tests/sim/expected/memories2.txt
									
										
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							
							
								
								
									
										363
									
								
								crates/fayalite/tests/sim/expected/memories2.vcd
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										363
									
								
								crates/fayalite/tests/sim/expected/memories2.vcd
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,363 @@ | |||
| $timescale 1 ps $end | ||||
| $scope module memories2 $end | ||||
| $scope struct rw $end | ||||
| $var wire 3 ! addr $end | ||||
| $var wire 1 " en $end | ||||
| $var wire 1 # clk $end | ||||
| $var wire 2 $ rdata $end | ||||
| $var wire 1 % wmode $end | ||||
| $var wire 2 & wdata $end | ||||
| $var wire 1 ' wmask $end | ||||
| $upscope $end | ||||
| $scope struct mem $end | ||||
| $scope struct contents $end | ||||
| $scope struct [0] $end | ||||
| $scope struct mem $end | ||||
| $var string 1 1 \$tag $end | ||||
| $var reg 1 6 HdlSome $end | ||||
| $upscope $end | ||||
| $upscope $end | ||||
| $scope struct [1] $end | ||||
| $scope struct mem $end | ||||
| $var string 1 2 \$tag $end | ||||
| $var reg 1 7 HdlSome $end | ||||
| $upscope $end | ||||
| $upscope $end | ||||
| $scope struct [2] $end | ||||
| $scope struct mem $end | ||||
| $var string 1 3 \$tag $end | ||||
| $var reg 1 8 HdlSome $end | ||||
| $upscope $end | ||||
| $upscope $end | ||||
| $scope struct [3] $end | ||||
| $scope struct mem $end | ||||
| $var string 1 4 \$tag $end | ||||
| $var reg 1 9 HdlSome $end | ||||
| $upscope $end | ||||
| $upscope $end | ||||
| $scope struct [4] $end | ||||
| $scope struct mem $end | ||||
| $var string 1 5 \$tag $end | ||||
| $var reg 1 : HdlSome $end | ||||
| $upscope $end | ||||
| $upscope $end | ||||
| $upscope $end | ||||
| $scope struct rw0 $end | ||||
| $var wire 3 ( addr $end | ||||
| $var wire 1 ) en $end | ||||
| $var wire 1 * clk $end | ||||
| $scope struct rdata $end | ||||
| $var string 1 + \$tag $end | ||||
| $var wire 1 , HdlSome $end | ||||
| $upscope $end | ||||
| $var wire 1 - wmode $end | ||||
| $scope struct wdata $end | ||||
| $var string 1 . \$tag $end | ||||
| $var wire 1 / HdlSome $end | ||||
| $upscope $end | ||||
| $var wire 1 0 wmask $end | ||||
| $upscope $end | ||||
| $upscope $end | ||||
| $upscope $end | ||||
| $enddefinitions $end | ||||
| $dumpvars | ||||
| sHdlSome\x20(1) 1 | ||||
| 16 | ||||
| sHdlSome\x20(1) 2 | ||||
| 17 | ||||
| sHdlSome\x20(1) 3 | ||||
| 18 | ||||
| sHdlSome\x20(1) 4 | ||||
| 19 | ||||
| sHdlSome\x20(1) 5 | ||||
| 1: | ||||
| b0 ! | ||||
| 0" | ||||
| 0# | ||||
| b0 $ | ||||
| 0% | ||||
| b0 & | ||||
| 0' | ||||
| b0 ( | ||||
| 0) | ||||
| 0* | ||||
| sHdlNone\x20(0) + | ||||
| 0, | ||||
| 0- | ||||
| sHdlNone\x20(0) . | ||||
| 0/ | ||||
| 00 | ||||
| $end | ||||
| #250000 | ||||
| 1# | ||||
| 1* | ||||
| #500000 | ||||
| #750000 | ||||
| 0# | ||||
| 0* | ||||
| #1000000 | ||||
| 1" | ||||
| 1) | ||||
| #1250000 | ||||
| 1# | ||||
| 1* | ||||
| b11 $ | ||||
| sHdlSome\x20(1) + | ||||
| 1, | ||||
| #1500000 | ||||
| #1750000 | ||||
| 0# | ||||
| 0* | ||||
| #2000000 | ||||
| 0" | ||||
| 0) | ||||
| #2250000 | ||||
| 1# | ||||
| 1* | ||||
| b0 $ | ||||
| sHdlNone\x20(0) + | ||||
| 0, | ||||
| #2500000 | ||||
| #2750000 | ||||
| 0# | ||||
| 0* | ||||
| #3000000 | ||||
| 1" | ||||
| 1% | ||||
| 1' | ||||
| 1) | ||||
| 1- | ||||
| 10 | ||||
| #3250000 | ||||
| sHdlNone\x20(0) 1 | ||||
| 06 | ||||
| 1# | ||||
| 1* | ||||
| #3500000 | ||||
| #3750000 | ||||
| 0# | ||||
| 0* | ||||
| #4000000 | ||||
| 0% | ||||
| 0' | ||||
| 0- | ||||
| 00 | ||||
| #4250000 | ||||
| 1# | ||||
| 1* | ||||
| #4500000 | ||||
| #4750000 | ||||
| 0# | ||||
| 0* | ||||
| #5000000 | ||||
| 1% | ||||
| b11 & | ||||
| 1- | ||||
| sHdlSome\x20(1) . | ||||
| 1/ | ||||
| #5250000 | ||||
| 1# | ||||
| 1* | ||||
| #5500000 | ||||
| #5750000 | ||||
| 0# | ||||
| 0* | ||||
| #6000000 | ||||
| b1 ! | ||||
| b1 & | ||||
| 1' | ||||
| b1 ( | ||||
| 0/ | ||||
| 10 | ||||
| #6250000 | ||||
| sHdlSome\x20(1) 2 | ||||
| 07 | ||||
| 1# | ||||
| 1* | ||||
| #6500000 | ||||
| #6750000 | ||||
| 0# | ||||
| 0* | ||||
| #7000000 | ||||
| b10 ! | ||||
| b10 & | ||||
| b10 ( | ||||
| sHdlNone\x20(0) . | ||||
| #7250000 | ||||
| sHdlNone\x20(0) 3 | ||||
| 08 | ||||
| 1# | ||||
| 1* | ||||
| #7500000 | ||||
| #7750000 | ||||
| 0# | ||||
| 0* | ||||
| #8000000 | ||||
| b11 ! | ||||
| b11 & | ||||
| b11 ( | ||||
| sHdlSome\x20(1) . | ||||
| 1/ | ||||
| #8250000 | ||||
| sHdlSome\x20(1) 4 | ||||
| 19 | ||||
| 1# | ||||
| 1* | ||||
| #8500000 | ||||
| #8750000 | ||||
| 0# | ||||
| 0* | ||||
| #9000000 | ||||
| b100 ! | ||||
| b10 & | ||||
| b100 ( | ||||
| sHdlNone\x20(0) . | ||||
| 0/ | ||||
| #9250000 | ||||
| sHdlNone\x20(0) 5 | ||||
| 0: | ||||
| 1# | ||||
| 1* | ||||
| #9500000 | ||||
| #9750000 | ||||
| 0# | ||||
| 0* | ||||
| #10000000 | ||||
| b101 ! | ||||
| b1 & | ||||
| b101 ( | ||||
| sHdlSome\x20(1) . | ||||
| #10250000 | ||||
| 1# | ||||
| 1* | ||||
| #10500000 | ||||
| #10750000 | ||||
| 0# | ||||
| 0* | ||||
| #11000000 | ||||
| b110 ! | ||||
| b110 ( | ||||
| #11250000 | ||||
| 1# | ||||
| 1* | ||||
| #11500000 | ||||
| #11750000 | ||||
| 0# | ||||
| 0* | ||||
| #12000000 | ||||
| b111 ! | ||||
| b111 ( | ||||
| #12250000 | ||||
| 1# | ||||
| 1* | ||||
| #12500000 | ||||
| #12750000 | ||||
| 0# | ||||
| 0* | ||||
| #13000000 | ||||
| 0% | ||||
| b0 & | ||||
| 0' | ||||
| 0- | ||||
| sHdlNone\x20(0) . | ||||
| 00 | ||||
| #13250000 | ||||
| 1# | ||||
| 1* | ||||
| #13500000 | ||||
| #13750000 | ||||
| 0# | ||||
| 0* | ||||
| #14000000 | ||||
| b110 ! | ||||
| b110 ( | ||||
| #14250000 | ||||
| 1# | ||||
| 1* | ||||
| #14500000 | ||||
| #14750000 | ||||
| 0# | ||||
| 0* | ||||
| #15000000 | ||||
| b101 ! | ||||
| b101 ( | ||||
| #15250000 | ||||
| 1# | ||||
| 1* | ||||
| #15500000 | ||||
| #15750000 | ||||
| 0# | ||||
| 0* | ||||
| #16000000 | ||||
| b100 ! | ||||
| b100 ( | ||||
| #16250000 | ||||
| 1# | ||||
| 1* | ||||
| #16500000 | ||||
| #16750000 | ||||
| 0# | ||||
| 0* | ||||
| #17000000 | ||||
| b11 ! | ||||
| b11 ( | ||||
| #17250000 | ||||
| 1# | ||||
| 1* | ||||
| b11 $ | ||||
| sHdlSome\x20(1) + | ||||
| 1, | ||||
| #17500000 | ||||
| #17750000 | ||||
| 0# | ||||
| 0* | ||||
| #18000000 | ||||
| b10 ! | ||||
| b10 ( | ||||
| #18250000 | ||||
| 1# | ||||
| 1* | ||||
| b0 $ | ||||
| sHdlNone\x20(0) + | ||||
| 0, | ||||
| #18500000 | ||||
| #18750000 | ||||
| 0# | ||||
| 0* | ||||
| #19000000 | ||||
| b0 ! | ||||
| b0 ( | ||||
| #19250000 | ||||
| 1# | ||||
| 1* | ||||
| #19500000 | ||||
| #19750000 | ||||
| 0# | ||||
| 0* | ||||
| #20000000 | ||||
| b1 ! | ||||
| b1 ( | ||||
| #20250000 | ||||
| 1# | ||||
| 1* | ||||
| b1 $ | ||||
| sHdlSome\x20(1) + | ||||
| #20500000 | ||||
| #20750000 | ||||
| 0# | ||||
| 0* | ||||
| #21000000 | ||||
| b0 ! | ||||
| 0" | ||||
| b0 ( | ||||
| 0) | ||||
| #21250000 | ||||
| 1# | ||||
| 1* | ||||
| b0 $ | ||||
| sHdlNone\x20(0) + | ||||
| #21500000 | ||||
| #21750000 | ||||
| 0# | ||||
| 0* | ||||
| #22000000 | ||||
							
								
								
									
										4882
									
								
								crates/fayalite/tests/sim/expected/memories3.txt
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										4882
									
								
								crates/fayalite/tests/sim/expected/memories3.txt
									
										
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							
							
								
								
									
										836
									
								
								crates/fayalite/tests/sim/expected/memories3.vcd
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										836
									
								
								crates/fayalite/tests/sim/expected/memories3.vcd
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,836 @@ | |||
| $timescale 1 ps $end | ||||
| $scope module memories3 $end | ||||
| $scope struct r $end | ||||
| $var wire 3 ! addr $end | ||||
| $var wire 1 " en $end | ||||
| $var wire 1 # clk $end | ||||
| $scope struct data $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 | ||||
| $upscope $end | ||||
| $upscope $end | ||||
| $scope struct w $end | ||||
| $var wire 3 , addr $end | ||||
| $var wire 1 - en $end | ||||
| $var wire 1 . clk $end | ||||
| $scope struct data $end | ||||
| $var wire 8 / \[0] $end | ||||
| $var wire 8 0 \[1] $end | ||||
| $var wire 8 1 \[2] $end | ||||
| $var wire 8 2 \[3] $end | ||||
| $var wire 8 3 \[4] $end | ||||
| $var wire 8 4 \[5] $end | ||||
| $var wire 8 5 \[6] $end | ||||
| $var wire 8 6 \[7] $end | ||||
| $upscope $end | ||||
| $scope struct mask $end | ||||
| $var wire 1 7 \[0] $end | ||||
| $var wire 1 8 \[1] $end | ||||
| $var wire 1 9 \[2] $end | ||||
| $var wire 1 : \[3] $end | ||||
| $var wire 1 ; \[4] $end | ||||
| $var wire 1 < \[5] $end | ||||
| $var wire 1 = \[6] $end | ||||
| $var wire 1 > \[7] $end | ||||
| $upscope $end | ||||
| $upscope $end | ||||
| $scope struct mem $end | ||||
| $scope struct contents $end | ||||
| $scope struct [0] $end | ||||
| $scope struct mem $end | ||||
| $var reg 8 ] \[0] $end | ||||
| $var reg 8 e \[1] $end | ||||
| $var reg 8 m \[2] $end | ||||
| $var reg 8 u \[3] $end | ||||
| $var reg 8 } \[4] $end | ||||
| $var reg 8 '" \[5] $end | ||||
| $var reg 8 /" \[6] $end | ||||
| $var reg 8 7" \[7] $end | ||||
| $upscope $end | ||||
| $upscope $end | ||||
| $scope struct [1] $end | ||||
| $scope struct mem $end | ||||
| $var reg 8 ^ \[0] $end | ||||
| $var reg 8 f \[1] $end | ||||
| $var reg 8 n \[2] $end | ||||
| $var reg 8 v \[3] $end | ||||
| $var reg 8 ~ \[4] $end | ||||
| $var reg 8 (" \[5] $end | ||||
| $var reg 8 0" \[6] $end | ||||
| $var reg 8 8" \[7] $end | ||||
| $upscope $end | ||||
| $upscope $end | ||||
| $scope struct [2] $end | ||||
| $scope struct mem $end | ||||
| $var reg 8 _ \[0] $end | ||||
| $var reg 8 g \[1] $end | ||||
| $var reg 8 o \[2] $end | ||||
| $var reg 8 w \[3] $end | ||||
| $var reg 8 !" \[4] $end | ||||
| $var reg 8 )" \[5] $end | ||||
| $var reg 8 1" \[6] $end | ||||
| $var reg 8 9" \[7] $end | ||||
| $upscope $end | ||||
| $upscope $end | ||||
| $scope struct [3] $end | ||||
| $scope struct mem $end | ||||
| $var reg 8 ` \[0] $end | ||||
| $var reg 8 h \[1] $end | ||||
| $var reg 8 p \[2] $end | ||||
| $var reg 8 x \[3] $end | ||||
| $var reg 8 "" \[4] $end | ||||
| $var reg 8 *" \[5] $end | ||||
| $var reg 8 2" \[6] $end | ||||
| $var reg 8 :" \[7] $end | ||||
| $upscope $end | ||||
| $upscope $end | ||||
| $scope struct [4] $end | ||||
| $scope struct mem $end | ||||
| $var reg 8 a \[0] $end | ||||
| $var reg 8 i \[1] $end | ||||
| $var reg 8 q \[2] $end | ||||
| $var reg 8 y \[3] $end | ||||
| $var reg 8 #" \[4] $end | ||||
| $var reg 8 +" \[5] $end | ||||
| $var reg 8 3" \[6] $end | ||||
| $var reg 8 ;" \[7] $end | ||||
| $upscope $end | ||||
| $upscope $end | ||||
| $scope struct [5] $end | ||||
| $scope struct mem $end | ||||
| $var reg 8 b \[0] $end | ||||
| $var reg 8 j \[1] $end | ||||
| $var reg 8 r \[2] $end | ||||
| $var reg 8 z \[3] $end | ||||
| $var reg 8 $" \[4] $end | ||||
| $var reg 8 ," \[5] $end | ||||
| $var reg 8 4" \[6] $end | ||||
| $var reg 8 <" \[7] $end | ||||
| $upscope $end | ||||
| $upscope $end | ||||
| $scope struct [6] $end | ||||
| $scope struct mem $end | ||||
| $var reg 8 c \[0] $end | ||||
| $var reg 8 k \[1] $end | ||||
| $var reg 8 s \[2] $end | ||||
| $var reg 8 { \[3] $end | ||||
| $var reg 8 %" \[4] $end | ||||
| $var reg 8 -" \[5] $end | ||||
| $var reg 8 5" \[6] $end | ||||
| $var reg 8 =" \[7] $end | ||||
| $upscope $end | ||||
| $upscope $end | ||||
| $scope struct [7] $end | ||||
| $scope struct mem $end | ||||
| $var reg 8 d \[0] $end | ||||
| $var reg 8 l \[1] $end | ||||
| $var reg 8 t \[2] $end | ||||
| $var reg 8 | \[3] $end | ||||
| $var reg 8 &" \[4] $end | ||||
| $var reg 8 ." \[5] $end | ||||
| $var reg 8 6" \[6] $end | ||||
| $var reg 8 >" \[7] $end | ||||
| $upscope $end | ||||
| $upscope $end | ||||
| $upscope $end | ||||
| $scope struct r0 $end | ||||
| $var wire 3 ? addr $end | ||||
| $var wire 1 @ en $end | ||||
| $var wire 1 A clk $end | ||||
| $scope struct data $end | ||||
| $var wire 8 B \[0] $end | ||||
| $var wire 8 C \[1] $end | ||||
| $var wire 8 D \[2] $end | ||||
| $var wire 8 E \[3] $end | ||||
| $var wire 8 F \[4] $end | ||||
| $var wire 8 G \[5] $end | ||||
| $var wire 8 H \[6] $end | ||||
| $var wire 8 I \[7] $end | ||||
| $upscope $end | ||||
| $upscope $end | ||||
| $scope struct w1 $end | ||||
| $var wire 3 J addr $end | ||||
| $var wire 1 K en $end | ||||
| $var wire 1 L clk $end | ||||
| $scope struct data $end | ||||
| $var wire 8 M \[0] $end | ||||
| $var wire 8 N \[1] $end | ||||
| $var wire 8 O \[2] $end | ||||
| $var wire 8 P \[3] $end | ||||
| $var wire 8 Q \[4] $end | ||||
| $var wire 8 R \[5] $end | ||||
| $var wire 8 S \[6] $end | ||||
| $var wire 8 T \[7] $end | ||||
| $upscope $end | ||||
| $scope struct mask $end | ||||
| $var wire 1 U \[0] $end | ||||
| $var wire 1 V \[1] $end | ||||
| $var wire 1 W \[2] $end | ||||
| $var wire 1 X \[3] $end | ||||
| $var wire 1 Y \[4] $end | ||||
| $var wire 1 Z \[5] $end | ||||
| $var wire 1 [ \[6] $end | ||||
| $var wire 1 \ \[7] $end | ||||
| $upscope $end | ||||
| $upscope $end | ||||
| $upscope $end | ||||
| $upscope $end | ||||
| $enddefinitions $end | ||||
| $dumpvars | ||||
| b0 ] | ||||
| b0 e | ||||
| b0 m | ||||
| b0 u | ||||
| b0 } | ||||
| b0 '" | ||||
| b0 /" | ||||
| b0 7" | ||||
| b0 ^ | ||||
| b0 f | ||||
| b0 n | ||||
| b0 v | ||||
| b0 ~ | ||||
| b0 (" | ||||
| b0 0" | ||||
| b0 8" | ||||
| b0 _ | ||||
| b0 g | ||||
| b0 o | ||||
| b0 w | ||||
| b0 !" | ||||
| b0 )" | ||||
| b0 1" | ||||
| b0 9" | ||||
| b0 ` | ||||
| b0 h | ||||
| b0 p | ||||
| b0 x | ||||
| b0 "" | ||||
| b0 *" | ||||
| b0 2" | ||||
| b0 :" | ||||
| b0 a | ||||
| b0 i | ||||
| b0 q | ||||
| b0 y | ||||
| b0 #" | ||||
| b0 +" | ||||
| b0 3" | ||||
| b0 ;" | ||||
| b0 b | ||||
| b0 j | ||||
| b0 r | ||||
| b0 z | ||||
| b0 $" | ||||
| b0 ," | ||||
| b0 4" | ||||
| b0 <" | ||||
| b0 c | ||||
| b0 k | ||||
| b0 s | ||||
| b0 { | ||||
| b0 %" | ||||
| b0 -" | ||||
| b0 5" | ||||
| b0 =" | ||||
| b0 d | ||||
| b0 l | ||||
| b0 t | ||||
| b0 | | ||||
| b0 &" | ||||
| b0 ." | ||||
| b0 6" | ||||
| b0 >" | ||||
| b0 ! | ||||
| 0" | ||||
| 0# | ||||
| b0 $ | ||||
| b0 % | ||||
| b0 & | ||||
| b0 ' | ||||
| b0 ( | ||||
| b0 ) | ||||
| b0 * | ||||
| b0 + | ||||
| b0 , | ||||
| 1- | ||||
| 0. | ||||
| b10010 / | ||||
| b110100 0 | ||||
| b1010110 1 | ||||
| b1111000 2 | ||||
| b10011010 3 | ||||
| b10111100 4 | ||||
| b11011110 5 | ||||
| b11110000 6 | ||||
| 07 | ||||
| 18 | ||||
| 09 | ||||
| 1: | ||||
| 1; | ||||
| 0< | ||||
| 0= | ||||
| 1> | ||||
| b0 ? | ||||
| 0@ | ||||
| 0A | ||||
| b0 B | ||||
| b0 C | ||||
| b0 D | ||||
| b0 E | ||||
| b0 F | ||||
| b0 G | ||||
| b0 H | ||||
| b0 I | ||||
| b0 J | ||||
| 1K | ||||
| 0L | ||||
| b10010 M | ||||
| b110100 N | ||||
| b1010110 O | ||||
| b1111000 P | ||||
| b10011010 Q | ||||
| b10111100 R | ||||
| b11011110 S | ||||
| b11110000 T | ||||
| 0U | ||||
| 1V | ||||
| 0W | ||||
| 1X | ||||
| 1Y | ||||
| 0Z | ||||
| 0[ | ||||
| 1\ | ||||
| $end | ||||
| #250000 | ||||
| 1# | ||||
| 1. | ||||
| 1A | ||||
| 1L | ||||
| #500000 | ||||
| #750000 | ||||
| 0# | ||||
| 0. | ||||
| 0A | ||||
| 0L | ||||
| #1000000 | ||||
| 1" | ||||
| b1 , | ||||
| 0- | ||||
| b0 / | ||||
| b0 0 | ||||
| b0 1 | ||||
| b0 2 | ||||
| b0 3 | ||||
| b0 4 | ||||
| b0 5 | ||||
| b0 6 | ||||
| 08 | ||||
| 0: | ||||
| 0; | ||||
| 0> | ||||
| 1@ | ||||
| b1 J | ||||
| 0K | ||||
| b0 M | ||||
| b0 N | ||||
| b0 O | ||||
| b0 P | ||||
| b0 Q | ||||
| b0 R | ||||
| b0 S | ||||
| b0 T | ||||
| 0V | ||||
| 0X | ||||
| 0Y | ||||
| 0\ | ||||
| #1250000 | ||||
| b0 ] | ||||
| b110100 e | ||||
| b0 m | ||||
| b1111000 u | ||||
| b10011010 } | ||||
| b0 '" | ||||
| b0 /" | ||||
| b11110000 7" | ||||
| 1# | ||||
| 1. | ||||
| 1A | ||||
| 1L | ||||
| #1500000 | ||||
| #1750000 | ||||
| 0# | ||||
| 0. | ||||
| 0A | ||||
| 0L | ||||
| #2000000 | ||||
| #2250000 | ||||
| 1# | ||||
| 1. | ||||
| 1A | ||||
| 1L | ||||
| #2500000 | ||||
| #2750000 | ||||
| 0# | ||||
| 0. | ||||
| 0A | ||||
| 0L | ||||
| #3000000 | ||||
| b0 , | ||||
| 1- | ||||
| b11111110 / | ||||
| b11011100 0 | ||||
| b10111010 1 | ||||
| b10011000 2 | ||||
| b1110110 3 | ||||
| b1010100 4 | ||||
| b110010 5 | ||||
| b10000 6 | ||||
| 17 | ||||
| 18 | ||||
| 19 | ||||
| 1: | ||||
| 1; | ||||
| 1< | ||||
| 1= | ||||
| 1> | ||||
| b0 J | ||||
| 1K | ||||
| b11111110 M | ||||
| b11011100 N | ||||
| b10111010 O | ||||
| b10011000 P | ||||
| b1110110 Q | ||||
| b1010100 R | ||||
| b110010 S | ||||
| b10000 T | ||||
| 1U | ||||
| 1V | ||||
| 1W | ||||
| 1X | ||||
| 1Y | ||||
| 1Z | ||||
| 1[ | ||||
| 1\ | ||||
| #3250000 | ||||
| 1# | ||||
| 1. | ||||
| 1A | ||||
| b110100 C | ||||
| b1111000 E | ||||
| b10011010 F | ||||
| b11110000 I | ||||
| 1L | ||||
| b110100 % | ||||
| b1111000 ' | ||||
| b10011010 ( | ||||
| b11110000 + | ||||
| #3500000 | ||||
| #3750000 | ||||
| 0# | ||||
| 0. | ||||
| 0A | ||||
| 0L | ||||
| #4000000 | ||||
| #4250000 | ||||
| b11111110 ] | ||||
| b11011100 e | ||||
| b10111010 m | ||||
| b10011000 u | ||||
| b1110110 } | ||||
| b1010100 '" | ||||
| b110010 /" | ||||
| b10000 7" | ||||
| 1# | ||||
| 1. | ||||
| 1A | ||||
| 1L | ||||
| #4500000 | ||||
| #4750000 | ||||
| 0# | ||||
| 0. | ||||
| 0A | ||||
| 0L | ||||
| #5000000 | ||||
| #5250000 | ||||
| b11111110 ] | ||||
| b11011100 e | ||||
| b10111010 m | ||||
| b10011000 u | ||||
| b1110110 } | ||||
| b1010100 '" | ||||
| b110010 /" | ||||
| b10000 7" | ||||
| 1# | ||||
| 1. | ||||
| 1A | ||||
| 1L | ||||
| #5500000 | ||||
| #5750000 | ||||
| 0# | ||||
| 0. | ||||
| 0A | ||||
| 0L | ||||
| #6000000 | ||||
| 0" | ||||
| b1 , | ||||
| b10011 / | ||||
| b1010111 0 | ||||
| b10011011 1 | ||||
| b11011111 2 | ||||
| b10 3 | ||||
| b1000110 4 | ||||
| b10001010 5 | ||||
| b11001110 6 | ||||
| 0@ | ||||
| b1 J | ||||
| b10011 M | ||||
| b1010111 N | ||||
| b10011011 O | ||||
| b11011111 P | ||||
| b10 Q | ||||
| b1000110 R | ||||
| b10001010 S | ||||
| b11001110 T | ||||
| #6250000 | ||||
| b11111110 ] | ||||
| b11011100 e | ||||
| b10111010 m | ||||
| b10011000 u | ||||
| b1110110 } | ||||
| b1010100 '" | ||||
| b110010 /" | ||||
| b10000 7" | ||||
| 1# | ||||
| 1. | ||||
| 1A | ||||
| b11111110 B | ||||
| b11011100 C | ||||
| b10111010 D | ||||
| b10011000 E | ||||
| b1110110 F | ||||
| b1010100 G | ||||
| b110010 H | ||||
| b10000 I | ||||
| 1L | ||||
| b11111110 $ | ||||
| b11011100 % | ||||
| b10111010 & | ||||
| b10011000 ' | ||||
| b1110110 ( | ||||
| b1010100 ) | ||||
| b110010 * | ||||
| b10000 + | ||||
| #6500000 | ||||
| #6750000 | ||||
| 0# | ||||
| 0. | ||||
| 0A | ||||
| 0L | ||||
| #7000000 | ||||
| b10 , | ||||
| b1110100 / | ||||
| b1100101 0 | ||||
| b1110011 1 | ||||
| b1110100 2 | ||||
| b1101001 3 | ||||
| b1101110 4 | ||||
| b1100111 5 | ||||
| b100001 6 | ||||
| b10 J | ||||
| b1110100 M | ||||
| b1100101 N | ||||
| b1110011 O | ||||
| b1110100 P | ||||
| b1101001 Q | ||||
| b1101110 R | ||||
| b1100111 S | ||||
| b100001 T | ||||
| #7250000 | ||||
| b10011 ^ | ||||
| b1010111 f | ||||
| b10011011 n | ||||
| b11011111 v | ||||
| b10 ~ | ||||
| b1000110 (" | ||||
| b10001010 0" | ||||
| b11001110 8" | ||||
| 1# | ||||
| 1. | ||||
| 1A | ||||
| b0 B | ||||
| b0 C | ||||
| b0 D | ||||
| b0 E | ||||
| b0 F | ||||
| b0 G | ||||
| b0 H | ||||
| b0 I | ||||
| 1L | ||||
| b0 $ | ||||
| b0 % | ||||
| b0 & | ||||
| b0 ' | ||||
| b0 ( | ||||
| b0 ) | ||||
| b0 * | ||||
| b0 + | ||||
| #7500000 | ||||
| #7750000 | ||||
| 0# | ||||
| 0. | ||||
| 0A | ||||
| 0L | ||||
| #8000000 | ||||
| b11 , | ||||
| b1101101 / | ||||
| b1101111 0 | ||||
| b1110010 1 | ||||
| b1100101 2 | ||||
| b100000 3 | ||||
| b1110100 4 | ||||
| b1110011 5 | ||||
| b1110100 6 | ||||
| b11 J | ||||
| b1101101 M | ||||
| b1101111 N | ||||
| b1110010 O | ||||
| b1100101 P | ||||
| b100000 Q | ||||
| b1110100 R | ||||
| b1110011 S | ||||
| b1110100 T | ||||
| #8250000 | ||||
| b1110100 _ | ||||
| b1100101 g | ||||
| b1110011 o | ||||
| b1110100 w | ||||
| b1101001 !" | ||||
| b1101110 )" | ||||
| b1100111 1" | ||||
| b100001 9" | ||||
| 1# | ||||
| 1. | ||||
| 1A | ||||
| 1L | ||||
| #8500000 | ||||
| #8750000 | ||||
| 0# | ||||
| 0. | ||||
| 0A | ||||
| 0L | ||||
| #9000000 | ||||
| 1" | ||||
| b0 , | ||||
| 0- | ||||
| b0 / | ||||
| b0 0 | ||||
| b0 1 | ||||
| b0 2 | ||||
| b0 3 | ||||
| b0 4 | ||||
| b0 5 | ||||
| b0 6 | ||||
| 07 | ||||
| 08 | ||||
| 09 | ||||
| 0: | ||||
| 0; | ||||
| 0< | ||||
| 0= | ||||
| 0> | ||||
| 1@ | ||||
| b0 J | ||||
| 0K | ||||
| b0 M | ||||
| b0 N | ||||
| b0 O | ||||
| b0 P | ||||
| b0 Q | ||||
| b0 R | ||||
| b0 S | ||||
| b0 T | ||||
| 0U | ||||
| 0V | ||||
| 0W | ||||
| 0X | ||||
| 0Y | ||||
| 0Z | ||||
| 0[ | ||||
| 0\ | ||||
| #9250000 | ||||
| b1101101 ` | ||||
| b1101111 h | ||||
| b1110010 p | ||||
| b1100101 x | ||||
| b100000 "" | ||||
| b1110100 *" | ||||
| b1110011 2" | ||||
| b1110100 :" | ||||
| 1# | ||||
| 1. | ||||
| 1A | ||||
| 1L | ||||
| #9500000 | ||||
| #9750000 | ||||
| 0# | ||||
| 0. | ||||
| 0A | ||||
| 0L | ||||
| #10000000 | ||||
| b1 ! | ||||
| b1 ? | ||||
| #10250000 | ||||
| 1# | ||||
| 1. | ||||
| 1A | ||||
| b11111110 B | ||||
| b11011100 C | ||||
| b10111010 D | ||||
| b10011000 E | ||||
| b1110110 F | ||||
| b1010100 G | ||||
| b110010 H | ||||
| b10000 I | ||||
| 1L | ||||
| b11111110 $ | ||||
| b11011100 % | ||||
| b10111010 & | ||||
| b10011000 ' | ||||
| b1110110 ( | ||||
| b1010100 ) | ||||
| b110010 * | ||||
| b10000 + | ||||
| #10500000 | ||||
| #10750000 | ||||
| 0# | ||||
| 0. | ||||
| 0A | ||||
| 0L | ||||
| #11000000 | ||||
| b10 ! | ||||
| b10 ? | ||||
| #11250000 | ||||
| 1# | ||||
| 1. | ||||
| 1A | ||||
| b10011 B | ||||
| b1010111 C | ||||
| b10011011 D | ||||
| b11011111 E | ||||
| b10 F | ||||
| b1000110 G | ||||
| b10001010 H | ||||
| b11001110 I | ||||
| 1L | ||||
| b10011 $ | ||||
| b1010111 % | ||||
| b10011011 & | ||||
| b11011111 ' | ||||
| b10 ( | ||||
| b1000110 ) | ||||
| b10001010 * | ||||
| b11001110 + | ||||
| #11500000 | ||||
| #11750000 | ||||
| 0# | ||||
| 0. | ||||
| 0A | ||||
| 0L | ||||
| #12000000 | ||||
| b11 ! | ||||
| b11 ? | ||||
| #12250000 | ||||
| 1# | ||||
| 1. | ||||
| 1A | ||||
| b1110100 B | ||||
| b1100101 C | ||||
| b1110011 D | ||||
| b1110100 E | ||||
| b1101001 F | ||||
| b1101110 G | ||||
| b1100111 H | ||||
| b100001 I | ||||
| 1L | ||||
| b1110100 $ | ||||
| b1100101 % | ||||
| b1110011 & | ||||
| b1110100 ' | ||||
| b1101001 ( | ||||
| b1101110 ) | ||||
| b1100111 * | ||||
| b100001 + | ||||
| #12500000 | ||||
| #12750000 | ||||
| 0# | ||||
| 0. | ||||
| 0A | ||||
| 0L | ||||
| #13000000 | ||||
| b0 ! | ||||
| 0" | ||||
| b0 ? | ||||
| 0@ | ||||
| #13250000 | ||||
| 1# | ||||
| 1. | ||||
| 1A | ||||
| b1101101 B | ||||
| b1101111 C | ||||
| b1110010 D | ||||
| b1100101 E | ||||
| b100000 F | ||||
| b1110100 G | ||||
| b1110011 H | ||||
| b1110100 I | ||||
| 1L | ||||
| b1101101 $ | ||||
| b1101111 % | ||||
| b1110010 & | ||||
| b1100101 ' | ||||
| b100000 ( | ||||
| b1110100 ) | ||||
| b1110011 * | ||||
| b1110100 + | ||||
| #13500000 | ||||
| #13750000 | ||||
| 0# | ||||
| 0. | ||||
| 0A | ||||
| 0L | ||||
| #14000000 | ||||
| #14250000 | ||||
| 1# | ||||
| 1. | ||||
| 1A | ||||
| b0 B | ||||
| b0 C | ||||
| b0 D | ||||
| b0 E | ||||
| b0 F | ||||
| b0 G | ||||
| b0 H | ||||
| b0 I | ||||
| 1L | ||||
| b0 $ | ||||
| b0 % | ||||
| b0 & | ||||
| b0 ' | ||||
| b0 ( | ||||
| b0 ) | ||||
| b0 * | ||||
| b0 + | ||||
| #14500000 | ||||
| #14750000 | ||||
| 0# | ||||
| 0. | ||||
| 0A | ||||
| 0L | ||||
| #15000000 | ||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue