forked from libre-chip/fayalite
		
	Compare commits
	
		
			38 commits
		
	
	
		
			86a1bb46be
			...
			668e714dc9
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 668e714dc9 | |||
| 88323a8c16 | |||
| 91e1b619e8 | |||
| e2d2d4110b | |||
| b1f9706e4e | |||
| 4eda4366c8 | |||
| 122c08d3cf | |||
| b08a747e20 | |||
| e0c9939147 | |||
| 07725ab489 | |||
| 36f1b9bbb6 | |||
| 9a1b047d2f | |||
| 5967e812a2 | |||
| 001fd31451 | |||
| 57aae7b7fb | |||
| 6929352be7 | |||
| 62058dc141 | |||
| c4b6a0fee6 | |||
| 9092e45447 | |||
| a40eaaa2da | |||
| 5028401a5a | |||
| e0f978fbb6 | |||
| ec3a61513b | |||
| fdc73b5f3b | |||
| a115585d5a | |||
| ab9ff4f2db | |||
| d1bd176b28 | |||
| 920d8d875f | |||
| d453755bb2 | |||
| 450e1004b6 | |||
| c0c5b550bc | |||
| 2fa0ea6192 | |||
| bd75fdfefd | |||
| 50c86e18dc | |||
| 60734cc9d1 | |||
| 3458c21f44 | |||
| 43797db36e | |||
| cdd84953d0 | 
					 78 changed files with 14589 additions and 8502 deletions
				
			
		|  | @ -12,10 +12,10 @@ jobs: | |||
|     outputs: | ||||
|       cache-primary-key: ${{ steps.restore-deps.outputs.cache-primary-key }} | ||||
|     steps: | ||||
|       - uses: https://code.forgejo.org/actions/checkout@v3 | ||||
|       - uses: https://git.libre-chip.org/mirrors/checkout@v3 | ||||
|         with: | ||||
|           fetch-depth: 0 | ||||
|       - uses: https://code.forgejo.org/actions/cache/restore@v3 | ||||
|       - uses: https://git.libre-chip.org/mirrors/cache/restore@v3 | ||||
|         id: restore-deps | ||||
|         with: | ||||
|           path: deps | ||||
|  | @ -58,19 +58,19 @@ jobs: | |||
|       - name: Get SymbiYosys | ||||
|         if: steps.restore-deps.outputs.cache-hit != 'true' | ||||
|         run: | | ||||
|           git clone --depth=1 --branch=yosys-0.45 https://github.com/YosysHQ/sby.git deps/sby | ||||
|           git clone --depth=1 --branch=yosys-0.45 https://git.libre-chip.org/mirrors/sby deps/sby | ||||
|       - name: Build Z3 | ||||
|         if: steps.restore-deps.outputs.cache-hit != 'true' | ||||
|         run: | | ||||
|           git clone --depth=1 --recursive --branch=z3-4.13.3 https://github.com/Z3Prover/z3.git deps/z3 | ||||
|           git clone --depth=1 --recursive --branch=z3-4.13.3 https://git.libre-chip.org/mirrors/z3 deps/z3 | ||||
|           (cd deps/z3; PYTHON=python3 ./configure --prefix=/usr/local) | ||||
|           make -C deps/z3/build -j"$(nproc)" | ||||
|       - name: Build Yosys | ||||
|         if: steps.restore-deps.outputs.cache-hit != 'true' | ||||
|         run: | | ||||
|           git clone --depth=1 --recursive --branch=0.45 https://github.com/YosysHQ/yosys.git deps/yosys | ||||
|           git clone --depth=1 --recursive --branch=0.45 https://git.libre-chip.org/mirrors/yosys deps/yosys | ||||
|           make -C deps/yosys -j"$(nproc)" | ||||
|       - uses: https://code.forgejo.org/actions/cache/save@v3 | ||||
|       - uses: https://git.libre-chip.org/mirrors/cache/save@v3 | ||||
|         if: steps.restore-deps.outputs.cache-hit != 'true' | ||||
|         with: | ||||
|           path: deps | ||||
|  |  | |||
|  | @ -9,7 +9,7 @@ jobs: | |||
|     runs-on: debian-12 | ||||
|     needs: deps | ||||
|     steps: | ||||
|       - uses: https://code.forgejo.org/actions/checkout@v3 | ||||
|       - uses: https://git.libre-chip.org/mirrors/checkout@v3 | ||||
|         with: | ||||
|           fetch-depth: 0 | ||||
|       - run: | | ||||
|  | @ -41,7 +41,7 @@ jobs: | |||
|           curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain 1.82.0 | ||||
|           source "$HOME/.cargo/env" | ||||
|           echo "$PATH" >> "$GITHUB_PATH" | ||||
|       - uses: https://code.forgejo.org/actions/cache/restore@v3 | ||||
|       - uses: https://git.libre-chip.org/mirrors/cache/restore@v3 | ||||
|         with: | ||||
|           path: deps | ||||
|           key: ${{ needs.deps.outputs.cache-primary-key }} | ||||
|  | @ -52,10 +52,11 @@ jobs: | |||
|           make -C deps/yosys install | ||||
|           export PATH="$(realpath deps/firtool/bin):$PATH" | ||||
|           echo "$PATH" >> "$GITHUB_PATH" | ||||
|       - uses: https://github.com/Swatinem/rust-cache@v2 | ||||
|       - uses: https://git.libre-chip.org/mirrors/rust-cache@v2 | ||||
|         with: | ||||
|           save-if: ${{ github.ref == 'refs/heads/master' }} | ||||
|       - run: cargo test | ||||
|       - run: cargo build --tests --features=unstable-doc | ||||
|       - run: cargo test --doc --features=unstable-doc | ||||
|       - run: cargo doc --features=unstable-doc | ||||
|       - run: FAYALITE_TEST_HASHER=always_zero cargo test --test=module --features=unstable-doc,unstable-test-hasher | ||||
|  |  | |||
							
								
								
									
										56
									
								
								Cargo.lock
									
										
									
										generated
									
									
									
								
							
							
						
						
									
										56
									
								
								Cargo.lock
									
										
									
										generated
									
									
									
								
							|  | @ -2,18 +2,6 @@ | |||
| # It is not intended for manual editing. | ||||
| version = 3 | ||||
| 
 | ||||
| [[package]] | ||||
| name = "ahash" | ||||
| version = "0.8.7" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "77c3a9648d43b9cd48db467b3f87fdd6e146bcc88ab0180006cef2179fe11d01" | ||||
| dependencies = [ | ||||
|  "cfg-if", | ||||
|  "once_cell", | ||||
|  "version_check", | ||||
|  "zerocopy", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "allocator-api2" | ||||
| version = "0.2.16" | ||||
|  | @ -365,6 +353,12 @@ version = "0.5.7" | |||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "1d674e81391d1e1ab681a28d99df07927c6d4aa5b027d7da16ba32d1d21ecd99" | ||||
| 
 | ||||
| [[package]] | ||||
| name = "foldhash" | ||||
| version = "0.1.5" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" | ||||
| 
 | ||||
| [[package]] | ||||
| name = "funty" | ||||
| version = "2.0.0" | ||||
|  | @ -400,12 +394,13 @@ checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" | |||
| 
 | ||||
| [[package]] | ||||
| name = "hashbrown" | ||||
| version = "0.14.3" | ||||
| version = "0.15.2" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" | ||||
| checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" | ||||
| dependencies = [ | ||||
|  "ahash", | ||||
|  "allocator-api2", | ||||
|  "equivalent", | ||||
|  "foldhash", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
|  | @ -431,9 +426,9 @@ checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683" | |||
| 
 | ||||
| [[package]] | ||||
| name = "indexmap" | ||||
| version = "2.5.0" | ||||
| version = "2.9.0" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "68b900aa2f7301e21c36462b170ee99994de34dff39a4a6a528e80e7376d07e5" | ||||
| checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e" | ||||
| dependencies = [ | ||||
|  "equivalent", | ||||
|  "hashbrown", | ||||
|  | @ -524,11 +519,14 @@ dependencies = [ | |||
| 
 | ||||
| [[package]] | ||||
| name = "petgraph" | ||||
| version = "0.6.5" | ||||
| source = "git+https://github.com/programmerjake/petgraph.git?rev=258ea8071209a924b73fe96f9f87a3b7b45cbc9f#258ea8071209a924b73fe96f9f87a3b7b45cbc9f" | ||||
| version = "0.8.1" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "7a98c6720655620a521dcc722d0ad66cd8afd5d86e34a89ef691c50b7b24de06" | ||||
| dependencies = [ | ||||
|  "fixedbitset", | ||||
|  "hashbrown", | ||||
|  "indexmap", | ||||
|  "serde", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
|  | @ -893,23 +891,3 @@ checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" | |||
| dependencies = [ | ||||
|  "tap", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "zerocopy" | ||||
| version = "0.7.32" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "74d4d3961e53fa4c9a25a8637fc2bfaf2595b3d3ae34875568a5cf64787716be" | ||||
| dependencies = [ | ||||
|  "zerocopy-derive", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "zerocopy-derive" | ||||
| version = "0.7.32" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" | ||||
| dependencies = [ | ||||
|  "proc-macro2", | ||||
|  "quote", | ||||
|  "syn", | ||||
| ] | ||||
|  |  | |||
|  | @ -23,14 +23,13 @@ blake3 = { version = "1.5.4", features = ["serde"] } | |||
| clap = { version = "4.5.9", features = ["derive", "env", "string"] } | ||||
| ctor = "0.2.8" | ||||
| eyre = "0.6.12" | ||||
| hashbrown = "0.14.3" | ||||
| hashbrown = "0.15.2" | ||||
| indexmap = { version = "2.5.0", features = ["serde"] } | ||||
| jobslot = "0.2.19" | ||||
| num-bigint = "0.4.6" | ||||
| num-traits = "0.2.16" | ||||
| os_pipe = "1.2.1" | ||||
| # TODO: switch back to crates.io once petgraph accepts PR #684 and releases a new version | ||||
| petgraph = { git = "https://github.com/programmerjake/petgraph.git", rev = "258ea8071209a924b73fe96f9f87a3b7b45cbc9f" } | ||||
| petgraph = "0.8.1" | ||||
| prettyplease = "0.2.20" | ||||
| proc-macro2 = "1.0.83" | ||||
| quote = "1.0.36" | ||||
|  |  | |||
|  | @ -220,6 +220,7 @@ forward_fold!(syn::ExprArray => fold_expr_array); | |||
| forward_fold!(syn::ExprCall => fold_expr_call); | ||||
| forward_fold!(syn::ExprIf => fold_expr_if); | ||||
| forward_fold!(syn::ExprMatch => fold_expr_match); | ||||
| forward_fold!(syn::ExprMethodCall => fold_expr_method_call); | ||||
| forward_fold!(syn::ExprPath => fold_expr_path); | ||||
| forward_fold!(syn::ExprRepeat => fold_expr_repeat); | ||||
| forward_fold!(syn::ExprStruct => fold_expr_struct); | ||||
|  |  | |||
|  | @ -30,7 +30,9 @@ pub(crate) struct ParsedBundle { | |||
|     pub(crate) field_flips: Vec<Option<HdlAttr<kw::flip, kw::hdl>>>, | ||||
|     pub(crate) mask_type_ident: Ident, | ||||
|     pub(crate) mask_type_match_variant_ident: Ident, | ||||
|     pub(crate) mask_type_sim_value_ident: Ident, | ||||
|     pub(crate) match_variant_ident: Ident, | ||||
|     pub(crate) sim_value_ident: Ident, | ||||
|     pub(crate) builder_ident: Ident, | ||||
|     pub(crate) mask_type_builder_ident: Ident, | ||||
| } | ||||
|  | @ -83,6 +85,7 @@ impl ParsedBundle { | |||
|             custom_bounds, | ||||
|             no_static: _, | ||||
|             no_runtime_generics: _, | ||||
|             cmp_eq: _, | ||||
|         } = options.body; | ||||
|         let mut fields = match fields { | ||||
|             syn::Fields::Named(fields) => fields, | ||||
|  | @ -124,7 +127,9 @@ impl ParsedBundle { | |||
|             field_flips, | ||||
|             mask_type_ident: format_ident!("__{}__MaskType", ident), | ||||
|             mask_type_match_variant_ident: format_ident!("__{}__MaskType__MatchVariant", ident), | ||||
|             mask_type_sim_value_ident: format_ident!("__{}__MaskType__SimValue", ident), | ||||
|             match_variant_ident: format_ident!("__{}__MatchVariant", ident), | ||||
|             sim_value_ident: format_ident!("__{}__SimValue", ident), | ||||
|             mask_type_builder_ident: format_ident!("__{}__MaskType__Builder", ident), | ||||
|             builder_ident: format_ident!("__{}__Builder", ident), | ||||
|             ident, | ||||
|  | @ -426,7 +431,9 @@ impl ToTokens for ParsedBundle { | |||
|             field_flips, | ||||
|             mask_type_ident, | ||||
|             mask_type_match_variant_ident, | ||||
|             mask_type_sim_value_ident, | ||||
|             match_variant_ident, | ||||
|             sim_value_ident, | ||||
|             builder_ident, | ||||
|             mask_type_builder_ident, | ||||
|         } = self; | ||||
|  | @ -437,6 +444,7 @@ impl ToTokens for ParsedBundle { | |||
|             custom_bounds: _, | ||||
|             no_static, | ||||
|             no_runtime_generics, | ||||
|             cmp_eq, | ||||
|         } = &options.body; | ||||
|         let target = get_target(target, ident); | ||||
|         let mut item_attrs = attrs.clone(); | ||||
|  | @ -521,7 +529,7 @@ impl ToTokens for ParsedBundle { | |||
|             semi_token: None, | ||||
|         } | ||||
|         .to_tokens(tokens); | ||||
|         let mut mask_type_match_variant_fields = mask_type_fields; | ||||
|         let mut mask_type_match_variant_fields = mask_type_fields.clone(); | ||||
|         for Field { ty, .. } in &mut mask_type_match_variant_fields.named { | ||||
|             *ty = parse_quote_spanned! {span=> | ||||
|                 ::fayalite::expr::Expr<#ty> | ||||
|  | @ -563,6 +571,58 @@ impl ToTokens for ParsedBundle { | |||
|             semi_token: None, | ||||
|         } | ||||
|         .to_tokens(tokens); | ||||
|         let mut mask_type_sim_value_fields = mask_type_fields; | ||||
|         for Field { ty, .. } in &mut mask_type_sim_value_fields.named { | ||||
|             *ty = parse_quote_spanned! {span=> | ||||
|                 ::fayalite::sim::value::SimValue<#ty> | ||||
|             }; | ||||
|         } | ||||
|         ItemStruct { | ||||
|             attrs: vec![ | ||||
|                 parse_quote_spanned! {span=> | ||||
|                     #[::fayalite::__std::prelude::v1::derive(
 | ||||
|                         ::fayalite::__std::fmt::Debug, | ||||
|                         ::fayalite::__std::clone::Clone, | ||||
|                     )] | ||||
|                 }, | ||||
|                 parse_quote_spanned! {span=> | ||||
|                     #[allow(non_camel_case_types, dead_code)] | ||||
|                 }, | ||||
|             ], | ||||
|             vis: vis.clone(), | ||||
|             struct_token: *struct_token, | ||||
|             ident: mask_type_sim_value_ident.clone(), | ||||
|             generics: generics.into(), | ||||
|             fields: Fields::Named(mask_type_sim_value_fields), | ||||
|             semi_token: None, | ||||
|         } | ||||
|         .to_tokens(tokens); | ||||
|         let mut sim_value_fields = FieldsNamed::from(fields.clone()); | ||||
|         for Field { ty, .. } in &mut sim_value_fields.named { | ||||
|             *ty = parse_quote_spanned! {span=> | ||||
|                 ::fayalite::sim::value::SimValue<#ty> | ||||
|             }; | ||||
|         } | ||||
|         ItemStruct { | ||||
|             attrs: vec![ | ||||
|                 parse_quote_spanned! {span=> | ||||
|                     #[::fayalite::__std::prelude::v1::derive(
 | ||||
|                         ::fayalite::__std::fmt::Debug, | ||||
|                         ::fayalite::__std::clone::Clone, | ||||
|                     )] | ||||
|                 }, | ||||
|                 parse_quote_spanned! {span=> | ||||
|                     #[allow(non_camel_case_types, dead_code)] | ||||
|                 }, | ||||
|             ], | ||||
|             vis: vis.clone(), | ||||
|             struct_token: *struct_token, | ||||
|             ident: sim_value_ident.clone(), | ||||
|             generics: generics.into(), | ||||
|             fields: Fields::Named(sim_value_fields), | ||||
|             semi_token: None, | ||||
|         } | ||||
|         .to_tokens(tokens); | ||||
|         let this_token = Ident::new("__this", span); | ||||
|         let fields_token = Ident::new("__fields", span); | ||||
|         let self_token = Token; | ||||
|  | @ -613,6 +673,31 @@ impl ToTokens for ParsedBundle { | |||
|                 } | ||||
|             }, | ||||
|         )); | ||||
|         let sim_value_from_bits_fields = Vec::from_iter(fields.named().into_iter().map(|field| { | ||||
|             let ident: &Ident = field.ident().as_ref().unwrap(); | ||||
|             quote_spanned! {span=> | ||||
|                 #ident: v.field_from_bits(), | ||||
|             } | ||||
|         })); | ||||
|         let sim_value_clone_from_bits_fields = | ||||
|             Vec::from_iter(fields.named().into_iter().map(|field| { | ||||
|                 let ident: &Ident = field.ident().as_ref().unwrap(); | ||||
|                 quote_spanned! {span=> | ||||
|                     v.field_clone_from_bits(&mut value.#ident); | ||||
|                 } | ||||
|             })); | ||||
|         let sim_value_to_bits_fields = Vec::from_iter(fields.named().into_iter().map(|field| { | ||||
|             let ident: &Ident = field.ident().as_ref().unwrap(); | ||||
|             quote_spanned! {span=> | ||||
|                 v.field_to_bits(&value.#ident); | ||||
|             } | ||||
|         })); | ||||
|         let to_sim_value_fields = Vec::from_iter(fields.named().into_iter().map(|field| { | ||||
|             let ident: &Ident = field.ident().as_ref().unwrap(); | ||||
|             quote_spanned! {span=> | ||||
|                 #ident: ::fayalite::sim::value::SimValue::ty(&self.#ident), | ||||
|             } | ||||
|         })); | ||||
|         let fields_len = fields.named().into_iter().len(); | ||||
|         quote_spanned! {span=> | ||||
|             #[automatically_derived] | ||||
|  | @ -621,6 +706,7 @@ impl ToTokens for ParsedBundle { | |||
|             { | ||||
|                 type BaseType = ::fayalite::bundle::Bundle; | ||||
|                 type MaskType = #mask_type_ident #type_generics; | ||||
|                 type SimValue = #mask_type_sim_value_ident #type_generics; | ||||
|                 type MatchVariant = #mask_type_match_variant_ident #type_generics; | ||||
|                 type MatchActiveScope = (); | ||||
|                 type MatchVariantAndInactiveScope = ::fayalite::ty::MatchVariantWithoutScope< | ||||
|  | @ -658,6 +744,34 @@ impl ToTokens for ParsedBundle { | |||
|                 fn source_location() -> ::fayalite::source_location::SourceLocation { | ||||
|                     ::fayalite::source_location::SourceLocation::caller() | ||||
|                 } | ||||
|                 fn sim_value_from_bits( | ||||
|                     &self, | ||||
|                     bits: &::fayalite::bitvec::slice::BitSlice, | ||||
|                 ) -> <Self as ::fayalite::ty::Type>::SimValue { | ||||
|                     #![allow(unused_mut, unused_variables)] | ||||
|                     let mut v = ::fayalite::bundle::BundleSimValueFromBits::new(*self, bits); | ||||
|                     #mask_type_sim_value_ident { | ||||
|                         #(#sim_value_from_bits_fields)* | ||||
|                     } | ||||
|                 } | ||||
|                 fn sim_value_clone_from_bits( | ||||
|                     &self, | ||||
|                     value: &mut <Self as ::fayalite::ty::Type>::SimValue, | ||||
|                     bits: &::fayalite::bitvec::slice::BitSlice, | ||||
|                 ) { | ||||
|                     #![allow(unused_mut, unused_variables)] | ||||
|                     let mut v = ::fayalite::bundle::BundleSimValueFromBits::new(*self, bits); | ||||
|                     #(#sim_value_clone_from_bits_fields)* | ||||
|                 } | ||||
|                 fn sim_value_to_bits( | ||||
|                     &self, | ||||
|                     value: &<Self as ::fayalite::ty::Type>::SimValue, | ||||
|                     bits: &mut ::fayalite::bitvec::slice::BitSlice, | ||||
|                 ) { | ||||
|                     #![allow(unused_mut, unused_variables)] | ||||
|                     let mut v = ::fayalite::bundle::BundleSimValueToBits::new(*self, bits); | ||||
|                     #(#sim_value_to_bits_fields)* | ||||
|                 } | ||||
|             } | ||||
|             #[automatically_derived] | ||||
|             impl #impl_generics ::fayalite::bundle::BundleType for #mask_type_ident #type_generics | ||||
|  | @ -689,11 +803,57 @@ impl ToTokens for ParsedBundle { | |||
|                 } | ||||
|             } | ||||
|             #[automatically_derived] | ||||
|             impl #impl_generics ::fayalite::sim::value::ToSimValue for #mask_type_sim_value_ident #type_generics | ||||
|             #where_clause | ||||
|             { | ||||
|                 type Type = #mask_type_ident #type_generics; | ||||
| 
 | ||||
|                 fn to_sim_value( | ||||
|                     &self, | ||||
|                 ) -> ::fayalite::sim::value::SimValue< | ||||
|                     <Self as ::fayalite::sim::value::ToSimValue>::Type, | ||||
|                 > { | ||||
|                     let ty = #mask_type_ident { | ||||
|                         #(#to_sim_value_fields)* | ||||
|                     }; | ||||
|                     ::fayalite::sim::value::SimValue::from_value(ty, ::fayalite::__std::clone::Clone::clone(self)) | ||||
|                 } | ||||
|                 fn into_sim_value( | ||||
|                     self, | ||||
|                 ) -> ::fayalite::sim::value::SimValue< | ||||
|                     <Self as ::fayalite::sim::value::ToSimValue>::Type, | ||||
|                 > { | ||||
|                     let ty = #mask_type_ident { | ||||
|                         #(#to_sim_value_fields)* | ||||
|                     }; | ||||
|                     ::fayalite::sim::value::SimValue::from_value(ty, self) | ||||
|                 } | ||||
|             } | ||||
|             #[automatically_derived] | ||||
|             impl #impl_generics ::fayalite::sim::value::ToSimValueWithType<#mask_type_ident #type_generics> | ||||
|             for #mask_type_sim_value_ident #type_generics | ||||
|             #where_clause | ||||
|             { | ||||
|                 fn to_sim_value_with_type( | ||||
|                     &self, | ||||
|                     ty: #mask_type_ident #type_generics, | ||||
|                 ) -> ::fayalite::sim::value::SimValue<#mask_type_ident #type_generics> { | ||||
|                     ::fayalite::sim::value::SimValue::from_value(ty, ::fayalite::__std::clone::Clone::clone(self)) | ||||
|                 } | ||||
|                 fn into_sim_value_with_type( | ||||
|                     self, | ||||
|                     ty: #mask_type_ident #type_generics, | ||||
|                 ) -> ::fayalite::sim::value::SimValue<#mask_type_ident #type_generics> { | ||||
|                     ::fayalite::sim::value::SimValue::from_value(ty, self) | ||||
|                 } | ||||
|             } | ||||
|             #[automatically_derived] | ||||
|             impl #impl_generics ::fayalite::ty::Type for #target #type_generics | ||||
|             #where_clause | ||||
|             { | ||||
|                 type BaseType = ::fayalite::bundle::Bundle; | ||||
|                 type MaskType = #mask_type_ident #type_generics; | ||||
|                 type SimValue = #sim_value_ident #type_generics; | ||||
|                 type MatchVariant = #match_variant_ident #type_generics; | ||||
|                 type MatchActiveScope = (); | ||||
|                 type MatchVariantAndInactiveScope = ::fayalite::ty::MatchVariantWithoutScope< | ||||
|  | @ -733,6 +893,34 @@ impl ToTokens for ParsedBundle { | |||
|                 fn source_location() -> ::fayalite::source_location::SourceLocation { | ||||
|                     ::fayalite::source_location::SourceLocation::caller() | ||||
|                 } | ||||
|                 fn sim_value_from_bits( | ||||
|                     &self, | ||||
|                     bits: &::fayalite::bitvec::slice::BitSlice, | ||||
|                 ) -> <Self as ::fayalite::ty::Type>::SimValue { | ||||
|                     #![allow(unused_mut, unused_variables)] | ||||
|                     let mut v = ::fayalite::bundle::BundleSimValueFromBits::new(*self, bits); | ||||
|                     #sim_value_ident { | ||||
|                         #(#sim_value_from_bits_fields)* | ||||
|                     } | ||||
|                 } | ||||
|                 fn sim_value_clone_from_bits( | ||||
|                     &self, | ||||
|                     value: &mut <Self as ::fayalite::ty::Type>::SimValue, | ||||
|                     bits: &::fayalite::bitvec::slice::BitSlice, | ||||
|                 ) { | ||||
|                     #![allow(unused_mut, unused_variables)] | ||||
|                     let mut v = ::fayalite::bundle::BundleSimValueFromBits::new(*self, bits); | ||||
|                     #(#sim_value_clone_from_bits_fields)* | ||||
|                 } | ||||
|                 fn sim_value_to_bits( | ||||
|                     &self, | ||||
|                     value: &<Self as ::fayalite::ty::Type>::SimValue, | ||||
|                     bits: &mut ::fayalite::bitvec::slice::BitSlice, | ||||
|                 ) { | ||||
|                     #![allow(unused_mut, unused_variables)] | ||||
|                     let mut v = ::fayalite::bundle::BundleSimValueToBits::new(*self, bits); | ||||
|                     #(#sim_value_to_bits_fields)* | ||||
|                 } | ||||
|             } | ||||
|             #[automatically_derived] | ||||
|             impl #impl_generics ::fayalite::bundle::BundleType for #target #type_generics | ||||
|  | @ -763,8 +951,144 @@ impl ToTokens for ParsedBundle { | |||
|                     ::fayalite::intern::Interned::into_inner(::fayalite::intern::Intern::intern_sized(__retval)) | ||||
|                 } | ||||
|             } | ||||
|             #[automatically_derived] | ||||
|             impl #impl_generics ::fayalite::sim::value::ToSimValue for #sim_value_ident #type_generics | ||||
|             #where_clause | ||||
|             { | ||||
|                 type Type = #target #type_generics; | ||||
| 
 | ||||
|                 fn to_sim_value( | ||||
|                     &self, | ||||
|                 ) -> ::fayalite::sim::value::SimValue< | ||||
|                     <Self as ::fayalite::sim::value::ToSimValue>::Type, | ||||
|                 > { | ||||
|                     let ty = #target { | ||||
|                         #(#to_sim_value_fields)* | ||||
|                     }; | ||||
|                     ::fayalite::sim::value::SimValue::from_value(ty, ::fayalite::__std::clone::Clone::clone(self)) | ||||
|                 } | ||||
|                 fn into_sim_value( | ||||
|                     self, | ||||
|                 ) -> ::fayalite::sim::value::SimValue< | ||||
|                     <Self as ::fayalite::sim::value::ToSimValue>::Type, | ||||
|                 > { | ||||
|                     let ty = #target { | ||||
|                         #(#to_sim_value_fields)* | ||||
|                     }; | ||||
|                     ::fayalite::sim::value::SimValue::from_value(ty, self) | ||||
|                 } | ||||
|             } | ||||
|             #[automatically_derived] | ||||
|             impl #impl_generics ::fayalite::sim::value::ToSimValueWithType<#target #type_generics> | ||||
|             for #sim_value_ident #type_generics | ||||
|             #where_clause | ||||
|             { | ||||
|                 fn to_sim_value_with_type( | ||||
|                     &self, | ||||
|                     ty: #target #type_generics, | ||||
|                 ) -> ::fayalite::sim::value::SimValue<#target #type_generics> { | ||||
|                     ::fayalite::sim::value::SimValue::from_value(ty, ::fayalite::__std::clone::Clone::clone(self)) | ||||
|                 } | ||||
|                 fn into_sim_value_with_type( | ||||
|                     self, | ||||
|                     ty: #target #type_generics, | ||||
|                 ) -> ::fayalite::sim::value::SimValue<#target #type_generics> { | ||||
|                     ::fayalite::sim::value::SimValue::from_value(ty, self) | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         .to_tokens(tokens); | ||||
|         if let Some((cmp_eq,)) = cmp_eq { | ||||
|             let mut expr_where_clause = | ||||
|                 Generics::from(generics) | ||||
|                     .where_clause | ||||
|                     .unwrap_or_else(|| syn::WhereClause { | ||||
|                         where_token: Token, | ||||
|                         predicates: Punctuated::new(), | ||||
|                     }); | ||||
|             let mut sim_value_where_clause = expr_where_clause.clone(); | ||||
|             let mut fields_sim_value_eq = vec![]; | ||||
|             let mut fields_cmp_eq = vec![]; | ||||
|             let mut fields_cmp_ne = vec![]; | ||||
|             for field in fields.named() { | ||||
|                 let field_ident = field.ident(); | ||||
|                 let field_ty = field.ty(); | ||||
|                 expr_where_clause | ||||
|                     .predicates | ||||
|                     .push(parse_quote_spanned! {cmp_eq.span=> | ||||
|                         #field_ty: ::fayalite::expr::ops::ExprPartialEq<#field_ty> | ||||
|                     }); | ||||
|                 sim_value_where_clause | ||||
|                     .predicates | ||||
|                     .push(parse_quote_spanned! {cmp_eq.span=> | ||||
|                         #field_ty: ::fayalite::sim::value::SimValuePartialEq<#field_ty> | ||||
|                     }); | ||||
|                 fields_sim_value_eq.push(quote_spanned! {span=> | ||||
|                     ::fayalite::sim::value::SimValuePartialEq::sim_value_eq(&__lhs.#field_ident, &__rhs.#field_ident) | ||||
|                 }); | ||||
|                 fields_cmp_eq.push(quote_spanned! {span=> | ||||
|                     ::fayalite::expr::ops::ExprPartialEq::cmp_eq(__lhs.#field_ident, __rhs.#field_ident) | ||||
|                 }); | ||||
|                 fields_cmp_ne.push(quote_spanned! {span=> | ||||
|                     ::fayalite::expr::ops::ExprPartialEq::cmp_ne(__lhs.#field_ident, __rhs.#field_ident) | ||||
|                 }); | ||||
|             } | ||||
|             let sim_value_eq_body; | ||||
|             let cmp_eq_body; | ||||
|             let cmp_ne_body; | ||||
|             if fields_len == 0 { | ||||
|                 sim_value_eq_body = quote_spanned! {span=> | ||||
|                     true | ||||
|                 }; | ||||
|                 cmp_eq_body = quote_spanned! {span=> | ||||
|                     ::fayalite::expr::ToExpr::to_expr(&true) | ||||
|                 }; | ||||
|                 cmp_ne_body = quote_spanned! {span=> | ||||
|                     ::fayalite::expr::ToExpr::to_expr(&false) | ||||
|                 }; | ||||
|             } else { | ||||
|                 sim_value_eq_body = quote_spanned! {span=> | ||||
|                     #(#fields_sim_value_eq)&&* | ||||
|                 }; | ||||
|                 cmp_eq_body = quote_spanned! {span=> | ||||
|                     #(#fields_cmp_eq)&* | ||||
|                 }; | ||||
|                 cmp_ne_body = quote_spanned! {span=> | ||||
|                     #(#fields_cmp_ne)|* | ||||
|                 }; | ||||
|             }; | ||||
|             quote_spanned! {span=> | ||||
|                 #[automatically_derived] | ||||
|                 impl #impl_generics ::fayalite::expr::ops::ExprPartialEq<Self> for #target #type_generics | ||||
|                 #expr_where_clause | ||||
|                 { | ||||
|                     fn cmp_eq( | ||||
|                         __lhs: ::fayalite::expr::Expr<Self>, | ||||
|                         __rhs: ::fayalite::expr::Expr<Self>, | ||||
|                     ) -> ::fayalite::expr::Expr<::fayalite::int::Bool> { | ||||
|                         #cmp_eq_body | ||||
|                     } | ||||
|                     fn cmp_ne( | ||||
|                         __lhs: ::fayalite::expr::Expr<Self>, | ||||
|                         __rhs: ::fayalite::expr::Expr<Self>, | ||||
|                     ) -> ::fayalite::expr::Expr<::fayalite::int::Bool> { | ||||
|                         #cmp_ne_body | ||||
|                     } | ||||
|                 } | ||||
|                 #[automatically_derived] | ||||
|                 impl #impl_generics ::fayalite::sim::value::SimValuePartialEq<Self> for #target #type_generics | ||||
|                 #sim_value_where_clause | ||||
|                 { | ||||
|                     fn sim_value_eq( | ||||
|                         __lhs: &::fayalite::sim::value::SimValue<Self>, | ||||
|                         __rhs: &::fayalite::sim::value::SimValue<Self>, | ||||
|                     ) -> bool { | ||||
|                         #sim_value_eq_body | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|             .to_tokens(tokens); | ||||
|         } | ||||
|         if let (None, MaybeParsed::Parsed(generics)) = (no_static, &self.generics) { | ||||
|             let static_generics = generics.clone().for_static_type(); | ||||
|             let (static_impl_generics, static_type_generics, static_where_clause) = | ||||
|  | @ -800,6 +1124,14 @@ impl ToTokens for ParsedBundle { | |||
|                 } | ||||
|             })); | ||||
|             quote_spanned! {span=> | ||||
|                 #[automatically_derived] | ||||
|                 impl #static_impl_generics ::fayalite::__std::default::Default for #mask_type_ident #static_type_generics | ||||
|                 #static_where_clause | ||||
|                 { | ||||
|                     fn default() -> Self { | ||||
|                         <Self as ::fayalite::ty::StaticType>::TYPE | ||||
|                     } | ||||
|                 } | ||||
|                 #[automatically_derived] | ||||
|                 impl #static_impl_generics ::fayalite::ty::StaticType for #mask_type_ident #static_type_generics | ||||
|                 #static_where_clause | ||||
|  | @ -822,6 +1154,15 @@ impl ToTokens for ParsedBundle { | |||
|                     }; | ||||
|                 } | ||||
|                 #[automatically_derived] | ||||
|                 impl #static_impl_generics ::fayalite::__std::default::Default | ||||
|                 for #target #static_type_generics | ||||
|                 #static_where_clause | ||||
|                 { | ||||
|                     fn default() -> Self { | ||||
|                         <Self as ::fayalite::ty::StaticType>::TYPE | ||||
|                     } | ||||
|                 } | ||||
|                 #[automatically_derived] | ||||
|                 impl #static_impl_generics ::fayalite::ty::StaticType for #target #static_type_generics | ||||
|                 #static_where_clause | ||||
|                 { | ||||
|  |  | |||
|  | @ -129,6 +129,9 @@ pub(crate) struct ParsedEnum { | |||
|     pub(crate) brace_token: Brace, | ||||
|     pub(crate) variants: Punctuated<ParsedVariant, Token![,]>, | ||||
|     pub(crate) match_variant_ident: Ident, | ||||
|     pub(crate) sim_value_ident: Ident, | ||||
|     pub(crate) sim_builder_ident: Ident, | ||||
|     pub(crate) sim_builder_ty_field_ident: Ident, | ||||
| } | ||||
| 
 | ||||
| impl ParsedEnum { | ||||
|  | @ -155,7 +158,11 @@ impl ParsedEnum { | |||
|             custom_bounds, | ||||
|             no_static: _, | ||||
|             no_runtime_generics: _, | ||||
|             cmp_eq, | ||||
|         } = options.body; | ||||
|         if let Some((cmp_eq,)) = cmp_eq { | ||||
|             errors.error(cmp_eq, "#[hdl(cmp_eq)] is not yet implemented for enums"); | ||||
|         } | ||||
|         attrs.retain(|attr| { | ||||
|             if attr.path().is_ident("repr") { | ||||
|                 errors.error(attr, "#[repr] is not supported on #[hdl] enums"); | ||||
|  | @ -186,6 +193,9 @@ impl ParsedEnum { | |||
|             brace_token, | ||||
|             variants, | ||||
|             match_variant_ident: format_ident!("__{}__MatchVariant", ident), | ||||
|             sim_value_ident: format_ident!("__{}__SimValue", ident), | ||||
|             sim_builder_ident: format_ident!("__{}__SimBuilder", ident), | ||||
|             sim_builder_ty_field_ident: format_ident!("__ty", span = ident.span()), | ||||
|             ident, | ||||
|         }) | ||||
|     } | ||||
|  | @ -203,6 +213,9 @@ impl ToTokens for ParsedEnum { | |||
|             brace_token, | ||||
|             variants, | ||||
|             match_variant_ident, | ||||
|             sim_value_ident, | ||||
|             sim_builder_ident, | ||||
|             sim_builder_ty_field_ident, | ||||
|         } = self; | ||||
|         let span = ident.span(); | ||||
|         let ItemOptions { | ||||
|  | @ -211,6 +224,7 @@ impl ToTokens for ParsedEnum { | |||
|             custom_bounds: _, | ||||
|             no_static, | ||||
|             no_runtime_generics, | ||||
|             cmp_eq: _, // TODO: implement cmp_eq for enums
 | ||||
|         } = &options.body; | ||||
|         let target = get_target(target, ident); | ||||
|         let mut struct_attrs = attrs.clone(); | ||||
|  | @ -404,6 +418,133 @@ impl ToTokens for ParsedEnum { | |||
|             )), | ||||
|         } | ||||
|         .to_tokens(tokens); | ||||
|         let mut struct_attrs = attrs.clone(); | ||||
|         struct_attrs.push(parse_quote_spanned! {span=> | ||||
|             #[allow(dead_code, non_camel_case_types)] | ||||
|         }); | ||||
|         ItemStruct { | ||||
|             attrs: struct_attrs, | ||||
|             vis: vis.clone(), | ||||
|             struct_token: Token, | ||||
|             ident: sim_builder_ident.clone(), | ||||
|             generics: generics.into(), | ||||
|             fields: FieldsNamed { | ||||
|                 brace_token: *brace_token, | ||||
|                 named: Punctuated::from_iter([Field { | ||||
|                     attrs: vec![], | ||||
|                     vis: Visibility::Inherited, | ||||
|                     mutability: FieldMutability::None, | ||||
|                     ident: Some(sim_builder_ty_field_ident.clone()), | ||||
|                     colon_token: Some(Token), | ||||
|                     ty: parse_quote_spanned! {span=> | ||||
|                         #target #type_generics | ||||
|                     }, | ||||
|                 }]), | ||||
|             } | ||||
|             .into(), | ||||
|             semi_token: None, | ||||
|         } | ||||
|         .to_tokens(tokens); | ||||
|         let mut enum_attrs = attrs.clone(); | ||||
|         enum_attrs.push(parse_quote_spanned! {span=> | ||||
|             #[::fayalite::__std::prelude::v1::derive(
 | ||||
|                 ::fayalite::__std::fmt::Debug, | ||||
|                 ::fayalite::__std::clone::Clone, | ||||
|             )] | ||||
|         }); | ||||
|         enum_attrs.push(parse_quote_spanned! {span=> | ||||
|             #[allow(dead_code, non_camel_case_types)] | ||||
|         }); | ||||
|         let sim_value_has_unknown_variant = !variants.len().is_power_of_two(); | ||||
|         let sim_value_unknown_variant_name = sim_value_has_unknown_variant.then(|| { | ||||
|             let mut name = String::new(); | ||||
|             let unknown = "Unknown"; | ||||
|             loop { | ||||
|                 let orig_len = name.len(); | ||||
|                 name.push_str(unknown); | ||||
|                 if variants.iter().all(|v| v.ident != name) { | ||||
|                     break Ident::new(&name, span); | ||||
|                 } | ||||
|                 name.truncate(orig_len); | ||||
|                 name.push('_'); | ||||
|             } | ||||
|         }); | ||||
|         let sim_value_unknown_variant = | ||||
|             sim_value_unknown_variant_name | ||||
|                 .as_ref() | ||||
|                 .map(|unknown_variant_name| { | ||||
|                     Pair::End(parse_quote_spanned! {span=> | ||||
|                         #unknown_variant_name(::fayalite::enum_::UnknownVariantSimValue) | ||||
|                     }) | ||||
|                 }); | ||||
|         ItemEnum { | ||||
|             attrs: enum_attrs, | ||||
|             vis: vis.clone(), | ||||
|             enum_token: *enum_token, | ||||
|             ident: sim_value_ident.clone(), | ||||
|             generics: generics.into(), | ||||
|             brace_token: *brace_token, | ||||
|             variants: Punctuated::from_iter( | ||||
|                 variants | ||||
|                     .pairs() | ||||
|                     .map_pair_value_ref( | ||||
|                         |ParsedVariant { | ||||
|                              attrs, | ||||
|                              options: _, | ||||
|                              ident, | ||||
|                              field, | ||||
|                          }| Variant { | ||||
|                             attrs: attrs.clone(), | ||||
|                             ident: ident.clone(), | ||||
|                             fields: match field { | ||||
|                                 Some(ParsedVariantField { | ||||
|                                     paren_token, | ||||
|                                     attrs, | ||||
|                                     options: _, | ||||
|                                     ty, | ||||
|                                     comma_token, | ||||
|                                 }) => Fields::Unnamed(FieldsUnnamed { | ||||
|                                     paren_token: *paren_token, | ||||
|                                     unnamed: Punctuated::from_iter([ | ||||
|                                         Pair::new( | ||||
|                                             Field { | ||||
|                                                 attrs: attrs.clone(), | ||||
|                                                 vis: Visibility::Inherited, | ||||
|                                                 mutability: FieldMutability::None, | ||||
|                                                 ident: None, | ||||
|                                                 colon_token: None, | ||||
|                                                 ty: parse_quote_spanned! {span=> | ||||
|                                                     ::fayalite::sim::value::SimValue<#ty> | ||||
|                                                 }, | ||||
|                                             }, | ||||
|                                             Some(comma_token.unwrap_or(Token))), | ||||
|                                         ), | ||||
|                                         Pair::new( | ||||
|                                             Field { | ||||
|                                                 attrs: vec![], | ||||
|                                                 vis: Visibility::Inherited, | ||||
|                                                 mutability: FieldMutability::None, | ||||
|                                                 ident: None, | ||||
|                                                 colon_token: None, | ||||
|                                                 ty: parse_quote_spanned! {span=> | ||||
|                                                     ::fayalite::enum_::EnumPaddingSimValue | ||||
|                                                 }, | ||||
|                                             }, | ||||
|                                             None, | ||||
|                                         ), | ||||
|                                     ]), | ||||
|                                 }), | ||||
|                                 None => Fields::Unnamed(parse_quote_spanned! {span=> | ||||
|                                     (::fayalite::enum_::EnumPaddingSimValue) | ||||
|                                 }), | ||||
|                             }, | ||||
|                             discriminant: None, | ||||
|                         }, | ||||
|                     ) | ||||
|                     .chain(sim_value_unknown_variant), | ||||
|             ), | ||||
|         } | ||||
|         .to_tokens(tokens); | ||||
|         let self_token = Token; | ||||
|         for (index, ParsedVariant { ident, field, .. }) in variants.iter().enumerate() { | ||||
|             if let Some(ParsedVariantField { ty, .. }) = field { | ||||
|  | @ -430,6 +571,25 @@ impl ToTokens for ParsedEnum { | |||
|                             ) | ||||
|                         } | ||||
|                     } | ||||
|                     #[automatically_derived] | ||||
|                     impl #impl_generics #sim_builder_ident #type_generics | ||||
|                     #where_clause | ||||
|                     { | ||||
|                         #[allow(non_snake_case, dead_code)] | ||||
|                         #vis fn #ident<__V: ::fayalite::sim::value::ToSimValueWithType<#ty>>( | ||||
|                             #self_token, | ||||
|                             v: __V, | ||||
|                         ) -> ::fayalite::sim::value::SimValue<#target #type_generics> { | ||||
|                             let v = ::fayalite::sim::value::ToSimValueWithType::into_sim_value_with_type( | ||||
|                                 v, | ||||
|                                 #self_token.#sim_builder_ty_field_ident.#ident, | ||||
|                             ); | ||||
|                             ::fayalite::sim::value::SimValue::from_value( | ||||
|                                 #self_token.#sim_builder_ty_field_ident, | ||||
|                                 #sim_value_ident::#ident(v, ::fayalite::enum_::EnumPaddingSimValue::new()), | ||||
|                             ) | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|             } else { | ||||
|                 quote_spanned! {span=> | ||||
|  | @ -448,6 +608,18 @@ impl ToTokens for ParsedEnum { | |||
|                             ) | ||||
|                         } | ||||
|                     } | ||||
|                     #[automatically_derived] | ||||
|                     impl #impl_generics #sim_builder_ident #type_generics | ||||
|                     #where_clause | ||||
|                     { | ||||
|                         #[allow(non_snake_case, dead_code)] | ||||
|                         #vis fn #ident(#self_token) -> ::fayalite::sim::value::SimValue<#target #type_generics> { | ||||
|                             ::fayalite::sim::value::SimValue::from_value( | ||||
|                                 #self_token.#sim_builder_ty_field_ident, | ||||
|                                 #sim_value_ident::#ident(::fayalite::enum_::EnumPaddingSimValue::new()), | ||||
|                             ) | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|             .to_tokens(tokens); | ||||
|  | @ -529,6 +701,142 @@ impl ToTokens for ParsedEnum { | |||
|                 } | ||||
|             }, | ||||
|         )); | ||||
|         let sim_value_from_bits_unknown_match_arm = if let Some(sim_value_unknown_variant_name) = | ||||
|             &sim_value_unknown_variant_name | ||||
|         { | ||||
|             quote_spanned! {span=> | ||||
|                 _ => #sim_value_ident::#sim_value_unknown_variant_name(v.unknown_variant_from_bits()), | ||||
|             } | ||||
|         } else { | ||||
|             quote_spanned! {span=> | ||||
|                 _ => ::fayalite::__std::unreachable!(), | ||||
|             } | ||||
|         }; | ||||
|         let sim_value_from_bits_match_arms = Vec::from_iter( | ||||
|             variants | ||||
|                 .iter() | ||||
|                 .enumerate() | ||||
|                 .map( | ||||
|                     |( | ||||
|                         index, | ||||
|                         ParsedVariant { | ||||
|                             attrs: _, | ||||
|                             options: _, | ||||
|                             ident, | ||||
|                             field, | ||||
|                         }, | ||||
|                     )| { | ||||
|                         if let Some(_) = field { | ||||
|                             quote_spanned! {span=> | ||||
|                                 #index => { | ||||
|                                     let (field, padding) = v.variant_with_field_from_bits(); | ||||
|                                     #sim_value_ident::#ident(field, padding) | ||||
|                                 } | ||||
|                             } | ||||
|                         } else { | ||||
|                             quote_spanned! {span=> | ||||
|                                 #index => #sim_value_ident::#ident( | ||||
|                                     v.variant_no_field_from_bits(), | ||||
|                                 ), | ||||
|                             } | ||||
|                         } | ||||
|                     }, | ||||
|                 ) | ||||
|                 .chain([sim_value_from_bits_unknown_match_arm]), | ||||
|         ); | ||||
|         let sim_value_clone_from_bits_unknown_match_arm = | ||||
|             if let Some(sim_value_unknown_variant_name) = &sim_value_unknown_variant_name { | ||||
|                 quote_spanned! {span=> | ||||
|                     _ => if let #sim_value_ident::#sim_value_unknown_variant_name(value) = value { | ||||
|                         v.unknown_variant_clone_from_bits(value); | ||||
|                     } else { | ||||
|                         *value = #sim_value_ident::#sim_value_unknown_variant_name( | ||||
|                             v.unknown_variant_from_bits(), | ||||
|                         ); | ||||
|                     }, | ||||
|                 } | ||||
|             } else { | ||||
|                 quote_spanned! {span=> | ||||
|                     _ => ::fayalite::__std::unreachable!(), | ||||
|                 } | ||||
|             }; | ||||
|         let sim_value_clone_from_bits_match_arms = Vec::from_iter( | ||||
|             variants | ||||
|                 .iter() | ||||
|                 .enumerate() | ||||
|                 .map( | ||||
|                     |( | ||||
|                         index, | ||||
|                         ParsedVariant { | ||||
|                             attrs: _, | ||||
|                             options: _, | ||||
|                             ident, | ||||
|                             field, | ||||
|                         }, | ||||
|                     )| { | ||||
|                         if let Some(_) = field { | ||||
|                             quote_spanned! {span=> | ||||
|                                 #index => if let #sim_value_ident::#ident(field, padding) = value { | ||||
|                                     v.variant_with_field_clone_from_bits(field, padding); | ||||
|                                 } else { | ||||
|                                     let (field, padding) = v.variant_with_field_from_bits(); | ||||
|                                     *value = #sim_value_ident::#ident(field, padding); | ||||
|                                 }, | ||||
|                             } | ||||
|                         } else { | ||||
|                             quote_spanned! {span=> | ||||
|                                 #index => if let #sim_value_ident::#ident(padding) = value { | ||||
|                                     v.variant_no_field_clone_from_bits(padding); | ||||
|                                 } else { | ||||
|                                     *value = #sim_value_ident::#ident( | ||||
|                                         v.variant_no_field_from_bits(), | ||||
|                                     ); | ||||
|                                 }, | ||||
|                             } | ||||
|                         } | ||||
|                     }, | ||||
|                 ) | ||||
|                 .chain([sim_value_clone_from_bits_unknown_match_arm]), | ||||
|         ); | ||||
|         let sim_value_to_bits_match_arms = Vec::from_iter( | ||||
|             variants | ||||
|                 .iter() | ||||
|                 .enumerate() | ||||
|                 .map( | ||||
|                     |( | ||||
|                         index, | ||||
|                         ParsedVariant { | ||||
|                             attrs: _, | ||||
|                             options: _, | ||||
|                             ident, | ||||
|                             field, | ||||
|                         }, | ||||
|                     )| { | ||||
|                         if let Some(_) = field { | ||||
|                             quote_spanned! {span=> | ||||
|                                 #sim_value_ident::#ident(field, padding) => { | ||||
|                                     v.variant_with_field_to_bits(#index, field, padding); | ||||
|                                 } | ||||
|                             } | ||||
|                         } else { | ||||
|                             quote_spanned! {span=> | ||||
|                                 #sim_value_ident::#ident(padding) => { | ||||
|                                     v.variant_no_field_to_bits(#index, padding); | ||||
|                                 } | ||||
|                             } | ||||
|                         } | ||||
|                     }, | ||||
|                 ) | ||||
|                 .chain(sim_value_unknown_variant_name.as_ref().map( | ||||
|                     |sim_value_unknown_variant_name| { | ||||
|                         quote_spanned! {span=> | ||||
|                             #sim_value_ident::#sim_value_unknown_variant_name(value) => { | ||||
|                                 v.unknown_variant_to_bits(value); | ||||
|                             } | ||||
|                         } | ||||
|                     }, | ||||
|                 )), | ||||
|         ); | ||||
|         let variants_len = variants.len(); | ||||
|         quote_spanned! {span=> | ||||
|             #[automatically_derived] | ||||
|  | @ -537,6 +845,7 @@ impl ToTokens for ParsedEnum { | |||
|             { | ||||
|                 type BaseType = ::fayalite::enum_::Enum; | ||||
|                 type MaskType = ::fayalite::int::Bool; | ||||
|                 type SimValue = #sim_value_ident #type_generics; | ||||
|                 type MatchVariant = #match_variant_ident #type_generics; | ||||
|                 type MatchActiveScope = ::fayalite::module::Scope; | ||||
|                 type MatchVariantAndInactiveScope = ::fayalite::enum_::EnumMatchVariantAndInactiveScope<Self>; | ||||
|  | @ -569,11 +878,41 @@ impl ToTokens for ParsedEnum { | |||
|                 fn source_location() -> ::fayalite::source_location::SourceLocation { | ||||
|                     ::fayalite::source_location::SourceLocation::caller() | ||||
|                 } | ||||
|                 fn sim_value_from_bits( | ||||
|                     &self, | ||||
|                     bits: &::fayalite::bitvec::slice::BitSlice, | ||||
|                 ) -> <Self as ::fayalite::ty::Type>::SimValue { | ||||
|                     let v = ::fayalite::enum_::EnumSimValueFromBits::new(*self, bits); | ||||
|                     match v.discriminant() { | ||||
|                         #(#sim_value_from_bits_match_arms)* | ||||
|                     } | ||||
|                 } | ||||
|                 fn sim_value_clone_from_bits( | ||||
|                     &self, | ||||
|                     value: &mut <Self as ::fayalite::ty::Type>::SimValue, | ||||
|                     bits: &::fayalite::bitvec::slice::BitSlice, | ||||
|                 ) { | ||||
|                     let v = ::fayalite::enum_::EnumSimValueFromBits::new(*self, bits); | ||||
|                     match v.discriminant() { | ||||
|                         #(#sim_value_clone_from_bits_match_arms)* | ||||
|                     } | ||||
|                 } | ||||
|                 fn sim_value_to_bits( | ||||
|                     &self, | ||||
|                     value: &<Self as ::fayalite::ty::Type>::SimValue, | ||||
|                     bits: &mut ::fayalite::bitvec::slice::BitSlice, | ||||
|                 ) { | ||||
|                     let v = ::fayalite::enum_::EnumSimValueToBits::new(*self, bits); | ||||
|                     match value { | ||||
|                         #(#sim_value_to_bits_match_arms)* | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|             #[automatically_derived] | ||||
|             impl #impl_generics ::fayalite::enum_::EnumType for #target #type_generics | ||||
|             #where_clause | ||||
|             { | ||||
|                 type SimBuilder = #sim_builder_ident #type_generics; | ||||
|                 fn match_activate_scope( | ||||
|                     v: <Self as ::fayalite::ty::Type>::MatchVariantAndInactiveScope, | ||||
|                 ) -> (<Self as ::fayalite::ty::Type>::MatchVariant, <Self as ::fayalite::ty::Type>::MatchActiveScope) { | ||||
|  | @ -592,6 +931,33 @@ impl ToTokens for ParsedEnum { | |||
|                     ][..]) | ||||
|                 } | ||||
|             } | ||||
|             #[automatically_derived] | ||||
|             impl #impl_generics ::fayalite::sim::value::ToSimValueWithType<#target #type_generics> | ||||
|             for #sim_value_ident #type_generics | ||||
|             #where_clause | ||||
|             { | ||||
|                 fn to_sim_value_with_type( | ||||
|                     &self, | ||||
|                     ty: #target #type_generics, | ||||
|                 ) -> ::fayalite::sim::value::SimValue<#target #type_generics> { | ||||
|                     ::fayalite::sim::value::SimValue::from_value(ty, ::fayalite::__std::clone::Clone::clone(self)) | ||||
|                 } | ||||
|                 fn into_sim_value_with_type( | ||||
|                     self, | ||||
|                     ty: #target #type_generics, | ||||
|                 ) -> ::fayalite::sim::value::SimValue<#target #type_generics> { | ||||
|                     ::fayalite::sim::value::SimValue::from_value(ty, self) | ||||
|                 } | ||||
|             } | ||||
|             #[automatically_derived] | ||||
|             impl #impl_generics ::fayalite::__std::convert::From<#target #type_generics> | ||||
|             for #sim_builder_ident #type_generics | ||||
|             #where_clause | ||||
|             { | ||||
|                 fn from(#sim_builder_ty_field_ident: #target #type_generics) -> Self { | ||||
|                     Self { #sim_builder_ty_field_ident } | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         .to_tokens(tokens); | ||||
|         if let (None, MaybeParsed::Parsed(generics)) = (no_static, &self.generics) { | ||||
|  | @ -629,6 +995,15 @@ impl ToTokens for ParsedEnum { | |||
|                     } | ||||
|                 })); | ||||
|             quote_spanned! {span=> | ||||
|                 #[automatically_derived] | ||||
|                 impl #static_impl_generics ::fayalite::__std::default::Default | ||||
|                 for #target #static_type_generics | ||||
|                 #static_where_clause | ||||
|                 { | ||||
|                     fn default() -> Self { | ||||
|                         <Self as ::fayalite::ty::StaticType>::TYPE | ||||
|                     } | ||||
|                 } | ||||
|                 #[automatically_derived] | ||||
|                 impl #static_impl_generics ::fayalite::ty::StaticType | ||||
|                 for #target #static_type_generics | ||||
|  | @ -647,6 +1022,34 @@ impl ToTokens for ParsedEnum { | |||
|                     const MASK_TYPE_PROPERTIES: ::fayalite::ty::TypeProperties = | ||||
|                         <::fayalite::int::Bool as ::fayalite::ty::StaticType>::TYPE_PROPERTIES; | ||||
|                 } | ||||
|                 #[automatically_derived] | ||||
|                 impl #static_impl_generics ::fayalite::sim::value::ToSimValue | ||||
|                 for #sim_value_ident #static_type_generics | ||||
|                 #static_where_clause | ||||
|                 { | ||||
|                     type Type = #target #static_type_generics; | ||||
| 
 | ||||
|                     fn to_sim_value( | ||||
|                         &self, | ||||
|                     ) -> ::fayalite::sim::value::SimValue< | ||||
|                         <Self as ::fayalite::sim::value::ToSimValue>::Type, | ||||
|                     > { | ||||
|                         ::fayalite::sim::value::SimValue::from_value( | ||||
|                             ::fayalite::ty::StaticType::TYPE, | ||||
|                             ::fayalite::__std::clone::Clone::clone(self), | ||||
|                         ) | ||||
|                     } | ||||
|                     fn into_sim_value( | ||||
|                         self, | ||||
|                     ) -> ::fayalite::sim::value::SimValue< | ||||
|                         <Self as ::fayalite::sim::value::ToSimValue>::Type, | ||||
|                     > { | ||||
|                         ::fayalite::sim::value::SimValue::from_value( | ||||
|                             ::fayalite::ty::StaticType::TYPE, | ||||
|                             self, | ||||
|                         ) | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|             .to_tokens(tokens); | ||||
|         } | ||||
|  |  | |||
|  | @ -49,10 +49,14 @@ impl ParsedTypeAlias { | |||
|             custom_bounds, | ||||
|             no_static, | ||||
|             no_runtime_generics: _, | ||||
|             cmp_eq, | ||||
|         } = options.body; | ||||
|         if let Some((no_static,)) = no_static { | ||||
|             errors.error(no_static, "no_static is not valid on type aliases"); | ||||
|         } | ||||
|         if let Some((cmp_eq,)) = cmp_eq { | ||||
|             errors.error(cmp_eq, "cmp_eq is not valid on type aliases"); | ||||
|         } | ||||
|         let generics = if custom_bounds.is_some() { | ||||
|             MaybeParsed::Unrecognized(generics) | ||||
|         } else if let Some(generics) = errors.ok(ParsedGenerics::parse(&mut generics)) { | ||||
|  | @ -95,6 +99,7 @@ impl ToTokens for ParsedTypeAlias { | |||
|             custom_bounds: _, | ||||
|             no_static: _, | ||||
|             no_runtime_generics, | ||||
|             cmp_eq: _, | ||||
|         } = &options.body; | ||||
|         let target = get_target(target, ident); | ||||
|         let mut type_attrs = attrs.clone(); | ||||
|  |  | |||
|  | @ -26,6 +26,7 @@ crate::options! { | |||
|         CustomBounds(custom_bounds), | ||||
|         NoStatic(no_static), | ||||
|         NoRuntimeGenerics(no_runtime_generics), | ||||
|         CmpEq(cmp_eq), | ||||
|     } | ||||
| } | ||||
| 
 | ||||
|  | @ -2069,11 +2070,16 @@ macro_rules! impl_bounds { | |||
|             $( | ||||
|                 $Variant:ident, | ||||
|             )* | ||||
|             $( | ||||
|                 #[unknown] | ||||
|                 $Unknown:ident, | ||||
|             )? | ||||
|         } | ||||
|     ) => { | ||||
|         #[derive(Clone, Debug)] | ||||
|         $vis enum $enum_type { | ||||
|             $($Variant(known_items::$Variant),)* | ||||
|             $($Unknown(syn::TypeParamBound),)? | ||||
|         } | ||||
| 
 | ||||
|         $(impl From<known_items::$Variant> for $enum_type { | ||||
|  | @ -2086,28 +2092,54 @@ macro_rules! impl_bounds { | |||
|             fn to_tokens(&self, tokens: &mut TokenStream) { | ||||
|                 match self { | ||||
|                     $(Self::$Variant(v) => v.to_tokens(tokens),)* | ||||
|                     $(Self::$Unknown(v) => v.to_tokens(tokens),)? | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         impl $enum_type { | ||||
|             $vis fn parse_path(path: Path) -> Result<Self, Path> { | ||||
|                 #![allow(unreachable_code)] | ||||
|                 $(let path = match known_items::$Variant::parse_path(path) { | ||||
|                     Ok(v) => return Ok(Self::$Variant(v)), | ||||
|                     Err(path) => path, | ||||
|                 };)* | ||||
|                 $(return Ok(Self::$Unknown(syn::TraitBound { | ||||
|                     paren_token: None, | ||||
|                     modifier: syn::TraitBoundModifier::None, | ||||
|                     lifetimes: None, | ||||
|                     path, | ||||
|                 }.into()));)? | ||||
|                 Err(path) | ||||
|             } | ||||
|             $vis fn parse_type_param_bound(mut type_param_bound: syn::TypeParamBound) -> Result<Self, syn::TypeParamBound> { | ||||
|                 #![allow(unreachable_code)] | ||||
|                 if let syn::TypeParamBound::Trait(mut trait_bound) = type_param_bound { | ||||
|                     if let syn::TraitBound { | ||||
|                         paren_token: _, | ||||
|                         modifier: syn::TraitBoundModifier::None, | ||||
|                         lifetimes: None, | ||||
|                         path: _, | ||||
|                     } = trait_bound { | ||||
|                         match Self::parse_path(trait_bound.path) { | ||||
|                             Ok(retval) => return Ok(retval), | ||||
|                             Err(path) => trait_bound.path = path, | ||||
|                         } | ||||
|                     } | ||||
|                     type_param_bound = trait_bound.into(); | ||||
|                 } | ||||
|                 $(return Ok(Self::$Unknown(type_param_bound));)? | ||||
|                 Err(type_param_bound) | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         impl Parse for $enum_type { | ||||
|             fn parse(input: ParseStream) -> syn::Result<Self> { | ||||
|                 Self::parse_path(Path::parse_mod_style(input)?).map_err(|path| { | ||||
|                     syn::Error::new_spanned( | ||||
|                         path, | ||||
|                 Self::parse_type_param_bound(input.parse()?) | ||||
|                     .map_err(|type_param_bound| syn::Error::new_spanned( | ||||
|                         type_param_bound, | ||||
|                         format_args!("expected one of: {}", [$(stringify!($Variant)),*].join(", ")), | ||||
|                     ) | ||||
|                 }) | ||||
|                     )) | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|  | @ -2115,6 +2147,7 @@ macro_rules! impl_bounds { | |||
|         #[allow(non_snake_case)] | ||||
|         $vis struct $struct_type { | ||||
|             $($vis $Variant: Option<known_items::$Variant>,)* | ||||
|             $($vis $Unknown: Vec<syn::TypeParamBound>,)? | ||||
|         } | ||||
| 
 | ||||
|         impl ToTokens for $struct_type { | ||||
|  | @ -2126,42 +2159,63 @@ macro_rules! impl_bounds { | |||
|                     separator = Some(<Token![+]>::default()); | ||||
|                     v.to_tokens(tokens); | ||||
|                 })* | ||||
|                 $(for v in &self.$Unknown { | ||||
|                     separator.to_tokens(tokens); | ||||
|                     separator = Some(<Token![+]>::default()); | ||||
|                     v.to_tokens(tokens); | ||||
|                 })* | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         const _: () = { | ||||
|             #[derive(Clone, Debug)] | ||||
|             $vis struct Iter($vis $struct_type); | ||||
|             #[allow(non_snake_case)] | ||||
|             $vis struct Iter { | ||||
|                 $($Variant: Option<known_items::$Variant>,)* | ||||
|                 $($Unknown: std::vec::IntoIter<syn::TypeParamBound>,)? | ||||
|             } | ||||
| 
 | ||||
|             impl IntoIterator for $struct_type { | ||||
|                 type Item = $enum_type; | ||||
|                 type IntoIter = Iter; | ||||
| 
 | ||||
|                 fn into_iter(self) -> Self::IntoIter { | ||||
|                     Iter(self) | ||||
|                     Iter { | ||||
|                         $($Variant: self.$Variant,)* | ||||
|                         $($Unknown: self.$Unknown.into_iter(),)? | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             impl Iterator for Iter { | ||||
|                 type Item = $enum_type; | ||||
| 
 | ||||
| 
 | ||||
|                 fn next(&mut self) -> Option<Self::Item> { | ||||
|                     $( | ||||
|                         if let Some(value) = self.0.$Variant.take() { | ||||
|                         if let Some(value) = self.$Variant.take() { | ||||
|                             return Some($enum_type::$Variant(value)); | ||||
|                         } | ||||
|                     )* | ||||
|                     $( | ||||
|                         if let Some(value) = self.$Unknown.next() { | ||||
|                             return Some($enum_type::$Unknown(value)); | ||||
|                         } | ||||
|                     )? | ||||
|                     None | ||||
|                 } | ||||
| 
 | ||||
|                 #[allow(unused_mut, unused_variables)] | ||||
|                 fn fold<B, F: FnMut(B, Self::Item) -> B>(mut self, mut init: B, mut f: F) -> B { | ||||
|                     $( | ||||
|                         if let Some(value) = self.0.$Variant.take() { | ||||
|                         if let Some(value) = self.$Variant.take() { | ||||
|                             init = f(init, $enum_type::$Variant(value)); | ||||
|                         } | ||||
|                     )* | ||||
|                     $( | ||||
|                         if let Some(value) = self.$Unknown.next() { | ||||
|                             init = f(init, $enum_type::$Unknown(value)); | ||||
|                         } | ||||
|                     )? | ||||
|                     init | ||||
|                 } | ||||
|             } | ||||
|  | @ -2173,6 +2227,9 @@ macro_rules! impl_bounds { | |||
|                     $($enum_type::$Variant(v) => { | ||||
|                         self.$Variant = Some(v); | ||||
|                     })* | ||||
|                     $($enum_type::$Unknown(v) => { | ||||
|                         self.$Unknown.push(v); | ||||
|                     })? | ||||
|                 }); | ||||
|             } | ||||
|         } | ||||
|  | @ -2191,6 +2248,7 @@ macro_rules! impl_bounds { | |||
|                     $(if let Some(v) = v.$Variant { | ||||
|                         self.$Variant = Some(v); | ||||
|                     })* | ||||
|                     $(self.$Unknown.extend(v.$Unknown);)* | ||||
|                 }); | ||||
|             } | ||||
|         } | ||||
|  | @ -2244,6 +2302,8 @@ impl_bounds! { | |||
|         Size, | ||||
|         StaticType, | ||||
|         Type, | ||||
|         #[unknown] | ||||
|         Unknown, | ||||
|     } | ||||
| } | ||||
| 
 | ||||
|  | @ -2257,6 +2317,8 @@ impl_bounds! { | |||
|         ResetType, | ||||
|         StaticType, | ||||
|         Type, | ||||
|         #[unknown] | ||||
|         Unknown, | ||||
|     } | ||||
| } | ||||
| 
 | ||||
|  | @ -2270,6 +2332,7 @@ impl From<ParsedTypeBound> for ParsedBound { | |||
|             ParsedTypeBound::ResetType(v) => ParsedBound::ResetType(v), | ||||
|             ParsedTypeBound::StaticType(v) => ParsedBound::StaticType(v), | ||||
|             ParsedTypeBound::Type(v) => ParsedBound::Type(v), | ||||
|             ParsedTypeBound::Unknown(v) => ParsedBound::Unknown(v), | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | @ -2284,6 +2347,7 @@ impl From<ParsedTypeBounds> for ParsedBounds { | |||
|             ResetType, | ||||
|             StaticType, | ||||
|             Type, | ||||
|             Unknown, | ||||
|         } = value; | ||||
|         Self { | ||||
|             BoolOrIntType, | ||||
|  | @ -2295,6 +2359,7 @@ impl From<ParsedTypeBounds> for ParsedBounds { | |||
|             Size: None, | ||||
|             StaticType, | ||||
|             Type, | ||||
|             Unknown, | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | @ -2330,6 +2395,7 @@ impl ParsedTypeBound { | |||
|                 ParsedTypeBound::Type(known_items::Type(span)), | ||||
|             ]), | ||||
|             Self::Type(v) => ParsedTypeBounds::from_iter([ParsedTypeBound::from(v)]), | ||||
|             Self::Unknown(v) => ParsedTypeBounds::from_iter([ParsedTypeBound::Unknown(v)]), | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | @ -2364,6 +2430,7 @@ impl From<ParsedSizeTypeBounds> for ParsedBounds { | |||
|             Size, | ||||
|             StaticType: None, | ||||
|             Type: None, | ||||
|             Unknown: vec![], | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | @ -2391,6 +2458,7 @@ impl ParsedBounds { | |||
|     fn categorize(self, errors: &mut Errors, span: Span) -> ParsedBoundsCategory { | ||||
|         let mut type_bounds = None; | ||||
|         let mut size_type_bounds = None; | ||||
|         let mut unknown_bounds = vec![]; | ||||
|         self.into_iter().for_each(|bound| match bound.categorize() { | ||||
|             ParsedBoundCategory::Type(bound) => { | ||||
|                 type_bounds | ||||
|  | @ -2402,15 +2470,37 @@ impl ParsedBounds { | |||
|                     .get_or_insert_with(ParsedSizeTypeBounds::default) | ||||
|                     .extend([bound]); | ||||
|             } | ||||
|             ParsedBoundCategory::Unknown(bound) => unknown_bounds.push(bound), | ||||
|         }); | ||||
|         match (type_bounds, size_type_bounds) { | ||||
|             (None, None) => ParsedBoundsCategory::Type(ParsedTypeBounds { | ||||
|         match (type_bounds, size_type_bounds, unknown_bounds.is_empty()) { | ||||
|             (None, None, true) => ParsedBoundsCategory::Type(ParsedTypeBounds { | ||||
|                 Type: Some(known_items::Type(span)), | ||||
|                 ..Default::default() | ||||
|             }), | ||||
|             (None, Some(bounds)) => ParsedBoundsCategory::SizeType(bounds), | ||||
|             (Some(bounds), None) => ParsedBoundsCategory::Type(bounds), | ||||
|             (Some(type_bounds), Some(size_type_bounds)) => { | ||||
|             (None, None, false) => { | ||||
|                 errors.error( | ||||
|                     unknown_bounds.remove(0), | ||||
|                     "unknown bounds: must use at least one known bound (such as `Type`) with any unknown bounds", | ||||
|                 ); | ||||
|                 ParsedBoundsCategory::Type(ParsedTypeBounds { | ||||
|                     Unknown: unknown_bounds, | ||||
|                     ..Default::default() | ||||
|                 }) | ||||
|             } | ||||
|             (None, Some(bounds), true) => ParsedBoundsCategory::SizeType(bounds), | ||||
|             (None, Some(bounds), false) => { | ||||
|                 // TODO: implement
 | ||||
|                 errors.error( | ||||
|                     unknown_bounds.remove(0), | ||||
|                     "unknown bounds with `Size` bounds are not implemented", | ||||
|                 ); | ||||
|                 ParsedBoundsCategory::SizeType(bounds) | ||||
|             } | ||||
|             (Some(bounds), None, _) => ParsedBoundsCategory::Type(ParsedTypeBounds { | ||||
|                 Unknown: unknown_bounds, | ||||
|                 ..bounds | ||||
|             }), | ||||
|             (Some(type_bounds), Some(size_type_bounds), _) => { | ||||
|                 errors.error( | ||||
|                     size_type_bounds | ||||
|                         .Size | ||||
|  | @ -2427,6 +2517,7 @@ impl ParsedBounds { | |||
| pub(crate) enum ParsedBoundCategory { | ||||
|     Type(ParsedTypeBound), | ||||
|     SizeType(ParsedSizeTypeBound), | ||||
|     Unknown(syn::TypeParamBound), | ||||
| } | ||||
| 
 | ||||
| impl ParsedBound { | ||||
|  | @ -2441,12 +2532,14 @@ impl ParsedBound { | |||
|             Self::Size(v) => ParsedBoundCategory::SizeType(ParsedSizeTypeBound::Size(v)), | ||||
|             Self::StaticType(v) => ParsedBoundCategory::Type(ParsedTypeBound::StaticType(v)), | ||||
|             Self::Type(v) => ParsedBoundCategory::Type(ParsedTypeBound::Type(v)), | ||||
|             Self::Unknown(v) => ParsedBoundCategory::Unknown(v), | ||||
|         } | ||||
|     } | ||||
|     fn implied_bounds(self) -> ParsedBounds { | ||||
|         match self.categorize() { | ||||
|             ParsedBoundCategory::Type(v) => v.implied_bounds().into(), | ||||
|             ParsedBoundCategory::SizeType(v) => v.implied_bounds().into(), | ||||
|             ParsedBoundCategory::Unknown(v) => ParsedBounds::from_iter([ParsedBound::Unknown(v)]), | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | @ -3325,7 +3418,7 @@ impl ParsedGenerics { | |||
|                                 | ParsedTypeBound::EnumType(_) | ||||
|                                 | ParsedTypeBound::IntType(_) | ||||
|                                 | ParsedTypeBound::ResetType(_) => { | ||||
|                                     errors.error(bound, "bound on mask type not implemented"); | ||||
|                                     errors.error(bound, "bounds on mask types are not implemented"); | ||||
|                                 } | ||||
|                                 ParsedTypeBound::StaticType(bound) => { | ||||
|                                     if bounds.StaticType.is_none() { | ||||
|  | @ -3337,6 +3430,12 @@ impl ParsedGenerics { | |||
|                                     } | ||||
|                                 } | ||||
|                                 ParsedTypeBound::Type(_) => {} | ||||
|                                 ParsedTypeBound::Unknown(_) => { | ||||
|                                     errors.error( | ||||
|                                         bound, | ||||
|                                         "unknown bounds on mask types are not implemented", | ||||
|                                     ); | ||||
|                                 } | ||||
|                             } | ||||
|                         } | ||||
|                         bounds.add_implied_bounds(); | ||||
|  |  | |||
|  | @ -72,13 +72,14 @@ mod kw { | |||
|     custom_keyword!(cfg); | ||||
|     custom_keyword!(cfg_attr); | ||||
|     custom_keyword!(clock_domain); | ||||
|     custom_keyword!(cmp_eq); | ||||
|     custom_keyword!(connect_inexact); | ||||
|     custom_keyword!(custom_bounds); | ||||
|     custom_keyword!(flip); | ||||
|     custom_keyword!(hdl); | ||||
|     custom_keyword!(hdl_module); | ||||
|     custom_keyword!(input); | ||||
|     custom_keyword!(incomplete_wire); | ||||
|     custom_keyword!(input); | ||||
|     custom_keyword!(instance); | ||||
|     custom_keyword!(m); | ||||
|     custom_keyword!(memory); | ||||
|  | @ -92,6 +93,7 @@ mod kw { | |||
|     custom_keyword!(output); | ||||
|     custom_keyword!(reg_builder); | ||||
|     custom_keyword!(reset); | ||||
|     custom_keyword!(sim); | ||||
|     custom_keyword!(skip); | ||||
|     custom_keyword!(target); | ||||
|     custom_keyword!(wire); | ||||
|  |  | |||
|  | @ -377,7 +377,7 @@ impl ModuleFn { | |||
|             module_kind, | ||||
|             vis, | ||||
|             sig, | ||||
|             block, | ||||
|             mut block, | ||||
|             struct_generics, | ||||
|             the_struct, | ||||
|         } = match self.0 { | ||||
|  | @ -439,6 +439,12 @@ impl ModuleFn { | |||
|         body_sig | ||||
|             .inputs | ||||
|             .insert(0, parse_quote! { m: &::fayalite::module::ModuleBuilder }); | ||||
|         block.stmts.insert( | ||||
|             0, | ||||
|             parse_quote! { | ||||
|                 let _ = m; | ||||
|             }, | ||||
|         ); | ||||
|         let body_fn = ItemFn { | ||||
|             attrs: vec![], | ||||
|             vis: Visibility::Inherited, | ||||
|  |  | |||
|  | @ -16,7 +16,7 @@ use std::{borrow::Borrow, convert::Infallible}; | |||
| use syn::{ | ||||
|     fold::{fold_expr, fold_expr_lit, fold_expr_unary, fold_local, fold_stmt, Fold}, | ||||
|     parenthesized, | ||||
|     parse::{Nothing, Parse, ParseStream}, | ||||
|     parse::{Parse, ParseStream}, | ||||
|     parse_quote, parse_quote_spanned, | ||||
|     spanned::Spanned, | ||||
|     token::Paren, | ||||
|  | @ -27,6 +27,13 @@ use syn::{ | |||
| mod expand_aggregate_literals; | ||||
| mod expand_match; | ||||
| 
 | ||||
| options! { | ||||
|     #[options = ExprOptions] | ||||
|     pub(crate) enum ExprOption { | ||||
|         Sim(sim), | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| options! { | ||||
|     pub(crate) enum LetFnKind { | ||||
|         Input(input), | ||||
|  | @ -952,7 +959,7 @@ with_debug_clone_and_fold! { | |||
|     #[allow(dead_code)] | ||||
|     pub(crate) struct HdlLet<Kind = HdlLetKind> { | ||||
|         pub(crate) attrs: Vec<Attribute>, | ||||
|         pub(crate) hdl_attr: HdlAttr<Nothing, kw::hdl>, | ||||
|         pub(crate) hdl_attr: HdlAttr<syn::parse::Nothing, kw::hdl>, | ||||
|         pub(crate) let_token: Token![let], | ||||
|         pub(crate) mut_token: Option<Token![mut]>, | ||||
|         pub(crate) name: Ident, | ||||
|  | @ -1173,7 +1180,7 @@ impl Visitor<'_> { | |||
|             Some(_) => {} | ||||
|         } | ||||
|     } | ||||
|     fn process_hdl_if(&mut self, hdl_attr: HdlAttr<Nothing, kw::hdl>, expr_if: ExprIf) -> Expr { | ||||
|     fn process_hdl_if(&mut self, hdl_attr: HdlAttr<ExprOptions, kw::hdl>, expr_if: ExprIf) -> Expr { | ||||
|         let ExprIf { | ||||
|             attrs, | ||||
|             if_token, | ||||
|  | @ -1181,10 +1188,10 @@ impl Visitor<'_> { | |||
|             then_branch, | ||||
|             else_branch, | ||||
|         } = expr_if; | ||||
|         self.require_normal_module_or_fn(if_token); | ||||
|         let else_expr = else_branch.unzip().1.map(|else_expr| match *else_expr { | ||||
|             Expr::If(expr_if) => self.process_hdl_if(hdl_attr.clone(), expr_if), | ||||
|             expr => expr, | ||||
|         let (else_token, else_expr) = else_branch.unzip(); | ||||
|         let else_expr = else_expr.map(|else_expr| match *else_expr { | ||||
|             Expr::If(expr_if) => Box::new(self.process_hdl_if(hdl_attr.clone(), expr_if)), | ||||
|             _ => else_expr, | ||||
|         }); | ||||
|         if let Expr::Let(ExprLet { | ||||
|             attrs: let_attrs, | ||||
|  | @ -1206,7 +1213,19 @@ impl Visitor<'_> { | |||
|                 }, | ||||
|             ); | ||||
|         } | ||||
|         if let Some(else_expr) = else_expr { | ||||
|         let ExprOptions { sim } = hdl_attr.body; | ||||
|         if sim.is_some() { | ||||
|             ExprIf { | ||||
|                 attrs, | ||||
|                 if_token, | ||||
|                 cond: parse_quote_spanned! {if_token.span=> | ||||
|                     *::fayalite::sim::value::SimValue::<::fayalite::int::Bool>::value(&::fayalite::sim::value::ToSimValue::into_sim_value(#cond)) | ||||
|                 }, | ||||
|                 then_branch, | ||||
|                 else_branch: else_token.zip(else_expr), | ||||
|             } | ||||
|             .into() | ||||
|         } else if let Some(else_expr) = else_expr { | ||||
|             parse_quote_spanned! {if_token.span=> | ||||
|                 #(#attrs)* | ||||
|                 { | ||||
|  | @ -1668,6 +1687,8 @@ impl Fold for Visitor<'_> { | |||
|                 Repeat => process_hdl_repeat, | ||||
|                 Struct => process_hdl_struct, | ||||
|                 Tuple => process_hdl_tuple, | ||||
|                 MethodCall => process_hdl_method_call, | ||||
|                 Call => process_hdl_call, | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | @ -1675,7 +1696,7 @@ impl Fold for Visitor<'_> { | |||
|     fn fold_local(&mut self, mut let_stmt: Local) -> Local { | ||||
|         match self | ||||
|             .errors | ||||
|             .ok(HdlAttr::<Nothing, kw::hdl>::parse_and_leave_attr( | ||||
|             .ok(HdlAttr::<ExprOptions, kw::hdl>::parse_and_leave_attr( | ||||
|                 &let_stmt.attrs, | ||||
|             )) { | ||||
|             None => return empty_let(), | ||||
|  | @ -1694,7 +1715,8 @@ impl Fold for Visitor<'_> { | |||
|             subpat: None, | ||||
|         }) = pat | ||||
|         else { | ||||
|             let hdl_attr = HdlAttr::<Nothing, kw::hdl>::parse_and_take_attr(&mut let_stmt.attrs) | ||||
|             let hdl_attr = | ||||
|                 HdlAttr::<ExprOptions, kw::hdl>::parse_and_take_attr(&mut let_stmt.attrs) | ||||
|                     .ok() | ||||
|                     .flatten() | ||||
|                     .expect("already checked above"); | ||||
|  |  | |||
|  | @ -1,45 +1,103 @@ | |||
| // SPDX-License-Identifier: LGPL-3.0-or-later
 | ||||
| // See Notices.txt for copyright information
 | ||||
| use crate::{kw, module::transform_body::Visitor, HdlAttr}; | ||||
| 
 | ||||
| use crate::{ | ||||
|     kw, | ||||
|     module::transform_body::{ | ||||
|         expand_match::{parse_enum_path, EnumPath}, | ||||
|         ExprOptions, Visitor, | ||||
|     }, | ||||
|     HdlAttr, | ||||
| }; | ||||
| use quote::{format_ident, quote_spanned}; | ||||
| use std::mem; | ||||
| use syn::{ | ||||
|     parse::Nothing, parse_quote, parse_quote_spanned, spanned::Spanned, Expr, ExprArray, ExprPath, | ||||
|     ExprRepeat, ExprStruct, ExprTuple, FieldValue, TypePath, | ||||
|     parse_quote_spanned, punctuated::Punctuated, spanned::Spanned, token::Paren, Expr, ExprArray, | ||||
|     ExprCall, ExprGroup, ExprMethodCall, ExprParen, ExprPath, ExprRepeat, ExprStruct, ExprTuple, | ||||
|     FieldValue, Token, TypePath, | ||||
| }; | ||||
| 
 | ||||
| impl Visitor<'_> { | ||||
|     pub(crate) fn process_hdl_array( | ||||
|         &mut self, | ||||
|         hdl_attr: HdlAttr<Nothing, kw::hdl>, | ||||
|         hdl_attr: HdlAttr<ExprOptions, kw::hdl>, | ||||
|         mut expr_array: ExprArray, | ||||
|     ) -> Expr { | ||||
|         self.require_normal_module_or_fn(hdl_attr); | ||||
|         let ExprOptions { sim } = hdl_attr.body; | ||||
|         let span = hdl_attr.kw.span; | ||||
|         if sim.is_some() { | ||||
|             for elem in &mut expr_array.elems { | ||||
|                 *elem = parse_quote_spanned! {elem.span()=> | ||||
|                     ::fayalite::sim::value::ToSimValue::to_sim_value(&(#elem)) | ||||
|                 }; | ||||
|             } | ||||
|             parse_quote_spanned! {span=> | ||||
|                 ::fayalite::sim::value::ToSimValue::into_sim_value(#expr_array) | ||||
|             } | ||||
|         } else { | ||||
|             for elem in &mut expr_array.elems { | ||||
|                 *elem = parse_quote_spanned! {elem.span()=> | ||||
|                     ::fayalite::expr::ToExpr::to_expr(&(#elem)) | ||||
|                 }; | ||||
|             } | ||||
|         parse_quote! {::fayalite::expr::ToExpr::to_expr(&#expr_array)} | ||||
|             parse_quote_spanned! {span=> | ||||
|                 ::fayalite::expr::ToExpr::to_expr(&#expr_array) | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|     pub(crate) fn process_hdl_repeat( | ||||
|         &mut self, | ||||
|         hdl_attr: HdlAttr<Nothing, kw::hdl>, | ||||
|         hdl_attr: HdlAttr<ExprOptions, kw::hdl>, | ||||
|         mut expr_repeat: ExprRepeat, | ||||
|     ) -> Expr { | ||||
|         self.require_normal_module_or_fn(hdl_attr); | ||||
|         let repeated_value = &expr_repeat.expr; | ||||
|         let ExprOptions { sim } = hdl_attr.body; | ||||
|         let span = hdl_attr.kw.span; | ||||
|         if sim.is_some() { | ||||
|             *expr_repeat.expr = parse_quote_spanned! {repeated_value.span()=> | ||||
|                 ::fayalite::sim::value::ToSimValue::to_sim_value(&(#repeated_value)) | ||||
|             }; | ||||
|             parse_quote_spanned! {span=> | ||||
|                 ::fayalite::sim::value::ToSimValue::into_sim_value(#expr_repeat) | ||||
|             } | ||||
|         } else { | ||||
|             *expr_repeat.expr = parse_quote_spanned! {repeated_value.span()=> | ||||
|                 ::fayalite::expr::ToExpr::to_expr(&(#repeated_value)) | ||||
|             }; | ||||
|         parse_quote! {::fayalite::expr::ToExpr::to_expr(&#expr_repeat)} | ||||
|             parse_quote_spanned! {span=> | ||||
|                 ::fayalite::expr::ToExpr::to_expr(&#expr_repeat) | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|     pub(crate) fn process_hdl_struct( | ||||
|         &mut self, | ||||
|         hdl_attr: HdlAttr<Nothing, kw::hdl>, | ||||
|         expr_struct: ExprStruct, | ||||
|         hdl_attr: HdlAttr<ExprOptions, kw::hdl>, | ||||
|         mut expr_struct: ExprStruct, | ||||
|     ) -> Expr { | ||||
|         self.require_normal_module_or_fn(&hdl_attr); | ||||
|         let name_span = expr_struct.path.segments.last().unwrap().ident.span(); | ||||
|         let ExprOptions { sim } = hdl_attr.body; | ||||
|         if sim.is_some() { | ||||
|             let ty_path = TypePath { | ||||
|                 qself: expr_struct.qself.take(), | ||||
|                 path: expr_struct.path, | ||||
|             }; | ||||
|             expr_struct.path = parse_quote_spanned! {name_span=> | ||||
|                 __SimValue::<#ty_path> | ||||
|             }; | ||||
|             for field in &mut expr_struct.fields { | ||||
|                 let expr = &field.expr; | ||||
|                 field.expr = parse_quote_spanned! {field.member.span()=> | ||||
|                     ::fayalite::sim::value::ToSimValue::to_sim_value(&(#expr)) | ||||
|                 }; | ||||
|             } | ||||
|             return parse_quote_spanned! {name_span=> | ||||
|                 { | ||||
|                     type __SimValue<T> = <T as ::fayalite::ty::Type>::SimValue; | ||||
|                     let value: ::fayalite::sim::value::SimValue<#ty_path> = ::fayalite::sim::value::ToSimValue::into_sim_value(#expr_struct); | ||||
|                     value | ||||
|                 } | ||||
|             }; | ||||
|         } | ||||
|         let builder_ident = format_ident!("__builder", span = name_span); | ||||
|         let empty_builder = if expr_struct.qself.is_some() | ||||
|             || expr_struct | ||||
|  | @ -91,12 +149,126 @@ impl Visitor<'_> { | |||
|     } | ||||
|     pub(crate) fn process_hdl_tuple( | ||||
|         &mut self, | ||||
|         hdl_attr: HdlAttr<Nothing, kw::hdl>, | ||||
|         expr_tuple: ExprTuple, | ||||
|         hdl_attr: HdlAttr<ExprOptions, kw::hdl>, | ||||
|         mut expr_tuple: ExprTuple, | ||||
|     ) -> Expr { | ||||
|         self.require_normal_module_or_fn(hdl_attr); | ||||
|         let ExprOptions { sim } = hdl_attr.body; | ||||
|         if sim.is_some() { | ||||
|             for element in &mut expr_tuple.elems { | ||||
|                 *element = parse_quote_spanned! {element.span()=> | ||||
|                     &(#element) | ||||
|                 }; | ||||
|             } | ||||
|             parse_quote_spanned! {expr_tuple.span()=> | ||||
|                 ::fayalite::sim::value::ToSimValue::into_sim_value(#expr_tuple) | ||||
|             } | ||||
|         } else { | ||||
|             parse_quote_spanned! {expr_tuple.span()=> | ||||
|                 ::fayalite::expr::ToExpr::to_expr(&#expr_tuple) | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|     pub(crate) fn process_hdl_call( | ||||
|         &mut self, | ||||
|         hdl_attr: HdlAttr<ExprOptions, kw::hdl>, | ||||
|         mut expr_call: ExprCall, | ||||
|     ) -> Expr { | ||||
|         let span = hdl_attr.kw.span; | ||||
|         let mut func = &mut *expr_call.func; | ||||
|         let EnumPath { | ||||
|             variant_path: _, | ||||
|             enum_path, | ||||
|             variant_name, | ||||
|         } = loop { | ||||
|             match func { | ||||
|                 Expr::Group(ExprGroup { expr, .. }) | Expr::Paren(ExprParen { expr, .. }) => { | ||||
|                     func = &mut **expr; | ||||
|                 } | ||||
|                 Expr::Path(_) => { | ||||
|                     let Expr::Path(ExprPath { attrs, qself, path }) = | ||||
|                         mem::replace(func, Expr::PLACEHOLDER) | ||||
|                     else { | ||||
|                         unreachable!(); | ||||
|                     }; | ||||
|                     match parse_enum_path(TypePath { qself, path }) { | ||||
|                         Ok(path) => break path, | ||||
|                         Err(path) => { | ||||
|                             self.errors.error(&path, "unsupported enum variant path"); | ||||
|                             let TypePath { qself, path } = path; | ||||
|                             *func = ExprPath { attrs, qself, path }.into(); | ||||
|                             return expr_call.into(); | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|                 _ => { | ||||
|                     self.errors.error( | ||||
|                         &expr_call.func, | ||||
|                         "#[hdl] function call -- function must be a possibly-parenthesized path", | ||||
|                     ); | ||||
|                     return expr_call.into(); | ||||
|                 } | ||||
|             } | ||||
|         }; | ||||
|         self.process_hdl_method_call( | ||||
|             hdl_attr, | ||||
|             ExprMethodCall { | ||||
|                 attrs: expr_call.attrs, | ||||
|                 receiver: parse_quote_spanned! {span=> | ||||
|                     <#enum_path as ::fayalite::ty::StaticType>::TYPE | ||||
|                 }, | ||||
|                 dot_token: Token, | ||||
|                 method: variant_name, | ||||
|                 turbofish: None, | ||||
|                 paren_token: expr_call.paren_token, | ||||
|                 args: expr_call.args, | ||||
|             }, | ||||
|         ) | ||||
|     } | ||||
|     pub(crate) fn process_hdl_method_call( | ||||
|         &mut self, | ||||
|         hdl_attr: HdlAttr<ExprOptions, kw::hdl>, | ||||
|         mut expr_method_call: ExprMethodCall, | ||||
|     ) -> Expr { | ||||
|         let ExprOptions { sim } = hdl_attr.body; | ||||
|         let span = hdl_attr.kw.span; | ||||
|         // remove any number of groups and up to one paren
 | ||||
|         let mut receiver = &mut *expr_method_call.receiver; | ||||
|         let mut has_group = false; | ||||
|         let receiver = loop { | ||||
|             match receiver { | ||||
|                 Expr::Group(ExprGroup { expr, .. }) => { | ||||
|                     has_group = true; | ||||
|                     receiver = expr; | ||||
|                 } | ||||
|                 Expr::Paren(ExprParen { expr, .. }) => break &mut **expr, | ||||
|                 receiver @ Expr::Path(_) => break receiver, | ||||
|                 _ => { | ||||
|                     if !has_group { | ||||
|                         self.errors.error( | ||||
|                             &expr_method_call.receiver, | ||||
|                             "#[hdl] on a method call needs parenthesized receiver", | ||||
|                         ); | ||||
|                     } | ||||
|                     break &mut *expr_method_call.receiver; | ||||
|                 } | ||||
|             } | ||||
|         }; | ||||
|         let func = if sim.is_some() { | ||||
|             parse_quote_spanned! {span=> | ||||
|                 ::fayalite::enum_::enum_type_to_sim_builder | ||||
|             } | ||||
|         } else { | ||||
|             parse_quote_spanned! {span=> | ||||
|                 ::fayalite::enum_::assert_is_enum_type | ||||
|             } | ||||
|         }; | ||||
|         *expr_method_call.receiver = ExprCall { | ||||
|             attrs: vec![], | ||||
|             func, | ||||
|             paren_token: Paren(span), | ||||
|             args: Punctuated::from_iter([mem::replace(receiver, Expr::PLACEHOLDER)]), | ||||
|         } | ||||
|         .into(); | ||||
|         expr_method_call.into() | ||||
|     } | ||||
| } | ||||
|  |  | |||
|  | @ -3,7 +3,9 @@ | |||
| use crate::{ | ||||
|     fold::{impl_fold, DoFold}, | ||||
|     kw, | ||||
|     module::transform_body::{empty_let, with_debug_clone_and_fold, wrap_ty_with_expr, Visitor}, | ||||
|     module::transform_body::{ | ||||
|         empty_let, with_debug_clone_and_fold, wrap_ty_with_expr, ExprOptions, Visitor, | ||||
|     }, | ||||
|     Errors, HdlAttr, PairsIterExt, | ||||
| }; | ||||
| use proc_macro2::{Span, TokenStream}; | ||||
|  | @ -11,7 +13,6 @@ use quote::{format_ident, quote_spanned, ToTokens, TokenStreamExt}; | |||
| use std::collections::BTreeSet; | ||||
| use syn::{ | ||||
|     fold::{fold_arm, fold_expr_match, fold_local, fold_pat, Fold}, | ||||
|     parse::Nothing, | ||||
|     parse_quote_spanned, | ||||
|     punctuated::Punctuated, | ||||
|     spanned::Spanned, | ||||
|  | @ -82,7 +83,14 @@ visit_trait! { | |||
|         } | ||||
|     } | ||||
|     fn visit_match_pat_enum_variant(state: _, v: &MatchPatEnumVariant) { | ||||
|         let MatchPatEnumVariant {match_span:_, variant_path: _, enum_path: _, variant_name: _, field } = v; | ||||
|         let MatchPatEnumVariant { | ||||
|             match_span:_, | ||||
|             sim:_, | ||||
|             variant_path: _, | ||||
|             enum_path: _, | ||||
|             variant_name: _, | ||||
|             field, | ||||
|         } = v; | ||||
|         if let Some((_, v)) = field { | ||||
|             state.visit_match_pat_simple(v); | ||||
|         } | ||||
|  | @ -292,6 +300,7 @@ impl ToTokens for MatchPatTuple { | |||
| with_debug_clone_and_fold! { | ||||
|     struct MatchPatEnumVariant<> { | ||||
|         match_span: Span, | ||||
|         sim: Option<(kw::sim,)>, | ||||
|         variant_path: Path, | ||||
|         enum_path: Path, | ||||
|         variant_name: Ident, | ||||
|  | @ -303,6 +312,7 @@ impl ToTokens for MatchPatEnumVariant { | |||
|     fn to_tokens(&self, tokens: &mut TokenStream) { | ||||
|         let Self { | ||||
|             match_span, | ||||
|             sim, | ||||
|             variant_path: _, | ||||
|             enum_path, | ||||
|             variant_name, | ||||
|  | @ -312,7 +322,28 @@ impl ToTokens for MatchPatEnumVariant { | |||
|             __MatchTy::<#enum_path>::#variant_name | ||||
|         } | ||||
|         .to_tokens(tokens); | ||||
|         if sim.is_some() { | ||||
|             if let Some((paren_token, field)) = field { | ||||
|                 paren_token.surround(tokens, |tokens| { | ||||
|                     field.to_tokens(tokens); | ||||
|                     match field { | ||||
|                         MatchPatSimple::Paren(_) | ||||
|                         | MatchPatSimple::Or(_) | ||||
|                         | MatchPatSimple::Binding(_) | ||||
|                         | MatchPatSimple::Wild(_) => quote_spanned! {*match_span=> | ||||
|                             , _ | ||||
|                         } | ||||
|                         .to_tokens(tokens), | ||||
|                         MatchPatSimple::Rest(_) => {} | ||||
|                     } | ||||
|                 }); | ||||
|             } else { | ||||
|                 quote_spanned! {*match_span=> | ||||
|                     (_) | ||||
|                 } | ||||
|                 .to_tokens(tokens); | ||||
|             } | ||||
|         } else if let Some((paren_token, field)) = field { | ||||
|             paren_token.surround(tokens, |tokens| field.to_tokens(tokens)); | ||||
|         } | ||||
|     } | ||||
|  | @ -349,13 +380,13 @@ impl ToTokens for MatchPatSimple { | |||
|     } | ||||
| } | ||||
| 
 | ||||
| struct EnumPath { | ||||
|     variant_path: Path, | ||||
|     enum_path: Path, | ||||
|     variant_name: Ident, | ||||
| pub(crate) struct EnumPath { | ||||
|     pub(crate) variant_path: Path, | ||||
|     pub(crate) enum_path: Path, | ||||
|     pub(crate) variant_name: Ident, | ||||
| } | ||||
| 
 | ||||
| fn parse_enum_path(variant_path: TypePath) -> Result<EnumPath, TypePath> { | ||||
| pub(crate) fn parse_enum_path(variant_path: TypePath) -> Result<EnumPath, TypePath> { | ||||
|     let TypePath { | ||||
|         qself: None, | ||||
|         path: variant_path, | ||||
|  | @ -447,6 +478,7 @@ trait ParseMatchPat: Sized { | |||
|                         state, | ||||
|                         MatchPatEnumVariant { | ||||
|                             match_span: state.match_span, | ||||
|                             sim: state.sim, | ||||
|                             variant_path, | ||||
|                             enum_path, | ||||
|                             variant_name, | ||||
|  | @ -493,6 +525,7 @@ trait ParseMatchPat: Sized { | |||
|                     state, | ||||
|                     MatchPatEnumVariant { | ||||
|                         match_span: state.match_span, | ||||
|                         sim: state.sim, | ||||
|                         variant_path, | ||||
|                         enum_path, | ||||
|                         variant_name, | ||||
|  | @ -577,6 +610,7 @@ trait ParseMatchPat: Sized { | |||
|                     state, | ||||
|                     MatchPatEnumVariant { | ||||
|                         match_span: state.match_span, | ||||
|                         sim: state.sim, | ||||
|                         variant_path, | ||||
|                         enum_path, | ||||
|                         variant_name, | ||||
|  | @ -939,6 +973,7 @@ impl Fold for RewriteAsCheckMatch { | |||
| } | ||||
| 
 | ||||
| struct HdlMatchParseState<'a> { | ||||
|     sim: Option<(kw::sim,)>, | ||||
|     match_span: Span, | ||||
|     errors: &'a mut Errors, | ||||
| } | ||||
|  | @ -981,10 +1016,11 @@ impl<'a> VisitMatchPat<'a> for HdlLetPatVisitState<'a> { | |||
| impl Visitor<'_> { | ||||
|     pub(crate) fn process_hdl_let_pat( | ||||
|         &mut self, | ||||
|         _hdl_attr: HdlAttr<Nothing, kw::hdl>, | ||||
|         hdl_attr: HdlAttr<ExprOptions, kw::hdl>, | ||||
|         mut let_stmt: Local, | ||||
|     ) -> Local { | ||||
|         let span = let_stmt.let_token.span(); | ||||
|         let ExprOptions { sim } = hdl_attr.body; | ||||
|         if let Pat::Type(pat) = &mut let_stmt.pat { | ||||
|             *pat.ty = wrap_ty_with_expr((*pat.ty).clone()); | ||||
|         } | ||||
|  | @ -996,7 +1032,6 @@ impl Visitor<'_> { | |||
|             init, | ||||
|             semi_token, | ||||
|         } = let_stmt; | ||||
|         self.require_normal_module_or_fn(let_token); | ||||
|         let Some(syn::LocalInit { | ||||
|             eq_token, | ||||
|             expr, | ||||
|  | @ -1015,6 +1050,7 @@ impl Visitor<'_> { | |||
|         } | ||||
|         let Ok(pat) = MatchPat::parse( | ||||
|             &mut HdlMatchParseState { | ||||
|                 sim, | ||||
|                 match_span: span, | ||||
|                 errors: &mut self.errors, | ||||
|             }, | ||||
|  | @ -1031,20 +1067,37 @@ impl Visitor<'_> { | |||
|             errors: _, | ||||
|             bindings, | ||||
|         } = state; | ||||
|         let retval = parse_quote_spanned! {span=> | ||||
|         let retval = if sim.is_some() { | ||||
|             parse_quote_spanned! {span=> | ||||
|                 let (#(#bindings,)*) = { | ||||
|                     type __MatchTy<T> = <T as ::fayalite::ty::Type>::SimValue; | ||||
|                     let __match_value = ::fayalite::sim::value::ToSimValue::to_sim_value(&(#expr)); | ||||
|                     #let_token #pat #eq_token ::fayalite::sim::value::SimValue::into_value(__match_value) #semi_token | ||||
|                     (#(#bindings,)*) | ||||
|                 }; | ||||
|             } | ||||
|         } else { | ||||
|             parse_quote_spanned! {span=> | ||||
|                 let (#(#bindings,)* __scope,) = { | ||||
|                     type __MatchTy<T> = <T as ::fayalite::ty::Type>::MatchVariant; | ||||
|                     let __match_expr = ::fayalite::expr::ToExpr::to_expr(&(#expr)); | ||||
|                 ::fayalite::expr::check_match_expr(__match_expr, |__match_value, __infallible| { | ||||
|                     ::fayalite::expr::check_match_expr( | ||||
|                         __match_expr, | ||||
|                         |__match_value, __infallible| { | ||||
|                             #[allow(unused_variables)] | ||||
|                             #check_let_stmt | ||||
|                             match __infallible {} | ||||
|                 }); | ||||
|                         }, | ||||
|                     ); | ||||
|                     let mut __match_iter = ::fayalite::module::match_(__match_expr); | ||||
|                 let ::fayalite::__std::option::Option::Some(__match_variant) = ::fayalite::__std::iter::Iterator::next(&mut __match_iter) else { | ||||
|                     let ::fayalite::__std::option::Option::Some(__match_variant) = | ||||
|                         ::fayalite::__std::iter::Iterator::next(&mut __match_iter) | ||||
|                     else { | ||||
|                         ::fayalite::__std::unreachable!("#[hdl] let with uninhabited type"); | ||||
|                     }; | ||||
|                 let ::fayalite::__std::option::Option::None = ::fayalite::__std::iter::Iterator::next(&mut __match_iter) else { | ||||
|                     let ::fayalite::__std::option::Option::None = | ||||
|                         ::fayalite::__std::iter::Iterator::next(&mut __match_iter) | ||||
|                     else { | ||||
|                         ::fayalite::__std::unreachable!("#[hdl] let with refutable pattern"); | ||||
|                     }; | ||||
|                     let (__match_variant, __scope) = | ||||
|  | @ -1054,6 +1107,7 @@ impl Visitor<'_> { | |||
|                     #let_token #pat #eq_token __match_variant #semi_token | ||||
|                     (#(#bindings,)* __scope,) | ||||
|                 }; | ||||
|             } | ||||
|         }; | ||||
|         match retval { | ||||
|             syn::Stmt::Local(retval) => retval, | ||||
|  | @ -1062,7 +1116,7 @@ impl Visitor<'_> { | |||
|     } | ||||
|     pub(crate) fn process_hdl_match( | ||||
|         &mut self, | ||||
|         _hdl_attr: HdlAttr<Nothing, kw::hdl>, | ||||
|         hdl_attr: HdlAttr<ExprOptions, kw::hdl>, | ||||
|         expr_match: ExprMatch, | ||||
|     ) -> Expr { | ||||
|         let span = expr_match.match_token.span(); | ||||
|  | @ -1074,8 +1128,9 @@ impl Visitor<'_> { | |||
|             brace_token: _, | ||||
|             arms, | ||||
|         } = expr_match; | ||||
|         self.require_normal_module_or_fn(match_token); | ||||
|         let ExprOptions { sim } = hdl_attr.body; | ||||
|         let mut state = HdlMatchParseState { | ||||
|             sim, | ||||
|             match_span: span, | ||||
|             errors: &mut self.errors, | ||||
|         }; | ||||
|  | @ -1083,7 +1138,18 @@ impl Visitor<'_> { | |||
|             arms.into_iter() | ||||
|                 .filter_map(|arm| MatchArm::parse(&mut state, arm).ok()), | ||||
|         ); | ||||
|         let expr = quote_spanned! {span=> | ||||
|         let expr = if sim.is_some() { | ||||
|             quote_spanned! {span=> | ||||
|                 { | ||||
|                     type __MatchTy<T> = <T as ::fayalite::ty::Type>::SimValue; | ||||
|                     let __match_expr = ::fayalite::sim::value::ToSimValue::to_sim_value(&(#expr)); | ||||
|                     #match_token ::fayalite::sim::value::SimValue::into_value(__match_expr) { | ||||
|                         #(#arms)* | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } else { | ||||
|             quote_spanned! {span=> | ||||
|                 { | ||||
|                     type __MatchTy<T> = <T as ::fayalite::ty::Type>::MatchVariant; | ||||
|                     let __match_expr = ::fayalite::expr::ToExpr::to_expr(&(#expr)); | ||||
|  | @ -1101,6 +1167,7 @@ impl Visitor<'_> { | |||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         }; | ||||
|         syn::parse2(expr).unwrap() | ||||
|     } | ||||
|  |  | |||
|  | @ -40,6 +40,7 @@ fayalite-visit-gen.workspace = true | |||
| 
 | ||||
| [features] | ||||
| unstable-doc = [] | ||||
| unstable-test-hasher = [] | ||||
| 
 | ||||
| [package.metadata.docs.rs] | ||||
| features = ["unstable-doc"] | ||||
|  |  | |||
|  | @ -12,7 +12,7 @@ use std::{ | |||
|     ops::Deref, | ||||
| }; | ||||
| 
 | ||||
| #[derive(Clone)] | ||||
| #[derive(Clone, Debug)] | ||||
| struct CustomFirrtlAnnotationFieldsImpl { | ||||
|     value: serde_json::Map<String, serde_json::Value>, | ||||
|     serialized: Interned<str>, | ||||
|  |  | |||
|  | @ -2,17 +2,24 @@ | |||
| // See Notices.txt for copyright information
 | ||||
| 
 | ||||
| use crate::{ | ||||
|     expr::{ops::ArrayIndex, Expr, ToExpr}, | ||||
|     int::{DynSize, KnownSize, Size, SizeType, DYN_SIZE}, | ||||
|     expr::{ | ||||
|         ops::{ArrayLiteral, ExprFromIterator, ExprIntoIterator, ExprPartialEq}, | ||||
|         CastToBits, Expr, HdlPartialEq, ReduceBits, ToExpr, | ||||
|     }, | ||||
|     int::{Bool, DynSize, KnownSize, Size, SizeType, DYN_SIZE}, | ||||
|     intern::{Intern, Interned, LazyInterned}, | ||||
|     module::transform::visit::{Fold, Folder, Visit, Visitor}, | ||||
|     sim::value::{SimValue, SimValuePartialEq}, | ||||
|     source_location::SourceLocation, | ||||
|     ty::{ | ||||
|         CanonicalType, MatchVariantWithoutScope, StaticType, Type, TypeProperties, TypeWithDeref, | ||||
|         serde_impls::SerdeCanonicalType, CanonicalType, MatchVariantWithoutScope, StaticType, Type, | ||||
|         TypeProperties, TypeWithDeref, | ||||
|     }, | ||||
|     util::ConstUsize, | ||||
| }; | ||||
| use std::ops::Index; | ||||
| use bitvec::slice::BitSlice; | ||||
| use serde::{de::Error, Deserialize, Deserializer, Serialize, Serializer}; | ||||
| use std::{iter::FusedIterator, ops::Index}; | ||||
| 
 | ||||
| #[derive(Copy, Clone, PartialEq, Eq, Hash)] | ||||
| pub struct ArrayType<T: Type = CanonicalType, Len: Size = DynSize> { | ||||
|  | @ -91,6 +98,12 @@ impl<T: Type, Len: KnownSize + Size<SizeType = Len>> ArrayType<T, Len> { | |||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<T: StaticType, Len: KnownSize> Default for ArrayType<T, Len> { | ||||
|     fn default() -> Self { | ||||
|         Self::TYPE | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<T: StaticType, Len: KnownSize> StaticType for ArrayType<T, Len> { | ||||
|     const TYPE: Self = Self { | ||||
|         element: LazyInterned::new_lazy(&|| T::TYPE.intern_sized()), | ||||
|  | @ -139,6 +152,7 @@ impl<T: Type + Visit<State>, Len: Size, State: Visitor + ?Sized> Visit<State> | |||
| impl<T: Type, Len: Size> Type for ArrayType<T, Len> { | ||||
|     type BaseType = Array; | ||||
|     type MaskType = ArrayType<T::MaskType, Len>; | ||||
|     type SimValue = Len::ArraySimValue<T>; | ||||
|     type MatchVariant = Len::ArrayMatch<T>; | ||||
|     type MatchActiveScope = (); | ||||
|     type MatchVariantAndInactiveScope = MatchVariantWithoutScope<Len::ArrayMatch<T>>; | ||||
|  | @ -148,10 +162,8 @@ impl<T: Type, Len: Size> Type for ArrayType<T, Len> { | |||
|         this: Expr<Self>, | ||||
|         source_location: SourceLocation, | ||||
|     ) -> Self::MatchVariantsIter { | ||||
|         let base = Expr::as_dyn_array(this); | ||||
|         let base_ty = Expr::ty(base); | ||||
|         let _ = source_location; | ||||
|         let retval = Vec::from_iter((0..base_ty.len()).map(|i| ArrayIndex::new(base, i).to_expr())); | ||||
|         let retval = Vec::from_iter(this); | ||||
|         std::iter::once(MatchVariantWithoutScope( | ||||
|             Len::ArrayMatch::<T>::try_from(retval) | ||||
|                 .ok() | ||||
|  | @ -177,16 +189,97 @@ impl<T: Type, Len: Size> Type for ArrayType<T, Len> { | |||
|             Len::from_usize(array.len()), | ||||
|         ) | ||||
|     } | ||||
| 
 | ||||
|     fn source_location() -> SourceLocation { | ||||
|         SourceLocation::builtin() | ||||
|     } | ||||
| 
 | ||||
|     fn sim_value_from_bits(&self, bits: &BitSlice) -> Self::SimValue { | ||||
|         assert_eq!(bits.len(), self.type_properties.bit_width); | ||||
|         let element = self.element(); | ||||
|         let element_bit_width = element.canonical().bit_width(); | ||||
|         TryFrom::try_from(Vec::from_iter((0..self.len()).map(|i| { | ||||
|             SimValue::from_bitslice(element, &bits[i * element_bit_width..][..element_bit_width]) | ||||
|         }))) | ||||
|         .ok() | ||||
|         .expect("used correct length") | ||||
|     } | ||||
| 
 | ||||
|     fn sim_value_clone_from_bits(&self, value: &mut Self::SimValue, bits: &BitSlice) { | ||||
|         assert_eq!(bits.len(), self.type_properties.bit_width); | ||||
|         let element_ty = self.element(); | ||||
|         let element_bit_width = element_ty.canonical().bit_width(); | ||||
|         let value: &mut [SimValue<T>] = value.as_mut(); | ||||
|         assert_eq!(self.len(), value.len()); | ||||
|         for (i, element_value) in value.iter_mut().enumerate() { | ||||
|             assert_eq!(SimValue::ty(element_value), element_ty); | ||||
|             SimValue::bits_mut(element_value) | ||||
|                 .bits_mut() | ||||
|                 .copy_from_bitslice(&bits[i * element_bit_width..][..element_bit_width]); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     fn sim_value_to_bits(&self, value: &Self::SimValue, bits: &mut BitSlice) { | ||||
|         assert_eq!(bits.len(), self.type_properties.bit_width); | ||||
|         let element_ty = self.element(); | ||||
|         let element_bit_width = element_ty.canonical().bit_width(); | ||||
|         let value: &[SimValue<T>] = value.as_ref(); | ||||
|         assert_eq!(self.len(), value.len()); | ||||
|         for (i, element_value) in value.iter().enumerate() { | ||||
|             assert_eq!(SimValue::ty(element_value), element_ty); | ||||
|             bits[i * element_bit_width..][..element_bit_width] | ||||
|                 .copy_from_bitslice(SimValue::bits(element_value).bits()); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<T: Type + Serialize, Len: Size> Serialize for ArrayType<T, Len> { | ||||
|     fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> | ||||
|     where | ||||
|         S: Serializer, | ||||
|     { | ||||
|         SerdeCanonicalType::<T>::Array { | ||||
|             element: self.element(), | ||||
|             len: self.len(), | ||||
|         } | ||||
|         .serialize(serializer) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<'de, T: Type + Deserialize<'de>, Len: Size> Deserialize<'de> for ArrayType<T, Len> { | ||||
|     fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> | ||||
|     where | ||||
|         D: Deserializer<'de>, | ||||
|     { | ||||
|         let name = |len| -> String { | ||||
|             if let Some(len) = len { | ||||
|                 format!("an Array<_, {len}>") | ||||
|             } else { | ||||
|                 "an Array<_>".to_string() | ||||
|             } | ||||
|         }; | ||||
|         match SerdeCanonicalType::<T>::deserialize(deserializer)? { | ||||
|             SerdeCanonicalType::Array { element, len } => { | ||||
|                 if let Some(len) = Len::try_from_usize(len) { | ||||
|                     Ok(Self::new(element, len)) | ||||
|                 } else { | ||||
|                     Err(Error::invalid_value( | ||||
|                         serde::de::Unexpected::Other(&name(Some(len))), | ||||
|                         &&*name(Len::KNOWN_VALUE), | ||||
|                     )) | ||||
|                 } | ||||
|             } | ||||
|             ty => Err(Error::invalid_value( | ||||
|                 serde::de::Unexpected::Other(ty.as_serde_unexpected_str()), | ||||
|                 &&*name(Len::KNOWN_VALUE), | ||||
|             )), | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<T: Type, Len: Size> TypeWithDeref for ArrayType<T, Len> { | ||||
|     fn expr_deref(this: &Expr<Self>) -> &Self::MatchVariant { | ||||
|         let base = Expr::as_dyn_array(*this); | ||||
|         let base_ty = Expr::ty(base); | ||||
|         let retval = Vec::from_iter((0..base_ty.len()).map(|i| ArrayIndex::new(base, i).to_expr())); | ||||
|         let retval = Vec::from_iter(*this); | ||||
|         Interned::into_inner(Intern::intern_sized( | ||||
|             Len::ArrayMatch::<T>::try_from(retval) | ||||
|                 .ok() | ||||
|  | @ -218,3 +311,143 @@ impl<T: Type, L: SizeType> Index<L> for ArrayWithoutLen<T> { | |||
|         Interned::into_inner(Intern::intern_sized(ArrayType::new(self.element, len))) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<Lhs: Type, Rhs: Type, Len: Size> ExprPartialEq<ArrayType<Rhs, Len>> for ArrayType<Lhs, Len> | ||||
| where | ||||
|     Lhs: ExprPartialEq<Rhs>, | ||||
| { | ||||
|     fn cmp_eq(lhs: Expr<Self>, rhs: Expr<ArrayType<Rhs, Len>>) -> Expr<Bool> { | ||||
|         let lhs_ty = Expr::ty(lhs); | ||||
|         let rhs_ty = Expr::ty(rhs); | ||||
|         assert_eq!(lhs_ty.len(), rhs_ty.len()); | ||||
|         lhs.into_iter() | ||||
|             .zip(rhs) | ||||
|             .map(|(l, r)| l.cmp_eq(r)) | ||||
|             .collect::<Expr<Array<Bool>>>() | ||||
|             .cast_to_bits() | ||||
|             .all_one_bits() | ||||
|     } | ||||
| 
 | ||||
|     fn cmp_ne(lhs: Expr<Self>, rhs: Expr<ArrayType<Rhs, Len>>) -> Expr<Bool> { | ||||
|         let lhs_ty = Expr::ty(lhs); | ||||
|         let rhs_ty = Expr::ty(rhs); | ||||
|         assert_eq!(lhs_ty.len(), rhs_ty.len()); | ||||
|         lhs.into_iter() | ||||
|             .zip(rhs) | ||||
|             .map(|(l, r)| l.cmp_ne(r)) | ||||
|             .collect::<Expr<Array<Bool>>>() | ||||
|             .cast_to_bits() | ||||
|             .any_one_bits() | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<Lhs: Type, Rhs: Type, Len: Size> SimValuePartialEq<ArrayType<Rhs, Len>> for ArrayType<Lhs, Len> | ||||
| where | ||||
|     Lhs: SimValuePartialEq<Rhs>, | ||||
| { | ||||
|     fn sim_value_eq(this: &SimValue<Self>, other: &SimValue<ArrayType<Rhs, Len>>) -> bool { | ||||
|         AsRef::<[_]>::as_ref(&**this) | ||||
|             .iter() | ||||
|             .zip(AsRef::<[_]>::as_ref(&**other)) | ||||
|             .all(|(l, r)| SimValuePartialEq::sim_value_eq(l, r)) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<T: Type, Len: Size> ExprIntoIterator for ArrayType<T, Len> { | ||||
|     type Item = T; | ||||
|     type ExprIntoIter = ExprArrayIter<T, Len>; | ||||
| 
 | ||||
|     fn expr_into_iter(e: Expr<Self>) -> Self::ExprIntoIter { | ||||
|         ExprArrayIter { | ||||
|             base: e, | ||||
|             indexes: 0..Expr::ty(e).len(), | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[derive(Clone, Debug)] | ||||
| pub struct ExprArrayIter<T: Type, Len: Size> { | ||||
|     base: Expr<ArrayType<T, Len>>, | ||||
|     indexes: std::ops::Range<usize>, | ||||
| } | ||||
| 
 | ||||
| impl<T: Type, Len: Size> ExprArrayIter<T, Len> { | ||||
|     pub fn base(&self) -> Expr<ArrayType<T, Len>> { | ||||
|         self.base | ||||
|     } | ||||
|     pub fn indexes(&self) -> std::ops::Range<usize> { | ||||
|         self.indexes.clone() | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<T: Type, Len: Size> Iterator for ExprArrayIter<T, Len> { | ||||
|     type Item = Expr<T>; | ||||
| 
 | ||||
|     fn next(&mut self) -> Option<Self::Item> { | ||||
|         self.indexes.next().map(|i| self.base[i]) | ||||
|     } | ||||
| 
 | ||||
|     fn size_hint(&self) -> (usize, Option<usize>) { | ||||
|         self.indexes.size_hint() | ||||
|     } | ||||
| 
 | ||||
|     fn count(self) -> usize { | ||||
|         self.indexes.count() | ||||
|     } | ||||
| 
 | ||||
|     fn last(mut self) -> Option<Self::Item> { | ||||
|         self.next_back() | ||||
|     } | ||||
| 
 | ||||
|     fn nth(&mut self, n: usize) -> Option<Self::Item> { | ||||
|         self.indexes.nth(n).map(|i| self.base[i]) | ||||
|     } | ||||
| 
 | ||||
|     fn fold<B, F>(self, init: B, mut f: F) -> B | ||||
|     where | ||||
|         F: FnMut(B, Self::Item) -> B, | ||||
|     { | ||||
|         self.indexes.fold(init, |b, i| f(b, self.base[i])) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<T: Type, Len: Size> DoubleEndedIterator for ExprArrayIter<T, Len> { | ||||
|     fn next_back(&mut self) -> Option<Self::Item> { | ||||
|         self.indexes.next_back().map(|i| self.base[i]) | ||||
|     } | ||||
| 
 | ||||
|     fn nth_back(&mut self, n: usize) -> Option<Self::Item> { | ||||
|         self.indexes.nth_back(n).map(|i| self.base[i]) | ||||
|     } | ||||
| 
 | ||||
|     fn rfold<B, F>(self, init: B, mut f: F) -> B | ||||
|     where | ||||
|         F: FnMut(B, Self::Item) -> B, | ||||
|     { | ||||
|         self.indexes.rfold(init, |b, i| f(b, self.base[i])) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<T: Type, Len: Size> ExactSizeIterator for ExprArrayIter<T, Len> { | ||||
|     fn len(&self) -> usize { | ||||
|         self.indexes.len() | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<T: Type, Len: Size> FusedIterator for ExprArrayIter<T, Len> {} | ||||
| 
 | ||||
| impl<A: StaticType> ExprFromIterator<Expr<A>> for Array<A> { | ||||
|     fn expr_from_iter<T: IntoIterator<Item = Expr<A>>>(iter: T) -> Expr<Self> { | ||||
|         ArrayLiteral::new( | ||||
|             A::TYPE, | ||||
|             iter.into_iter().map(|v| Expr::canonical(v)).collect(), | ||||
|         ) | ||||
|         .to_expr() | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<'a, A: StaticType> ExprFromIterator<&'a Expr<A>> for Array<A> { | ||||
|     fn expr_from_iter<T: IntoIterator<Item = &'a Expr<A>>>(iter: T) -> Expr<Self> { | ||||
|         iter.into_iter().copied().collect() | ||||
|     } | ||||
| } | ||||
|  |  | |||
|  | @ -2,20 +2,25 @@ | |||
| // See Notices.txt for copyright information
 | ||||
| 
 | ||||
| use crate::{ | ||||
|     expr::{ops::BundleLiteral, Expr, ToExpr}, | ||||
|     expr::{ | ||||
|         ops::{ArrayLiteral, BundleLiteral, ExprPartialEq}, | ||||
|         CastToBits, Expr, ReduceBits, ToExpr, | ||||
|     }, | ||||
|     int::{Bool, DynSize}, | ||||
|     intern::{Intern, Interned}, | ||||
|     sim::{SimValue, ToSimValue}, | ||||
|     sim::value::{SimValue, SimValuePartialEq, ToSimValue, ToSimValueWithType}, | ||||
|     source_location::SourceLocation, | ||||
|     ty::{ | ||||
|         impl_match_variant_as_self, CanonicalType, MatchVariantWithoutScope, StaticType, Type, | ||||
|         TypeProperties, TypeWithDeref, | ||||
|         impl_match_variant_as_self, CanonicalType, MatchVariantWithoutScope, OpaqueSimValue, | ||||
|         StaticType, Type, TypeProperties, TypeWithDeref, | ||||
|     }, | ||||
|     util::HashMap, | ||||
| }; | ||||
| use bitvec::vec::BitVec; | ||||
| use hashbrown::HashMap; | ||||
| use bitvec::{slice::BitSlice, vec::BitVec}; | ||||
| use serde::{Deserialize, Serialize}; | ||||
| use std::{fmt, marker::PhantomData}; | ||||
| 
 | ||||
| #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)] | ||||
| #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, Serialize, Deserialize)] | ||||
| pub struct BundleField { | ||||
|     pub name: Interned<str>, | ||||
|     pub flipped: bool, | ||||
|  | @ -155,7 +160,7 @@ impl Default for BundleTypePropertiesBuilder { | |||
| impl Bundle { | ||||
|     #[track_caller] | ||||
|     pub fn new(fields: Interned<[BundleField]>) -> Self { | ||||
|         let mut name_indexes = HashMap::with_capacity(fields.len()); | ||||
|         let mut name_indexes = HashMap::with_capacity_and_hasher(fields.len(), Default::default()); | ||||
|         let mut field_offsets = Vec::with_capacity(fields.len()); | ||||
|         let mut type_props_builder = BundleTypePropertiesBuilder::new(); | ||||
|         for (index, &BundleField { name, flipped, ty }) in fields.iter().enumerate() { | ||||
|  | @ -212,6 +217,7 @@ impl Bundle { | |||
| impl Type for Bundle { | ||||
|     type BaseType = Bundle; | ||||
|     type MaskType = Bundle; | ||||
|     type SimValue = OpaqueSimValue; | ||||
|     impl_match_variant_as_self!(); | ||||
|     fn mask_type(&self) -> Self::MaskType { | ||||
|         Self::new(Interned::from_iter(self.0.fields.into_iter().map( | ||||
|  | @ -235,6 +241,20 @@ impl Type for Bundle { | |||
|     fn source_location() -> SourceLocation { | ||||
|         SourceLocation::builtin() | ||||
|     } | ||||
|     fn sim_value_from_bits(&self, bits: &BitSlice) -> Self::SimValue { | ||||
|         assert_eq!(bits.len(), self.type_properties().bit_width); | ||||
|         OpaqueSimValue::from_bitslice(bits) | ||||
|     } | ||||
|     fn sim_value_clone_from_bits(&self, value: &mut Self::SimValue, bits: &BitSlice) { | ||||
|         assert_eq!(bits.len(), self.type_properties().bit_width); | ||||
|         assert_eq!(value.bit_width(), self.type_properties().bit_width); | ||||
|         value.bits_mut().bits_mut().copy_from_bitslice(bits); | ||||
|     } | ||||
|     fn sim_value_to_bits(&self, value: &Self::SimValue, bits: &mut BitSlice) { | ||||
|         assert_eq!(bits.len(), self.type_properties().bit_width); | ||||
|         assert_eq!(value.bit_width(), self.type_properties().bit_width); | ||||
|         bits.copy_from_bitslice(value.bits().bits()); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| pub trait BundleType: Type<BaseType = Bundle> { | ||||
|  | @ -243,6 +263,93 @@ pub trait BundleType: Type<BaseType = Bundle> { | |||
|     fn fields(&self) -> Interned<[BundleField]>; | ||||
| } | ||||
| 
 | ||||
| pub struct BundleSimValueFromBits<'a> { | ||||
|     fields: std::slice::Iter<'static, BundleField>, | ||||
|     bits: &'a BitSlice, | ||||
| } | ||||
| 
 | ||||
| impl<'a> BundleSimValueFromBits<'a> { | ||||
|     #[track_caller] | ||||
|     pub fn new<T: BundleType>(bundle_ty: T, bits: &'a BitSlice) -> Self { | ||||
|         let fields = bundle_ty.fields(); | ||||
|         assert_eq!( | ||||
|             bits.len(), | ||||
|             fields | ||||
|                 .iter() | ||||
|                 .map(|BundleField { ty, .. }| ty.bit_width()) | ||||
|                 .sum::<usize>() | ||||
|         ); | ||||
|         Self { | ||||
|             fields: Interned::into_inner(fields).iter(), | ||||
|             bits, | ||||
|         } | ||||
|     } | ||||
|     #[track_caller] | ||||
|     fn field_ty_and_bits<T: Type>(&mut self) -> (T, &'a BitSlice) { | ||||
|         let Some(&BundleField { | ||||
|             name: _, | ||||
|             flipped: _, | ||||
|             ty, | ||||
|         }) = self.fields.next() | ||||
|         else { | ||||
|             panic!("tried to read too many fields from BundleSimValueFromBits"); | ||||
|         }; | ||||
|         let (field_bits, rest) = self.bits.split_at(ty.bit_width()); | ||||
|         self.bits = rest; | ||||
|         (T::from_canonical(ty), field_bits) | ||||
|     } | ||||
|     #[track_caller] | ||||
|     pub fn field_from_bits<T: Type>(&mut self) -> SimValue<T> { | ||||
|         let (field_ty, field_bits) = self.field_ty_and_bits::<T>(); | ||||
|         SimValue::from_bitslice(field_ty, field_bits) | ||||
|     } | ||||
|     #[track_caller] | ||||
|     pub fn field_clone_from_bits<T: Type>(&mut self, field_value: &mut SimValue<T>) { | ||||
|         let (field_ty, field_bits) = self.field_ty_and_bits::<T>(); | ||||
|         assert_eq!(field_ty, SimValue::ty(field_value)); | ||||
|         SimValue::bits_mut(field_value) | ||||
|             .bits_mut() | ||||
|             .copy_from_bitslice(field_bits); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| pub struct BundleSimValueToBits<'a> { | ||||
|     fields: std::slice::Iter<'static, BundleField>, | ||||
|     bits: &'a mut BitSlice, | ||||
| } | ||||
| 
 | ||||
| impl<'a> BundleSimValueToBits<'a> { | ||||
|     #[track_caller] | ||||
|     pub fn new<T: BundleType>(bundle_ty: T, bits: &'a mut BitSlice) -> Self { | ||||
|         let fields = bundle_ty.fields(); | ||||
|         assert_eq!( | ||||
|             bits.len(), | ||||
|             fields | ||||
|                 .iter() | ||||
|                 .map(|BundleField { ty, .. }| ty.bit_width()) | ||||
|                 .sum::<usize>() | ||||
|         ); | ||||
|         Self { | ||||
|             fields: Interned::into_inner(fields).iter(), | ||||
|             bits, | ||||
|         } | ||||
|     } | ||||
|     #[track_caller] | ||||
|     pub fn field_to_bits<T: Type>(&mut self, field_value: &SimValue<T>) { | ||||
|         let Some(&BundleField { | ||||
|             name: _, | ||||
|             flipped: _, | ||||
|             ty, | ||||
|         }) = self.fields.next() | ||||
|         else { | ||||
|             panic!("tried to read too many fields from BundleSimValueFromBits"); | ||||
|         }; | ||||
|         assert_eq!(T::from_canonical(ty), SimValue::ty(field_value)); | ||||
|         self.bits[..ty.bit_width()].copy_from_bitslice(SimValue::bits(field_value).bits()); | ||||
|         self.bits = &mut std::mem::take(&mut self.bits)[ty.bit_width()..]; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[derive(Default)] | ||||
| pub struct NoBuilder; | ||||
| 
 | ||||
|  | @ -325,7 +432,19 @@ macro_rules! impl_tuple_builder_fields { | |||
| } | ||||
| 
 | ||||
| macro_rules! impl_tuples { | ||||
|     ([$({#[num = $num:literal, field = $field:ident, ty = $ty_var:ident: $Ty:ident] $var:ident: $T:ident})*] []) => { | ||||
|     ( | ||||
|         [$({ | ||||
|             #[ | ||||
|                 num = $num:tt, | ||||
|                 field = $field:ident, | ||||
|                 ty = $ty_var:ident: $Ty:ident, | ||||
|                 lhs = $lhs_var:ident: $Lhs:ident, | ||||
|                 rhs = $rhs_var:ident: $Rhs:ident | ||||
|             ] | ||||
|             $var:ident: $T:ident | ||||
|         })*] | ||||
|         [] | ||||
|     ) => { | ||||
|         impl_tuple_builder_fields! { | ||||
|             {} | ||||
|             [$({ | ||||
|  | @ -337,6 +456,7 @@ macro_rules! impl_tuples { | |||
|         impl<$($T: Type,)*> Type for ($($T,)*) { | ||||
|             type BaseType = Bundle; | ||||
|             type MaskType = ($($T::MaskType,)*); | ||||
|             type SimValue = ($(SimValue<$T>,)*); | ||||
|             type MatchVariant = ($(Expr<$T>,)*); | ||||
|             type MatchActiveScope = (); | ||||
|             type MatchVariantAndInactiveScope = MatchVariantWithoutScope<Self::MatchVariant>; | ||||
|  | @ -375,6 +495,24 @@ macro_rules! impl_tuples { | |||
|             fn source_location() -> SourceLocation { | ||||
|                 SourceLocation::builtin() | ||||
|             } | ||||
|             fn sim_value_from_bits(&self, bits: &BitSlice) -> Self::SimValue { | ||||
|                 #![allow(unused_mut, unused_variables)] | ||||
|                 let mut v = BundleSimValueFromBits::new(*self, bits); | ||||
|                 $(let $var = v.field_from_bits();)* | ||||
|                 ($($var,)*) | ||||
|             } | ||||
|             fn sim_value_clone_from_bits(&self, value: &mut Self::SimValue, bits: &BitSlice) { | ||||
|                 #![allow(unused_mut, unused_variables)] | ||||
|                 let mut v = BundleSimValueFromBits::new(*self, bits); | ||||
|                 let ($($var,)*) = value; | ||||
|                 $(v.field_clone_from_bits($var);)* | ||||
|             } | ||||
|             fn sim_value_to_bits(&self, value: &Self::SimValue, bits: &mut BitSlice) { | ||||
|                 #![allow(unused_mut, unused_variables)] | ||||
|                 let mut v = BundleSimValueToBits::new(*self, bits); | ||||
|                 let ($($var,)*) = value; | ||||
|                 $(v.field_to_bits($var);)* | ||||
|             } | ||||
|         } | ||||
|         impl<$($T: Type,)*> BundleType for ($($T,)*) { | ||||
|             type Builder = TupleBuilder<($(Unfilled<$T>,)*)>; | ||||
|  | @ -425,77 +563,104 @@ macro_rules! impl_tuples { | |||
|                 BundleLiteral::new(ty, field_values[..].intern()).to_expr() | ||||
|             } | ||||
|         } | ||||
|         impl<$($T: ToSimValue<CanonicalType>,)*> ToSimValue<CanonicalType> for ($($T,)*) { | ||||
|         impl<$($T: ToSimValueWithType<CanonicalType>,)*> ToSimValueWithType<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() | ||||
|             fn to_sim_value_with_type(&self, ty: CanonicalType) -> SimValue<CanonicalType> { | ||||
|                 SimValue::into_canonical(ToSimValueWithType::<Bundle>::to_sim_value_with_type(self, Bundle::from_canonical(ty))) | ||||
|             } | ||||
|             #[track_caller] | ||||
|             fn into_sim_value(self, ty: CanonicalType) -> SimValue<CanonicalType> | ||||
|             fn into_sim_value_with_type(self, ty: CanonicalType) -> SimValue<CanonicalType> | ||||
|             { | ||||
|                 ToSimValue::<Bundle>::into_sim_value(self, Bundle::from_canonical(ty)).into_canonical() | ||||
|                 SimValue::into_canonical(ToSimValueWithType::<Bundle>::into_sim_value_with_type(self, Bundle::from_canonical(ty))) | ||||
|             } | ||||
|         } | ||||
|         impl<$($T: ToSimValueWithType<CanonicalType>,)*> ToSimValueWithType<Bundle> for ($($T,)*) { | ||||
|             #[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> { | ||||
|             fn to_sim_value_with_type(&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) | ||||
|                 $(let $var = $var.to_sim_value_with_type($ty_var.ty);)* | ||||
|                 ToSimValueWithType::into_sim_value_with_type(($($var,)*), ty) | ||||
|             } | ||||
|             #[track_caller] | ||||
|             fn into_sim_value(self, ty: Bundle) -> SimValue<Bundle> { | ||||
|             fn into_sim_value_with_type(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); | ||||
|                     } | ||||
|                 } | ||||
|                 let mut bits = BitVec::new(); | ||||
|                 $(let $var = $var.into_sim_value_with_type($ty_var.ty); | ||||
|                 assert_eq!(SimValue::ty(&$var), $ty_var.ty); | ||||
|                 bits.extend_from_bitslice(SimValue::bits(&$var).bits()); | ||||
|                 )* | ||||
|                 bits.unwrap_or_else(BitVec::new).into_sim_value(ty) | ||||
|                 bits.into_sim_value_with_type(ty) | ||||
|             } | ||||
|         } | ||||
|         impl<$($T: ToSimValueWithType<$Ty>, $Ty: Type,)*> ToSimValueWithType<($($Ty,)*)> for ($($T,)*) { | ||||
|             #[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,)*)> { | ||||
|             fn to_sim_value_with_type(&self, ty: ($($Ty,)*)) -> SimValue<($($Ty,)*)> { | ||||
|                 let ($($var,)*) = self; | ||||
|                 let ($($ty_var,)*) = ty; | ||||
|                 $(let $var = $var.to_sim_value($ty_var).into_canonical();)* | ||||
|                 SimValue::from_canonical(ToSimValue::into_sim_value(($($var,)*), ty.canonical())) | ||||
|                 $(let $var = $var.to_sim_value_with_type($ty_var);)* | ||||
|                 SimValue::from_value(ty, ($($var,)*)) | ||||
|             } | ||||
|             #[track_caller] | ||||
|             fn into_sim_value(self, ty: ($($Ty,)*)) -> SimValue<($($Ty,)*)> { | ||||
|             fn into_sim_value_with_type(self, ty: ($($Ty,)*)) -> SimValue<($($Ty,)*)> { | ||||
|                 let ($($var,)*) = self; | ||||
|                 let ($($ty_var,)*) = ty; | ||||
|                 $(let $var = $var.into_sim_value($ty_var).into_canonical();)* | ||||
|                 SimValue::from_canonical(ToSimValue::into_sim_value(($($var,)*), ty.canonical())) | ||||
|                 $(let $var = $var.into_sim_value_with_type($ty_var);)* | ||||
|                 SimValue::from_value(ty, ($($var,)*)) | ||||
|             } | ||||
|         } | ||||
|         impl<$($T: ToSimValue,)*> ToSimValue for ($($T,)*) { | ||||
|             type Type = ($($T::Type,)*); | ||||
|             #[track_caller] | ||||
|             fn to_sim_value(&self) -> SimValue<Self::Type> { | ||||
|                 let ($($var,)*) = self; | ||||
|                 $(let $var = $var.to_sim_value();)* | ||||
|                 SimValue::from_value(($(SimValue::ty(&$var),)*), ($($var,)*)) | ||||
|             } | ||||
|             #[track_caller] | ||||
|             fn box_into_sim_value(self: Box<Self>, ty: ($($Ty,)*)) -> SimValue<($($Ty,)*)> { | ||||
|                 Self::into_sim_value(*self, ty) | ||||
|             fn into_sim_value(self) -> SimValue<Self::Type> { | ||||
|                 let ($($var,)*) = self; | ||||
|                 $(let $var = $var.to_sim_value();)* | ||||
|                 SimValue::from_value(($(SimValue::ty(&$var),)*), ($($var,)*)) | ||||
|             } | ||||
|         } | ||||
|         impl<$($Lhs: Type + ExprPartialEq<$Rhs>, $Rhs: Type,)*> ExprPartialEq<($($Rhs,)*)> for ($($Lhs,)*) { | ||||
|             fn cmp_eq(lhs: Expr<Self>, rhs: Expr<($($Rhs,)*)>) -> Expr<Bool> { | ||||
|                 let ($($lhs_var,)*) = *lhs; | ||||
|                 let ($($rhs_var,)*) = *rhs; | ||||
|                 ArrayLiteral::<Bool, DynSize>::new( | ||||
|                     Bool, | ||||
|                     FromIterator::from_iter([$(Expr::canonical(ExprPartialEq::cmp_eq($lhs_var, $rhs_var)),)*]), | ||||
|                 ) | ||||
|                 .cast_to_bits() | ||||
|                 .all_one_bits() | ||||
|             } | ||||
| 
 | ||||
|             fn cmp_ne(lhs: Expr<Self>, rhs: Expr<($($Rhs,)*)>) -> Expr<Bool> { | ||||
|                 let ($($lhs_var,)*) = *lhs; | ||||
|                 let ($($rhs_var,)*) = *rhs; | ||||
|                 ArrayLiteral::<Bool, DynSize>::new( | ||||
|                     Bool, | ||||
|                     FromIterator::from_iter([$(Expr::canonical(ExprPartialEq::cmp_ne($lhs_var, $rhs_var)),)*]), | ||||
|                 ) | ||||
|                 .cast_to_bits() | ||||
|                 .any_one_bits() | ||||
|             } | ||||
|         } | ||||
|         impl<$($Lhs: SimValuePartialEq<$Rhs>, $Rhs: Type,)*> SimValuePartialEq<($($Rhs,)*)> for ($($Lhs,)*) { | ||||
|             fn sim_value_eq(lhs: &SimValue<Self>, rhs: &SimValue<($($Rhs,)*)>) -> bool { | ||||
|                 let ($($lhs_var,)*) = &**lhs; | ||||
|                 let ($($rhs_var,)*) = &**rhs; | ||||
|                 let retval = true; | ||||
|                 $(let retval = retval && $lhs_var == $rhs_var;)* | ||||
|                 retval | ||||
|             } | ||||
|         } | ||||
|     }; | ||||
|  | @ -507,24 +672,25 @@ macro_rules! impl_tuples { | |||
| 
 | ||||
| impl_tuples! { | ||||
|     [] [ | ||||
|         {#[num = 0, field = field_0, ty = ty0: Ty0] v0: T0} | ||||
|         {#[num = 1, field = field_1, ty = ty1: Ty1] v1: T1} | ||||
|         {#[num = 2, field = field_2, ty = ty2: Ty2] v2: T2} | ||||
|         {#[num = 3, field = field_3, ty = ty3: Ty3] v3: T3} | ||||
|         {#[num = 4, field = field_4, ty = ty4: Ty4] v4: T4} | ||||
|         {#[num = 5, field = field_5, ty = ty5: Ty5] v5: T5} | ||||
|         {#[num = 6, field = field_6, ty = ty6: Ty6] v6: T6} | ||||
|         {#[num = 7, field = field_7, ty = ty7: Ty7] v7: T7} | ||||
|         {#[num = 8, field = field_8, ty = ty8: Ty8] v8: T8} | ||||
|         {#[num = 9, field = field_9, ty = ty9: Ty9] v9: T9} | ||||
|         {#[num = 10, field = field_10, ty = ty10: Ty10] v10: T10} | ||||
|         {#[num = 11, field = field_11, ty = ty11: Ty11] v11: T11} | ||||
|         {#[num = 0, field = field_0, ty = ty0: Ty0, lhs = lhs0: Lhs0, rhs = rhs0: Rhs0] v0: T0} | ||||
|         {#[num = 1, field = field_1, ty = ty1: Ty1, lhs = lhs1: Lhs1, rhs = rhs1: Rhs1] v1: T1} | ||||
|         {#[num = 2, field = field_2, ty = ty2: Ty2, lhs = lhs2: Lhs2, rhs = rhs2: Rhs2] v2: T2} | ||||
|         {#[num = 3, field = field_3, ty = ty3: Ty3, lhs = lhs3: Lhs3, rhs = rhs3: Rhs3] v3: T3} | ||||
|         {#[num = 4, field = field_4, ty = ty4: Ty4, lhs = lhs4: Lhs4, rhs = rhs4: Rhs4] v4: T4} | ||||
|         {#[num = 5, field = field_5, ty = ty5: Ty5, lhs = lhs5: Lhs5, rhs = rhs5: Rhs5] v5: T5} | ||||
|         {#[num = 6, field = field_6, ty = ty6: Ty6, lhs = lhs6: Lhs6, rhs = rhs6: Rhs6] v6: T6} | ||||
|         {#[num = 7, field = field_7, ty = ty7: Ty7, lhs = lhs7: Lhs7, rhs = rhs7: Rhs7] v7: T7} | ||||
|         {#[num = 8, field = field_8, ty = ty8: Ty8, lhs = lhs8: Lhs8, rhs = rhs8: Rhs8] v8: T8} | ||||
|         {#[num = 9, field = field_9, ty = ty9: Ty9, lhs = lhs9: Lhs9, rhs = rhs9: Rhs9] v9: T9} | ||||
|         {#[num = 10, field = field_10, ty = ty10: Ty10, lhs = lhs10: Lhs10, rhs = rhs10: Rhs10] v10: T10} | ||||
|         {#[num = 11, field = field_11, ty = ty11: Ty11, lhs = lhs11: Lhs11, rhs = rhs11: Rhs11] v11: T11} | ||||
|     ] | ||||
| } | ||||
| 
 | ||||
| impl<T: ?Sized + Send + Sync + 'static> Type for PhantomData<T> { | ||||
|     type BaseType = Bundle; | ||||
|     type MaskType = (); | ||||
|     type SimValue = PhantomData<T>; | ||||
|     type MatchVariant = PhantomData<T>; | ||||
|     type MatchActiveScope = (); | ||||
|     type MatchVariantAndInactiveScope = MatchVariantWithoutScope<Self::MatchVariant>; | ||||
|  | @ -557,6 +723,16 @@ impl<T: ?Sized + Send + Sync + 'static> Type for PhantomData<T> { | |||
|     fn source_location() -> SourceLocation { | ||||
|         SourceLocation::builtin() | ||||
|     } | ||||
|     fn sim_value_from_bits(&self, bits: &BitSlice) -> Self::SimValue { | ||||
|         assert!(bits.is_empty()); | ||||
|         *self | ||||
|     } | ||||
|     fn sim_value_clone_from_bits(&self, _value: &mut Self::SimValue, bits: &BitSlice) { | ||||
|         assert!(bits.is_empty()); | ||||
|     } | ||||
|     fn sim_value_to_bits(&self, _value: &Self::SimValue, bits: &mut BitSlice) { | ||||
|         assert!(bits.is_empty()); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| pub struct PhantomDataBuilder<T: ?Sized + Send + Sync + 'static>(PhantomData<T>); | ||||
|  | @ -604,26 +780,38 @@ impl<T: ?Sized + Send + Sync + 'static> ToExpr for PhantomData<T> { | |||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<T: ?Sized + Send + Sync + 'static> ToSimValue<Self> for PhantomData<T> { | ||||
| impl<T: ?Sized + Send + Sync + 'static> ToSimValue for PhantomData<T> { | ||||
|     type Type = PhantomData<T>; | ||||
| 
 | ||||
|     #[track_caller] | ||||
|     fn to_sim_value(&self, ty: Self) -> SimValue<Self> { | ||||
|         ToSimValue::into_sim_value(BitVec::new(), ty) | ||||
|     fn to_sim_value(&self) -> SimValue<Self> { | ||||
|         SimValue::from_value(*self, *self) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<T: ?Sized> ToSimValue<Bundle> for PhantomData<T> { | ||||
| impl<T: ?Sized + Send + Sync + 'static> ToSimValueWithType<Self> for PhantomData<T> { | ||||
|     #[track_caller] | ||||
|     fn to_sim_value(&self, ty: Bundle) -> SimValue<Bundle> { | ||||
|     fn to_sim_value_with_type(&self, ty: Self) -> SimValue<Self> { | ||||
|         SimValue::from_value(ty, *self) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<T: ?Sized> ToSimValueWithType<Bundle> for PhantomData<T> { | ||||
|     #[track_caller] | ||||
|     fn to_sim_value_with_type(&self, ty: Bundle) -> SimValue<Bundle> { | ||||
|         assert!(ty.fields().is_empty()); | ||||
|         ToSimValue::into_sim_value(BitVec::new(), ty) | ||||
|         ToSimValueWithType::into_sim_value_with_type(BitVec::new(), ty) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<T: ?Sized> ToSimValue<CanonicalType> for PhantomData<T> { | ||||
| impl<T: ?Sized> ToSimValueWithType<CanonicalType> for PhantomData<T> { | ||||
|     #[track_caller] | ||||
|     fn to_sim_value(&self, ty: CanonicalType) -> SimValue<CanonicalType> { | ||||
|     fn to_sim_value_with_type(&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() | ||||
|         SimValue::into_canonical(ToSimValueWithType::into_sim_value_with_type( | ||||
|             BitVec::new(), | ||||
|             ty, | ||||
|         )) | ||||
|     } | ||||
| } | ||||
|  |  | |||
|  | @ -258,7 +258,7 @@ pub struct VerilogArgs { | |||
|         default_value = "firtool", | ||||
|         env = "FIRTOOL", | ||||
|         value_hint = ValueHint::CommandName, | ||||
|         value_parser = OsStringValueParser::new().try_map(which::which) | ||||
|         value_parser = OsStringValueParser::new().try_map(which) | ||||
|     )] | ||||
|     pub firtool: PathBuf, | ||||
|     #[arg(long)] | ||||
|  | @ -428,6 +428,13 @@ impl clap::Args for FormalAdjustArgs { | |||
|     } | ||||
| } | ||||
| 
 | ||||
| fn which(v: std::ffi::OsString) -> which::Result<PathBuf> { | ||||
|     #[cfg(not(miri))] | ||||
|     return which::which(v); | ||||
|     #[cfg(miri)] | ||||
|     return Ok(Path::new("/").join(v)); | ||||
| } | ||||
| 
 | ||||
| #[derive(Parser, Clone)] | ||||
| #[non_exhaustive] | ||||
| pub struct FormalArgs { | ||||
|  | @ -438,7 +445,7 @@ pub struct FormalArgs { | |||
|         default_value = "sby", | ||||
|         env = "SBY", | ||||
|         value_hint = ValueHint::CommandName, | ||||
|         value_parser = OsStringValueParser::new().try_map(which::which) | ||||
|         value_parser = OsStringValueParser::new().try_map(which) | ||||
|     )] | ||||
|     pub sby: PathBuf, | ||||
|     #[arg(long)] | ||||
|  |  | |||
|  | @ -8,6 +8,7 @@ use crate::{ | |||
|     source_location::SourceLocation, | ||||
|     ty::{impl_match_variant_as_self, CanonicalType, StaticType, Type, TypeProperties}, | ||||
| }; | ||||
| use bitvec::slice::BitSlice; | ||||
| 
 | ||||
| #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, Default)] | ||||
| pub struct Clock; | ||||
|  | @ -15,6 +16,7 @@ pub struct Clock; | |||
| impl Type for Clock { | ||||
|     type BaseType = Clock; | ||||
|     type MaskType = Bool; | ||||
|     type SimValue = bool; | ||||
| 
 | ||||
|     impl_match_variant_as_self!(); | ||||
| 
 | ||||
|  | @ -36,6 +38,21 @@ impl Type for Clock { | |||
|         }; | ||||
|         retval | ||||
|     } | ||||
| 
 | ||||
|     fn sim_value_from_bits(&self, bits: &BitSlice) -> Self::SimValue { | ||||
|         assert_eq!(bits.len(), 1); | ||||
|         bits[0] | ||||
|     } | ||||
| 
 | ||||
|     fn sim_value_clone_from_bits(&self, value: &mut Self::SimValue, bits: &BitSlice) { | ||||
|         assert_eq!(bits.len(), 1); | ||||
|         *value = bits[0]; | ||||
|     } | ||||
| 
 | ||||
|     fn sim_value_to_bits(&self, value: &Self::SimValue, bits: &mut BitSlice) { | ||||
|         assert_eq!(bits.len(), 1); | ||||
|         bits.set(0, *value); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl Clock { | ||||
|  |  | |||
|  | @ -2,21 +2,30 @@ | |||
| // See Notices.txt for copyright information
 | ||||
| 
 | ||||
| use crate::{ | ||||
|     expr::{ops::VariantAccess, Expr, ToExpr}, | ||||
|     expr::{ | ||||
|         ops::{ExprPartialEq, VariantAccess}, | ||||
|         Expr, ToExpr, | ||||
|     }, | ||||
|     hdl, | ||||
|     int::Bool, | ||||
|     int::{Bool, UIntValue}, | ||||
|     intern::{Intern, Interned}, | ||||
|     module::{ | ||||
|         connect, enum_match_variants_helper, incomplete_wire, wire, | ||||
|         EnumMatchVariantAndInactiveScopeImpl, EnumMatchVariantsIterImpl, Scope, | ||||
|     }, | ||||
|     sim::value::{SimValue, SimValuePartialEq}, | ||||
|     source_location::SourceLocation, | ||||
|     ty::{CanonicalType, MatchVariantAndInactiveScope, StaticType, Type, TypeProperties}, | ||||
|     ty::{ | ||||
|         CanonicalType, MatchVariantAndInactiveScope, OpaqueSimValue, StaticType, Type, | ||||
|         TypeProperties, | ||||
|     }, | ||||
|     util::HashMap, | ||||
| }; | ||||
| use hashbrown::HashMap; | ||||
| use std::{convert::Infallible, fmt, iter::FusedIterator}; | ||||
| use bitvec::{order::Lsb0, slice::BitSlice, view::BitView}; | ||||
| use serde::{Deserialize, Serialize}; | ||||
| use std::{convert::Infallible, fmt, iter::FusedIterator, sync::Arc}; | ||||
| 
 | ||||
| #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] | ||||
| #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, Serialize, Deserialize)] | ||||
| pub struct EnumVariant { | ||||
|     pub name: Interned<str>, | ||||
|     pub ty: Option<CanonicalType>, | ||||
|  | @ -149,6 +158,12 @@ impl EnumTypePropertiesBuilder { | |||
|             variant_count: variant_count + 1, | ||||
|         } | ||||
|     } | ||||
|     #[must_use] | ||||
|     pub fn variants(self, variants: impl IntoIterator<Item = EnumVariant>) -> Self { | ||||
|         variants.into_iter().fold(self, |this, variant| { | ||||
|             this.variant(variant.ty.map(CanonicalType::type_properties)) | ||||
|         }) | ||||
|     } | ||||
|     pub const fn finish(self) -> TypeProperties { | ||||
|         assert!( | ||||
|             self.variant_count != 0, | ||||
|  | @ -178,7 +193,8 @@ impl Default for EnumTypePropertiesBuilder { | |||
| impl Enum { | ||||
|     #[track_caller] | ||||
|     pub fn new(variants: Interned<[EnumVariant]>) -> Self { | ||||
|         let mut name_indexes = HashMap::with_capacity(variants.len()); | ||||
|         let mut name_indexes = | ||||
|             HashMap::with_capacity_and_hasher(variants.len(), Default::default()); | ||||
|         let mut type_props_builder = EnumTypePropertiesBuilder::new(); | ||||
|         for (index, EnumVariant { name, ty }) in variants.iter().enumerate() { | ||||
|             if let Some(old_index) = name_indexes.insert(*name, index) { | ||||
|  | @ -245,6 +261,7 @@ pub trait EnumType: | |||
|     MatchVariantsIter = EnumMatchVariantsIter<Self>, | ||||
| > | ||||
| { | ||||
|     type SimBuilder: From<Self>; | ||||
|     fn variants(&self) -> Interned<[EnumVariant]>; | ||||
|     fn match_activate_scope( | ||||
|         v: Self::MatchVariantAndInactiveScope, | ||||
|  | @ -307,7 +324,18 @@ impl<T: EnumType> DoubleEndedIterator for EnumMatchVariantsIter<T> { | |||
|     } | ||||
| } | ||||
| 
 | ||||
| pub struct NoBuilder { | ||||
|     _ty: Enum, | ||||
| } | ||||
| 
 | ||||
| impl From<Enum> for NoBuilder { | ||||
|     fn from(_ty: Enum) -> Self { | ||||
|         Self { _ty } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl EnumType for Enum { | ||||
|     type SimBuilder = NoBuilder; | ||||
|     fn match_activate_scope( | ||||
|         v: Self::MatchVariantAndInactiveScope, | ||||
|     ) -> (Self::MatchVariant, Self::MatchActiveScope) { | ||||
|  | @ -322,6 +350,7 @@ impl EnumType for Enum { | |||
| impl Type for Enum { | ||||
|     type BaseType = Enum; | ||||
|     type MaskType = Bool; | ||||
|     type SimValue = OpaqueSimValue; | ||||
|     type MatchVariant = Option<Expr<CanonicalType>>; | ||||
|     type MatchActiveScope = Scope; | ||||
|     type MatchVariantAndInactiveScope = EnumMatchVariantAndInactiveScope<Self>; | ||||
|  | @ -352,6 +381,309 @@ impl Type for Enum { | |||
|     fn source_location() -> SourceLocation { | ||||
|         SourceLocation::builtin() | ||||
|     } | ||||
|     fn sim_value_from_bits(&self, bits: &BitSlice) -> Self::SimValue { | ||||
|         assert_eq!(bits.len(), self.type_properties().bit_width); | ||||
|         OpaqueSimValue::from_bitslice(bits) | ||||
|     } | ||||
|     fn sim_value_clone_from_bits(&self, value: &mut Self::SimValue, bits: &BitSlice) { | ||||
|         assert_eq!(bits.len(), self.type_properties().bit_width); | ||||
|         assert_eq!(value.bit_width(), self.type_properties().bit_width); | ||||
|         value.bits_mut().bits_mut().copy_from_bitslice(bits); | ||||
|     } | ||||
|     fn sim_value_to_bits(&self, value: &Self::SimValue, bits: &mut BitSlice) { | ||||
|         assert_eq!(bits.len(), self.type_properties().bit_width); | ||||
|         assert_eq!(value.bit_width(), self.type_properties().bit_width); | ||||
|         bits.copy_from_bitslice(value.bits().bits()); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[derive(Clone, PartialEq, Eq, Hash, Debug, Default)] | ||||
| pub struct EnumPaddingSimValue { | ||||
|     bits: Option<UIntValue>, | ||||
| } | ||||
| 
 | ||||
| impl EnumPaddingSimValue { | ||||
|     pub const fn new() -> Self { | ||||
|         Self { bits: None } | ||||
|     } | ||||
|     pub fn bit_width(&self) -> Option<usize> { | ||||
|         self.bits.as_ref().map(UIntValue::width) | ||||
|     } | ||||
|     pub fn bits(&self) -> &Option<UIntValue> { | ||||
|         &self.bits | ||||
|     } | ||||
|     pub fn bits_mut(&mut self) -> &mut Option<UIntValue> { | ||||
|         &mut self.bits | ||||
|     } | ||||
|     pub fn into_bits(self) -> Option<UIntValue> { | ||||
|         self.bits | ||||
|     } | ||||
|     pub fn from_bits(bits: Option<UIntValue>) -> Self { | ||||
|         Self { bits } | ||||
|     } | ||||
|     pub fn from_bitslice(v: &BitSlice) -> Self { | ||||
|         Self { | ||||
|             bits: Some(UIntValue::new(Arc::new(v.to_bitvec()))), | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[derive(Clone, PartialEq, Eq, Hash, Debug)] | ||||
| pub struct UnknownVariantSimValue { | ||||
|     discriminant: usize, | ||||
|     body_bits: UIntValue, | ||||
| } | ||||
| 
 | ||||
| impl UnknownVariantSimValue { | ||||
|     pub fn discriminant(&self) -> usize { | ||||
|         self.discriminant | ||||
|     } | ||||
|     pub fn body_bits(&self) -> &UIntValue { | ||||
|         &self.body_bits | ||||
|     } | ||||
|     pub fn body_bits_mut(&mut self) -> &mut UIntValue { | ||||
|         &mut self.body_bits | ||||
|     } | ||||
|     pub fn into_body_bits(self) -> UIntValue { | ||||
|         self.body_bits | ||||
|     } | ||||
|     pub fn into_parts(self) -> (usize, UIntValue) { | ||||
|         (self.discriminant, self.body_bits) | ||||
|     } | ||||
|     pub fn new(discriminant: usize, body_bits: UIntValue) -> Self { | ||||
|         Self { | ||||
|             discriminant, | ||||
|             body_bits, | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| pub struct EnumSimValueFromBits<'a> { | ||||
|     variants: Interned<[EnumVariant]>, | ||||
|     discriminant: usize, | ||||
|     body_bits: &'a BitSlice, | ||||
| } | ||||
| 
 | ||||
| impl<'a> EnumSimValueFromBits<'a> { | ||||
|     #[track_caller] | ||||
|     pub fn new<T: EnumType>(ty: T, bits: &'a BitSlice) -> Self { | ||||
|         let variants = ty.variants(); | ||||
|         let bit_width = EnumTypePropertiesBuilder::new() | ||||
|             .variants(variants) | ||||
|             .finish() | ||||
|             .bit_width; | ||||
|         assert_eq!(bit_width, bits.len()); | ||||
|         let (discriminant_bits, body_bits) = | ||||
|             bits.split_at(discriminant_bit_width_impl(variants.len())); | ||||
|         let mut discriminant = 0usize; | ||||
|         discriminant.view_bits_mut::<Lsb0>()[..discriminant_bits.len()] | ||||
|             .copy_from_bitslice(discriminant_bits); | ||||
|         Self { | ||||
|             variants, | ||||
|             discriminant, | ||||
|             body_bits, | ||||
|         } | ||||
|     } | ||||
|     pub fn discriminant(&self) -> usize { | ||||
|         self.discriminant | ||||
|     } | ||||
|     #[track_caller] | ||||
|     #[cold] | ||||
|     fn usage_error(&self, clone: bool) -> ! { | ||||
|         let clone = if clone { "clone_" } else { "" }; | ||||
|         match self.variants.get(self.discriminant) { | ||||
|             None => { | ||||
|                 panic!("should have called EnumSimValueFromBits::unknown_variant_{clone}from_bits"); | ||||
|             } | ||||
|             Some(EnumVariant { ty: None, .. }) => { | ||||
|                 panic!( | ||||
|                     "should have called EnumSimValueFromBits::variant_no_field_{clone}from_bits" | ||||
|                 ); | ||||
|             } | ||||
|             Some(EnumVariant { ty: Some(_), .. }) => { | ||||
|                 panic!( | ||||
|                     "should have called EnumSimValueFromBits::variant_with_field_{clone}from_bits" | ||||
|                 ); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|     #[track_caller] | ||||
|     fn known_variant(&self, clone: bool) -> (Option<CanonicalType>, &'a BitSlice, &'a BitSlice) { | ||||
|         let Some(EnumVariant { ty, .. }) = self.variants.get(self.discriminant) else { | ||||
|             self.usage_error(clone); | ||||
|         }; | ||||
|         let variant_bit_width = ty.map_or(0, CanonicalType::bit_width); | ||||
|         let (variant_bits, padding_bits) = self.body_bits.split_at(variant_bit_width); | ||||
|         (*ty, variant_bits, padding_bits) | ||||
|     } | ||||
|     #[track_caller] | ||||
|     pub fn unknown_variant_from_bits(self) -> UnknownVariantSimValue { | ||||
|         let None = self.variants.get(self.discriminant) else { | ||||
|             self.usage_error(false); | ||||
|         }; | ||||
|         UnknownVariantSimValue::new( | ||||
|             self.discriminant, | ||||
|             UIntValue::new(Arc::new(self.body_bits.to_bitvec())), | ||||
|         ) | ||||
|     } | ||||
|     #[track_caller] | ||||
|     pub fn unknown_variant_clone_from_bits(self, value: &mut UnknownVariantSimValue) { | ||||
|         let None = self.variants.get(self.discriminant) else { | ||||
|             self.usage_error(true); | ||||
|         }; | ||||
|         value.discriminant = self.discriminant; | ||||
|         assert_eq!(value.body_bits.width(), self.body_bits.len()); | ||||
|         value | ||||
|             .body_bits | ||||
|             .bits_mut() | ||||
|             .copy_from_bitslice(self.body_bits); | ||||
|     } | ||||
|     #[track_caller] | ||||
|     pub fn variant_no_field_from_bits(self) -> EnumPaddingSimValue { | ||||
|         let (None, _variant_bits, padding_bits) = self.known_variant(false) else { | ||||
|             self.usage_error(false); | ||||
|         }; | ||||
|         EnumPaddingSimValue::from_bitslice(padding_bits) | ||||
|     } | ||||
|     #[track_caller] | ||||
|     pub fn variant_with_field_from_bits<T: Type>(self) -> (SimValue<T>, EnumPaddingSimValue) { | ||||
|         let (Some(variant_ty), variant_bits, padding_bits) = self.known_variant(false) else { | ||||
|             self.usage_error(false); | ||||
|         }; | ||||
|         ( | ||||
|             SimValue::from_bitslice(T::from_canonical(variant_ty), variant_bits), | ||||
|             EnumPaddingSimValue::from_bitslice(padding_bits), | ||||
|         ) | ||||
|     } | ||||
|     #[track_caller] | ||||
|     fn clone_padding_from_bits(padding: &mut EnumPaddingSimValue, padding_bits: &BitSlice) { | ||||
|         match padding.bits_mut() { | ||||
|             None => *padding = EnumPaddingSimValue::from_bitslice(padding_bits), | ||||
|             Some(padding) => { | ||||
|                 assert_eq!(padding.width(), padding_bits.len()); | ||||
|                 padding.bits_mut().copy_from_bitslice(padding_bits); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|     #[track_caller] | ||||
|     pub fn variant_no_field_clone_from_bits(self, padding: &mut EnumPaddingSimValue) { | ||||
|         let (None, _variant_bits, padding_bits) = self.known_variant(true) else { | ||||
|             self.usage_error(true); | ||||
|         }; | ||||
|         Self::clone_padding_from_bits(padding, padding_bits); | ||||
|     } | ||||
|     #[track_caller] | ||||
|     pub fn variant_with_field_clone_from_bits<T: Type>( | ||||
|         self, | ||||
|         value: &mut SimValue<T>, | ||||
|         padding: &mut EnumPaddingSimValue, | ||||
|     ) { | ||||
|         let (Some(variant_ty), variant_bits, padding_bits) = self.known_variant(true) else { | ||||
|             self.usage_error(true); | ||||
|         }; | ||||
|         assert_eq!(SimValue::ty(value), T::from_canonical(variant_ty)); | ||||
|         SimValue::bits_mut(value) | ||||
|             .bits_mut() | ||||
|             .copy_from_bitslice(variant_bits); | ||||
|         Self::clone_padding_from_bits(padding, padding_bits); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| pub struct EnumSimValueToBits<'a> { | ||||
|     variants: Interned<[EnumVariant]>, | ||||
|     bit_width: usize, | ||||
|     discriminant_bit_width: usize, | ||||
|     bits: &'a mut BitSlice, | ||||
| } | ||||
| 
 | ||||
| impl<'a> EnumSimValueToBits<'a> { | ||||
|     #[track_caller] | ||||
|     pub fn new<T: EnumType>(ty: T, bits: &'a mut BitSlice) -> Self { | ||||
|         let variants = ty.variants(); | ||||
|         let bit_width = EnumTypePropertiesBuilder::new() | ||||
|             .variants(variants) | ||||
|             .finish() | ||||
|             .bit_width; | ||||
|         assert_eq!(bit_width, bits.len()); | ||||
|         Self { | ||||
|             variants, | ||||
|             bit_width, | ||||
|             discriminant_bit_width: discriminant_bit_width_impl(variants.len()), | ||||
|             bits, | ||||
|         } | ||||
|     } | ||||
|     #[track_caller] | ||||
|     fn discriminant_to_bits(&mut self, mut discriminant: usize) { | ||||
|         let orig_discriminant = discriminant; | ||||
|         let discriminant_bits = | ||||
|             &mut discriminant.view_bits_mut::<Lsb0>()[..self.discriminant_bit_width]; | ||||
|         self.bits[..self.discriminant_bit_width].copy_from_bitslice(discriminant_bits); | ||||
|         discriminant_bits.fill(false); | ||||
|         assert!( | ||||
|             discriminant == 0, | ||||
|             "{orig_discriminant:#x} is too big to fit in enum discriminant bits", | ||||
|         ); | ||||
|     } | ||||
|     #[track_caller] | ||||
|     pub fn unknown_variant_to_bits(mut self, value: &UnknownVariantSimValue) { | ||||
|         self.discriminant_to_bits(value.discriminant); | ||||
|         let None = self.variants.get(value.discriminant) else { | ||||
|             panic!("can't use UnknownVariantSimValue to set known discriminant"); | ||||
|         }; | ||||
|         assert_eq!( | ||||
|             self.bit_width - self.discriminant_bit_width, | ||||
|             value.body_bits.width() | ||||
|         ); | ||||
|         self.bits[self.discriminant_bit_width..].copy_from_bitslice(value.body_bits.bits()); | ||||
|     } | ||||
|     #[track_caller] | ||||
|     fn known_variant( | ||||
|         mut self, | ||||
|         discriminant: usize, | ||||
|         padding: &EnumPaddingSimValue, | ||||
|     ) -> (Option<CanonicalType>, &'a mut BitSlice) { | ||||
|         self.discriminant_to_bits(discriminant); | ||||
|         let variant_ty = self.variants[discriminant].ty; | ||||
|         let variant_bit_width = variant_ty.map_or(0, CanonicalType::bit_width); | ||||
|         let padding_bits = &mut self.bits[self.discriminant_bit_width..][variant_bit_width..]; | ||||
|         if let Some(padding) = padding.bits() { | ||||
|             assert_eq!(padding.width(), padding_bits.len()); | ||||
|             padding_bits.copy_from_bitslice(padding.bits()); | ||||
|         } else { | ||||
|             padding_bits.fill(false); | ||||
|         } | ||||
|         let variant_bits = &mut self.bits[self.discriminant_bit_width..][..variant_bit_width]; | ||||
|         (variant_ty, variant_bits) | ||||
|     } | ||||
|     #[track_caller] | ||||
|     pub fn variant_no_field_to_bits(self, discriminant: usize, padding: &EnumPaddingSimValue) { | ||||
|         let (None, _variant_bits) = self.known_variant(discriminant, padding) else { | ||||
|             panic!("expected variant to have no field"); | ||||
|         }; | ||||
|     } | ||||
|     #[track_caller] | ||||
|     pub fn variant_with_field_to_bits<T: Type>( | ||||
|         self, | ||||
|         discriminant: usize, | ||||
|         value: &SimValue<T>, | ||||
|         padding: &EnumPaddingSimValue, | ||||
|     ) { | ||||
|         let (Some(variant_ty), variant_bits) = self.known_variant(discriminant, padding) else { | ||||
|             panic!("expected variant to have a field"); | ||||
|         }; | ||||
|         assert_eq!(SimValue::ty(value), T::from_canonical(variant_ty)); | ||||
|         variant_bits.copy_from_bitslice(SimValue::bits(value).bits()); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[doc(hidden)] | ||||
| pub fn assert_is_enum_type<T: EnumType>(v: T) -> T { | ||||
|     v | ||||
| } | ||||
| 
 | ||||
| #[doc(hidden)] | ||||
| pub fn enum_type_to_sim_builder<T: EnumType>(v: T) -> T::SimBuilder { | ||||
|     v.into() | ||||
| } | ||||
| 
 | ||||
| #[hdl] | ||||
|  | @ -360,6 +692,79 @@ pub enum HdlOption<T: Type> { | |||
|     HdlSome(T), | ||||
| } | ||||
| 
 | ||||
| impl<Lhs: Type + ExprPartialEq<Rhs>, Rhs: Type> ExprPartialEq<HdlOption<Rhs>> for HdlOption<Lhs> { | ||||
|     #[hdl] | ||||
|     fn cmp_eq(lhs: Expr<Self>, rhs: Expr<HdlOption<Rhs>>) -> Expr<Bool> { | ||||
|         #[hdl] | ||||
|         let cmp_eq = wire(); | ||||
|         #[hdl] | ||||
|         match lhs { | ||||
|             HdlSome(lhs) => | ||||
|             { | ||||
|                 #[hdl] | ||||
|                 match rhs { | ||||
|                     HdlSome(rhs) => connect(cmp_eq, ExprPartialEq::cmp_eq(lhs, rhs)), | ||||
|                     HdlNone => connect(cmp_eq, false), | ||||
|                 } | ||||
|             } | ||||
|             HdlNone => | ||||
|             { | ||||
|                 #[hdl] | ||||
|                 match rhs { | ||||
|                     HdlSome(_) => connect(cmp_eq, false), | ||||
|                     HdlNone => connect(cmp_eq, true), | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         cmp_eq | ||||
|     } | ||||
| 
 | ||||
|     #[hdl] | ||||
|     fn cmp_ne(lhs: Expr<Self>, rhs: Expr<HdlOption<Rhs>>) -> Expr<Bool> { | ||||
|         #[hdl] | ||||
|         let cmp_ne = wire(); | ||||
|         #[hdl] | ||||
|         match lhs { | ||||
|             HdlSome(lhs) => | ||||
|             { | ||||
|                 #[hdl] | ||||
|                 match rhs { | ||||
|                     HdlSome(rhs) => connect(cmp_ne, ExprPartialEq::cmp_ne(lhs, rhs)), | ||||
|                     HdlNone => connect(cmp_ne, true), | ||||
|                 } | ||||
|             } | ||||
|             HdlNone => | ||||
|             { | ||||
|                 #[hdl] | ||||
|                 match rhs { | ||||
|                     HdlSome(_) => connect(cmp_ne, true), | ||||
|                     HdlNone => connect(cmp_ne, false), | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         cmp_ne | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<Lhs: SimValuePartialEq<Rhs>, Rhs: Type> SimValuePartialEq<HdlOption<Rhs>> for HdlOption<Lhs> { | ||||
|     fn sim_value_eq(this: &SimValue<Self>, other: &SimValue<HdlOption<Rhs>>) -> bool { | ||||
|         type SimValueMatch<T> = <T as Type>::SimValue; | ||||
|         match (&**this, &**other) { | ||||
|             (SimValueMatch::<Self>::HdlNone(_), SimValueMatch::<HdlOption<Rhs>>::HdlNone(_)) => { | ||||
|                 true | ||||
|             } | ||||
|             (SimValueMatch::<Self>::HdlSome(..), SimValueMatch::<HdlOption<Rhs>>::HdlNone(_)) | ||||
|             | (SimValueMatch::<Self>::HdlNone(_), SimValueMatch::<HdlOption<Rhs>>::HdlSome(..)) => { | ||||
|                 false | ||||
|             } | ||||
|             ( | ||||
|                 SimValueMatch::<Self>::HdlSome(l, _), | ||||
|                 SimValueMatch::<HdlOption<Rhs>>::HdlSome(r, _), | ||||
|             ) => l == r, | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[allow(non_snake_case)] | ||||
| pub fn HdlNone<T: StaticType>() -> Expr<HdlOption<T>> { | ||||
|     HdlOption[T::TYPE].HdlNone() | ||||
|  |  | |||
|  | @ -16,6 +16,7 @@ use crate::{ | |||
|         transform::visit::{Fold, Folder, Visit, Visitor}, | ||||
|         Instance, ModuleIO, | ||||
|     }, | ||||
|     phantom_const::PhantomConst, | ||||
|     reg::Reg, | ||||
|     reset::{AsyncReset, Reset, ResetType, ResetTypeDispatch, SyncReset}, | ||||
|     ty::{CanonicalType, StaticType, Type, TypeWithDeref}, | ||||
|  | @ -109,6 +110,7 @@ expr_enum! { | |||
|         UIntLiteral(Interned<UIntValue>), | ||||
|         SIntLiteral(Interned<SIntValue>), | ||||
|         BoolLiteral(bool), | ||||
|         PhantomConst(PhantomConst), | ||||
|         BundleLiteral(ops::BundleLiteral), | ||||
|         ArrayLiteral(ops::ArrayLiteral<CanonicalType, DynSize>), | ||||
|         EnumLiteral(ops::EnumLiteral), | ||||
|  | @ -272,6 +274,17 @@ pub struct Expr<T: Type> { | |||
| 
 | ||||
| impl<T: Type + fmt::Debug> fmt::Debug for Expr<T> { | ||||
|     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||||
|         #[cfg(debug_assertions)] | ||||
|         { | ||||
|             let Self { | ||||
|                 __enum, | ||||
|                 __ty, | ||||
|                 __flow, | ||||
|             } = self; | ||||
|             let expr_ty = __ty.canonical(); | ||||
|             let enum_ty = __enum.to_expr().__ty; | ||||
|             assert_eq!(expr_ty, enum_ty, "expr ty mismatch:\nExpr {{\n__enum: {__enum:?},\n__ty: {__ty:?},\n__flow: {__flow:?}\n}}"); | ||||
|         } | ||||
|         self.__enum.fmt(f) | ||||
|     } | ||||
| } | ||||
|  | @ -698,6 +711,7 @@ impl<T: ToExpr + ?Sized> CastToBits for T { | |||
| } | ||||
| 
 | ||||
| pub trait CastBitsTo { | ||||
|     #[track_caller] | ||||
|     fn cast_bits_to<T: Type>(&self, ty: T) -> Expr<T>; | ||||
| } | ||||
| 
 | ||||
|  | @ -755,3 +769,27 @@ pub fn repeat<T: Type, L: SizeType>( | |||
|     ) | ||||
|     .to_expr() | ||||
| } | ||||
| 
 | ||||
| impl<T: ?Sized + crate::phantom_const::PhantomConstValue> ToExpr for PhantomConst<T> { | ||||
|     type Type = Self; | ||||
| 
 | ||||
|     fn to_expr(&self) -> Expr<Self::Type> { | ||||
|         Expr { | ||||
|             __enum: ExprEnum::PhantomConst(self.canonical_phantom_const()).intern_sized(), | ||||
|             __ty: *self, | ||||
|             __flow: Flow::Source, | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<T: ?Sized + crate::phantom_const::PhantomConstValue> GetTarget for PhantomConst<T> { | ||||
|     fn target(&self) -> Option<Interned<Target>> { | ||||
|         None | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<T: ?Sized + crate::phantom_const::PhantomConstValue> ToLiteralBits for PhantomConst<T> { | ||||
|     fn to_literal_bits(&self) -> Result<Interned<BitSlice>, NotALiteralExpr> { | ||||
|         Ok(Interned::default()) | ||||
|     } | ||||
| } | ||||
|  |  | |||
|  | @ -11,14 +11,15 @@ use crate::{ | |||
|             GetTarget, Target, TargetPathArrayElement, TargetPathBundleField, | ||||
|             TargetPathDynArrayElement, TargetPathElement, | ||||
|         }, | ||||
|         CastTo, Expr, ExprEnum, Flow, HdlPartialEq, HdlPartialOrd, NotALiteralExpr, ReduceBits, | ||||
|         ToExpr, ToLiteralBits, | ||||
|         CastBitsTo as _, CastTo, CastToBits as _, Expr, ExprEnum, Flow, HdlPartialEq, | ||||
|         HdlPartialOrd, NotALiteralExpr, ReduceBits, ToExpr, ToLiteralBits, | ||||
|     }, | ||||
|     int::{ | ||||
|         Bool, BoolOrIntType, DynSize, IntType, KnownSize, SInt, SIntType, SIntValue, Size, UInt, | ||||
|         UIntType, UIntValue, | ||||
|     }, | ||||
|     intern::{Intern, Interned}, | ||||
|     phantom_const::{PhantomConst, PhantomConstValue}, | ||||
|     reset::{ | ||||
|         AsyncReset, Reset, ResetType, ResetTypeDispatch, SyncReset, ToAsyncReset, ToReset, | ||||
|         ToSyncReset, | ||||
|  | @ -1892,6 +1893,26 @@ impl ExprCastTo<Clock> for Clock { | |||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<T: ?Sized + PhantomConstValue> ExprCastTo<()> for PhantomConst<T> { | ||||
|     fn cast_to(src: Expr<Self>, to_type: ()) -> Expr<()> { | ||||
|         src.cast_to_bits().cast_bits_to(to_type) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<T: ?Sized + PhantomConstValue> ExprCastTo<PhantomConst<T>> for () { | ||||
|     fn cast_to(src: Expr<Self>, to_type: PhantomConst<T>) -> Expr<PhantomConst<T>> { | ||||
|         src.cast_to_bits().cast_bits_to(to_type) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<T: ?Sized + PhantomConstValue, U: ?Sized + PhantomConstValue> ExprCastTo<PhantomConst<T>> | ||||
|     for PhantomConst<U> | ||||
| { | ||||
|     fn cast_to(src: Expr<Self>, to_type: PhantomConst<T>) -> Expr<PhantomConst<T>> { | ||||
|         src.cast_to_bits().cast_bits_to(to_type) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[derive(Copy, Clone, PartialEq, Eq, Hash)] | ||||
| pub struct FieldAccess<FieldType: Type = CanonicalType> { | ||||
|     base: Expr<Bundle>, | ||||
|  | @ -2708,3 +2729,47 @@ impl<T: Type> ToExpr for Uninit<T> { | |||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| pub trait ExprIntoIterator: Type { | ||||
|     type Item: Type; | ||||
|     type ExprIntoIter: Iterator<Item = Expr<Self::Item>>; | ||||
| 
 | ||||
|     fn expr_into_iter(e: Expr<Self>) -> Self::ExprIntoIter; | ||||
| } | ||||
| 
 | ||||
| impl<T: ExprIntoIterator> IntoIterator for Expr<T> { | ||||
|     type Item = Expr<T::Item>; | ||||
|     type IntoIter = T::ExprIntoIter; | ||||
| 
 | ||||
|     fn into_iter(self) -> Self::IntoIter { | ||||
|         T::expr_into_iter(self) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<T: ExprIntoIterator> IntoIterator for &'_ Expr<T> { | ||||
|     type Item = Expr<T::Item>; | ||||
|     type IntoIter = T::ExprIntoIter; | ||||
| 
 | ||||
|     fn into_iter(self) -> Self::IntoIter { | ||||
|         T::expr_into_iter(*self) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<T: ExprIntoIterator> IntoIterator for &'_ mut Expr<T> { | ||||
|     type Item = Expr<T::Item>; | ||||
|     type IntoIter = T::ExprIntoIter; | ||||
| 
 | ||||
|     fn into_iter(self) -> Self::IntoIter { | ||||
|         T::expr_into_iter(*self) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| pub trait ExprFromIterator<A>: Type { | ||||
|     fn expr_from_iter<T: IntoIterator<Item = A>>(iter: T) -> Expr<Self>; | ||||
| } | ||||
| 
 | ||||
| impl<This: ExprFromIterator<A>, A> FromIterator<A> for Expr<This> { | ||||
|     fn from_iter<T: IntoIterator<Item = A>>(iter: T) -> Self { | ||||
|         This::expr_from_iter(iter) | ||||
|     } | ||||
| } | ||||
|  |  | |||
|  | @ -15,7 +15,7 @@ use crate::{ | |||
|         target::{ | ||||
|             Target, TargetBase, TargetPathArrayElement, TargetPathBundleField, TargetPathElement, | ||||
|         }, | ||||
|         Expr, ExprEnum, | ||||
|         CastBitsTo, Expr, ExprEnum, | ||||
|     }, | ||||
|     formal::FormalKind, | ||||
|     int::{Bool, DynSize, IntType, SIntValue, UInt, UIntValue}, | ||||
|  | @ -36,12 +36,11 @@ use crate::{ | |||
|     ty::{CanonicalType, Type}, | ||||
|     util::{ | ||||
|         const_str_array_is_strictly_ascending, BitSliceWriteWithBase, DebugAsRawString, | ||||
|         GenericConstBool, | ||||
|         GenericConstBool, HashMap, HashSet, | ||||
|     }, | ||||
| }; | ||||
| use bitvec::slice::BitSlice; | ||||
| use clap::value_parser; | ||||
| use hashbrown::{HashMap, HashSet}; | ||||
| use num_traits::Signed; | ||||
| use serde::Serialize; | ||||
| use std::{ | ||||
|  | @ -447,6 +446,7 @@ impl TypeState { | |||
|             CanonicalType::AsyncReset(AsyncReset {}) => "AsyncReset".into(), | ||||
|             CanonicalType::SyncReset(SyncReset {}) => "UInt<1>".into(), | ||||
|             CanonicalType::Reset(Reset {}) => "Reset".into(), | ||||
|             CanonicalType::PhantomConst(_) => "{}".into(), | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | @ -1152,6 +1152,7 @@ impl<'a> Exporter<'a> { | |||
|             | CanonicalType::Clock(_) | ||||
|             | CanonicalType::AsyncReset(_) | ||||
|             | CanonicalType::Reset(_) => format!("asUInt({value_str})"), | ||||
|             CanonicalType::PhantomConst(_) => "UInt<0>(0)".into(), | ||||
|         } | ||||
|     } | ||||
|     fn expr_cast_bits_to_bundle( | ||||
|  | @ -1357,6 +1358,12 @@ impl<'a> Exporter<'a> { | |||
|             CanonicalType::AsyncReset(_) => format!("asAsyncReset({value_str})"), | ||||
|             CanonicalType::SyncReset(_) => value_str, | ||||
|             CanonicalType::Reset(_) => unreachable!("Reset is not bit castable to"), | ||||
|             CanonicalType::PhantomConst(_) => { | ||||
|                 let retval = self.module.ns.make_new("_cast_bits_to_phantom_const_expr"); | ||||
|                 definitions.add_definition_line(format_args!("{extra_indent}wire {retval}: {{}}")); | ||||
|                 definitions.add_definition_line(format_args!("{extra_indent}invalidate {retval}")); | ||||
|                 return retval.to_string(); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|     fn expr_unary<T: Type>( | ||||
|  | @ -1395,6 +1402,11 @@ impl<'a> Exporter<'a> { | |||
|             ExprEnum::UIntLiteral(literal) => self.uint_literal(&literal), | ||||
|             ExprEnum::SIntLiteral(literal) => self.sint_literal(&literal), | ||||
|             ExprEnum::BoolLiteral(literal) => self.bool_literal(literal), | ||||
|             ExprEnum::PhantomConst(ty) => self.expr( | ||||
|                 UInt[0].zero().cast_bits_to(ty.canonical()), | ||||
|                 definitions, | ||||
|                 const_ty, | ||||
|             ), | ||||
|             ExprEnum::ArrayLiteral(array_literal) => { | ||||
|                 self.array_literal_expr(array_literal, definitions, const_ty) | ||||
|             } | ||||
|  | @ -2245,6 +2257,7 @@ impl<'a> Exporter<'a> { | |||
|             ModuleBody::Extern(ExternModuleBody { | ||||
|                 verilog_name, | ||||
|                 parameters, | ||||
|                 simulation: _, | ||||
|             }) => { | ||||
|                 let verilog_name = Ident(verilog_name); | ||||
|                 writeln!(body, "{indent}defname = {verilog_name}").unwrap(); | ||||
|  | @ -2608,7 +2621,7 @@ fn export_impl( | |||
|             indent_depth: &indent_depth, | ||||
|             indent: "    ", | ||||
|         }, | ||||
|         seen_modules: HashSet::new(), | ||||
|         seen_modules: HashSet::default(), | ||||
|         unwritten_modules: VecDeque::new(), | ||||
|         global_ns, | ||||
|         module: ModuleState::default(), | ||||
|  |  | |||
|  | @ -2,27 +2,52 @@ | |||
| // See Notices.txt for copyright information
 | ||||
| 
 | ||||
| use crate::{ | ||||
|     array::ArrayType, | ||||
|     expr::{ | ||||
|         target::{GetTarget, Target}, | ||||
|         Expr, NotALiteralExpr, ToExpr, ToLiteralBits, | ||||
|     }, | ||||
|     hdl, | ||||
|     intern::{Intern, Interned, Memoize}, | ||||
|     sim::value::{SimValue, ToSimValueWithType}, | ||||
|     source_location::SourceLocation, | ||||
|     ty::{impl_match_variant_as_self, CanonicalType, StaticType, Type, TypeProperties}, | ||||
|     util::{interned_bit, ConstBool, ConstUsize, GenericConstBool, GenericConstUsize}, | ||||
| }; | ||||
| use bitvec::{order::Lsb0, slice::BitSlice, vec::BitVec}; | ||||
| use bitvec::{order::Lsb0, slice::BitSlice, vec::BitVec, view::BitView}; | ||||
| use num_bigint::{BigInt, BigUint, Sign}; | ||||
| use num_traits::{Signed, Zero}; | ||||
| use num_traits::{One, Signed, Zero}; | ||||
| use serde::{ | ||||
|     de::{DeserializeOwned, Error, Visitor}, | ||||
|     Deserialize, Deserializer, Serialize, Serializer, | ||||
| }; | ||||
| use std::{ | ||||
|     borrow::{BorrowMut, Cow}, | ||||
|     fmt, | ||||
|     marker::PhantomData, | ||||
|     num::NonZero, | ||||
|     ops::{Bound, Index, Not, Range, RangeBounds, RangeInclusive}, | ||||
|     str::FromStr, | ||||
|     sync::Arc, | ||||
| }; | ||||
| 
 | ||||
| mod uint_in_range; | ||||
| 
 | ||||
| #[hdl] | ||||
| pub type UIntInRangeType<Start: Size, End: Size> = uint_in_range::UIntInRangeType<Start, End>; | ||||
| 
 | ||||
| #[hdl] | ||||
| pub type UIntInRange<const START: usize, const END: usize> = | ||||
|     UIntInRangeType<ConstUsize<START>, ConstUsize<END>>; | ||||
| 
 | ||||
| #[hdl] | ||||
| pub type UIntInRangeInclusiveType<Start: Size, End: Size> = | ||||
|     uint_in_range::UIntInRangeInclusiveType<Start, End>; | ||||
| 
 | ||||
| #[hdl] | ||||
| pub type UIntInRangeInclusive<const START: usize, const END: usize> = | ||||
|     UIntInRangeInclusiveType<ConstUsize<START>, ConstUsize<END>>; | ||||
| 
 | ||||
| mod sealed { | ||||
|     pub trait BoolOrIntTypeSealed {} | ||||
|     pub trait SizeSealed {} | ||||
|  | @ -49,6 +74,16 @@ pub trait KnownSize: | |||
|         + IntoIterator<Item = Expr<Element>> | ||||
|         + TryFrom<Vec<Expr<Element>>> | ||||
|         + Into<Vec<Expr<Element>>>; | ||||
|     type ArraySimValue<Element: Type>: AsRef<[SimValue<Element>]> | ||||
|         + AsMut<[SimValue<Element>]> | ||||
|         + BorrowMut<[SimValue<Element>]> | ||||
|         + 'static | ||||
|         + Clone | ||||
|         + std::fmt::Debug | ||||
|         + IntoIterator<Item = SimValue<Element>> | ||||
|         + TryFrom<Vec<SimValue<Element>>> | ||||
|         + Into<Vec<SimValue<Element>>> | ||||
|         + ToSimValueWithType<ArrayType<Element, Self>>; | ||||
| } | ||||
| 
 | ||||
| macro_rules! known_widths { | ||||
|  | @ -60,6 +95,7 @@ macro_rules! known_widths { | |||
|         }> { | ||||
|             const SIZE: Self = Self; | ||||
|             type ArrayMatch<Element: Type> = [Expr<Element>; Self::VALUE]; | ||||
|             type ArraySimValue<Element: Type> = [SimValue<Element>; Self::VALUE]; | ||||
|         } | ||||
|     }; | ||||
|     ([2 $($rest:tt)*] $($bits:literal)+) => { | ||||
|  | @ -72,6 +108,7 @@ macro_rules! known_widths { | |||
|         impl KnownSize for ConstUsize<{2 $(* $rest)*}> { | ||||
|             const SIZE: Self = Self; | ||||
|             type ArrayMatch<Element: Type> = [Expr<Element>; Self::VALUE]; | ||||
|             type ArraySimValue<Element: Type> = [SimValue<Element>; Self::VALUE]; | ||||
|         } | ||||
|     }; | ||||
| } | ||||
|  | @ -79,13 +116,31 @@ macro_rules! known_widths { | |||
| known_widths!([2 2 2 2 2 2 2 2 2]); | ||||
| 
 | ||||
| pub trait SizeType: | ||||
|     sealed::SizeTypeSealed + Copy + Ord + std::hash::Hash + std::fmt::Debug + Send + Sync + 'static | ||||
|     sealed::SizeTypeSealed | ||||
|     + Copy | ||||
|     + Ord | ||||
|     + std::hash::Hash | ||||
|     + std::fmt::Debug | ||||
|     + Send | ||||
|     + Sync | ||||
|     + 'static | ||||
|     + Serialize | ||||
|     + DeserializeOwned | ||||
| { | ||||
|     type Size: Size<SizeType = Self>; | ||||
| } | ||||
| 
 | ||||
| pub trait Size: | ||||
|     sealed::SizeSealed + Copy + Ord + std::hash::Hash + std::fmt::Debug + Send + Sync + 'static | ||||
|     sealed::SizeSealed | ||||
|     + Copy | ||||
|     + Ord | ||||
|     + std::hash::Hash | ||||
|     + std::fmt::Debug | ||||
|     + Send | ||||
|     + Sync | ||||
|     + 'static | ||||
|     + Serialize | ||||
|     + DeserializeOwned | ||||
| { | ||||
|     type ArrayMatch<Element: Type>: AsRef<[Expr<Element>]> | ||||
|         + AsMut<[Expr<Element>]> | ||||
|  | @ -100,6 +155,16 @@ pub trait Size: | |||
|         + IntoIterator<Item = Expr<Element>> | ||||
|         + TryFrom<Vec<Expr<Element>>> | ||||
|         + Into<Vec<Expr<Element>>>; | ||||
|     type ArraySimValue<Element: Type>: AsRef<[SimValue<Element>]> | ||||
|         + AsMut<[SimValue<Element>]> | ||||
|         + BorrowMut<[SimValue<Element>]> | ||||
|         + 'static | ||||
|         + Clone | ||||
|         + std::fmt::Debug | ||||
|         + IntoIterator<Item = SimValue<Element>> | ||||
|         + TryFrom<Vec<SimValue<Element>>> | ||||
|         + Into<Vec<SimValue<Element>>> | ||||
|         + ToSimValueWithType<ArrayType<Element, Self>>; | ||||
|     const KNOWN_VALUE: Option<usize>; | ||||
|     type SizeType: SizeType<Size = Self> | ||||
|         + Copy | ||||
|  | @ -125,6 +190,7 @@ impl SizeType for usize { | |||
| 
 | ||||
| impl Size for DynSize { | ||||
|     type ArrayMatch<Element: Type> = Box<[Expr<Element>]>; | ||||
|     type ArraySimValue<Element: Type> = Box<[SimValue<Element>]>; | ||||
|     const KNOWN_VALUE: Option<usize> = None; | ||||
|     type SizeType = usize; | ||||
| 
 | ||||
|  | @ -147,6 +213,7 @@ impl<T: KnownSize> SizeType for T { | |||
| 
 | ||||
| impl<T: KnownSize> Size for T { | ||||
|     type ArrayMatch<Element: Type> = <T as KnownSize>::ArrayMatch<Element>; | ||||
|     type ArraySimValue<Element: Type> = <T as KnownSize>::ArraySimValue<Element>; | ||||
| 
 | ||||
|     const KNOWN_VALUE: Option<usize> = Some(T::VALUE); | ||||
| 
 | ||||
|  | @ -165,6 +232,305 @@ impl<T: KnownSize> Size for T { | |||
|     } | ||||
| } | ||||
| 
 | ||||
| #[derive(Clone, PartialEq, Eq, Debug)] | ||||
| pub enum ParseIntValueError { | ||||
|     Empty, | ||||
|     InvalidDigit, | ||||
|     MissingDigits, | ||||
|     InvalidRadix, | ||||
|     MissingType, | ||||
|     InvalidType, | ||||
|     TypeMismatch { | ||||
|         parsed_signed: bool, | ||||
|         parsed_width: usize, | ||||
|         expected_signed: bool, | ||||
|         expected_width: usize, | ||||
|     }, | ||||
|     PosOverflow, | ||||
|     NegOverflow, | ||||
|     WidthOverflow, | ||||
|     MissingWidth, | ||||
| } | ||||
| 
 | ||||
| impl std::error::Error for ParseIntValueError {} | ||||
| 
 | ||||
| impl fmt::Display for ParseIntValueError { | ||||
|     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||||
|         f.write_str(match self { | ||||
|             Self::Empty => "can't parse integer from empty string", | ||||
|             Self::InvalidDigit => "invalid digit", | ||||
|             Self::MissingDigits => "missing digits", | ||||
|             Self::InvalidRadix => "invalid radix", | ||||
|             Self::MissingType => "missing type", | ||||
|             Self::InvalidType => "invalid type", | ||||
|             Self::TypeMismatch { | ||||
|                 parsed_signed, | ||||
|                 parsed_width, | ||||
|                 expected_signed, | ||||
|                 expected_width, | ||||
|             } => { | ||||
|                 return write!( | ||||
|                     f, | ||||
|                     "type mismatch: parsed type {parsed_signed_str}{parsed_width}, \ | ||||
|                     expected type {expected_signed_str}{expected_width}",
 | ||||
|                     parsed_signed_str = if *parsed_signed { "i" } else { "u" }, | ||||
|                     expected_signed_str = if *expected_signed { "i" } else { "u" }, | ||||
|                 ); | ||||
|             } | ||||
|             Self::PosOverflow => "value too large to fit in type", | ||||
|             Self::NegOverflow => "value too small to fit in type", | ||||
|             Self::WidthOverflow => "width is too large", | ||||
|             Self::MissingWidth => "missing width", | ||||
|         }) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| fn parse_int_value( | ||||
|     s: &str, | ||||
|     type_is_signed: bool, | ||||
|     type_width: Option<usize>, | ||||
|     parse_type: bool, | ||||
| ) -> Result<Arc<BitVec>, ParseIntValueError> { | ||||
|     if !parse_type && type_width.is_none() { | ||||
|         return Err(ParseIntValueError::MissingWidth); | ||||
|     } | ||||
|     let mut s = s.trim(); | ||||
|     if s.is_empty() { | ||||
|         return Err(ParseIntValueError::Empty); | ||||
|     } | ||||
|     let negative = match s.bytes().next() { | ||||
|         Some(ch @ (b'+' | b'-')) => { | ||||
|             s = s[1..].trim_start(); | ||||
|             ch == b'-' | ||||
|         } | ||||
|         _ => false, | ||||
|     }; | ||||
|     let radix = match s.bytes().next() { | ||||
|         Some(b'0') => match s.bytes().nth(1) { | ||||
|             Some(b'x' | b'X') => { | ||||
|                 s = &s[2..]; | ||||
|                 16 | ||||
|             } | ||||
|             Some(b'b' | b'B') => { | ||||
|                 s = &s[2..]; | ||||
|                 2 | ||||
|             } | ||||
|             Some(b'o' | b'O') => { | ||||
|                 s = &s[2..]; | ||||
|                 8 | ||||
|             } | ||||
|             _ => 10, | ||||
|         }, | ||||
|         Some(b'1'..=b'9') => 10, | ||||
|         _ => return Err(ParseIntValueError::InvalidDigit), | ||||
|     }; | ||||
|     let mut any_digits = false; | ||||
|     let digits_end = s | ||||
|         .as_bytes() | ||||
|         .iter() | ||||
|         .position(|&ch| { | ||||
|             if ch == b'_' { | ||||
|                 false | ||||
|             } else if (ch as char).to_digit(radix).is_some() { | ||||
|                 any_digits = true; | ||||
|                 false | ||||
|             } else { | ||||
|                 true | ||||
|             } | ||||
|         }) | ||||
|         .unwrap_or(s.len()); | ||||
|     let digits = &s[..digits_end]; | ||||
|     s = &s[digits_end..]; | ||||
|     if !any_digits { | ||||
|         return Err(ParseIntValueError::MissingDigits); | ||||
|     } | ||||
|     let width = if parse_type { | ||||
|         const HDL_PREFIX: &[u8] = b"hdl_"; | ||||
|         let mut missing_type = ParseIntValueError::MissingType; | ||||
|         if s.as_bytes() | ||||
|             .get(..HDL_PREFIX.len()) | ||||
|             .is_some_and(|bytes| bytes.eq_ignore_ascii_case(HDL_PREFIX)) | ||||
|         { | ||||
|             s = &s[HDL_PREFIX.len()..]; | ||||
|             missing_type = ParseIntValueError::InvalidType; | ||||
|         } | ||||
|         let signed = match s.bytes().next() { | ||||
|             Some(b'u' | b'U') => false, | ||||
|             Some(b'i' | b'I') => true, | ||||
|             Some(_) => return Err(ParseIntValueError::InvalidType), | ||||
|             None => return Err(missing_type), | ||||
|         }; | ||||
|         s = &s[1..]; | ||||
|         let mut width = 0usize; | ||||
|         let mut any_digits = false; | ||||
|         for ch in s.bytes() { | ||||
|             let digit = (ch as char) | ||||
|                 .to_digit(10) | ||||
|                 .ok_or(ParseIntValueError::InvalidDigit)?; | ||||
|             any_digits = true; | ||||
|             width = width | ||||
|                 .checked_mul(10) | ||||
|                 .and_then(|v| v.checked_add(digit as usize)) | ||||
|                 .ok_or(ParseIntValueError::WidthOverflow)?; | ||||
|         } | ||||
|         if !any_digits { | ||||
|             return Err(ParseIntValueError::MissingDigits); | ||||
|         } | ||||
|         if width > <BitSlice>::MAX_BITS { | ||||
|             return Err(ParseIntValueError::WidthOverflow); | ||||
|         } | ||||
|         let expected_width = type_width.unwrap_or(width); | ||||
|         if type_is_signed != signed || expected_width != width { | ||||
|             let expected_width = type_width.unwrap_or(width); | ||||
|             return Err(ParseIntValueError::TypeMismatch { | ||||
|                 parsed_signed: signed, | ||||
|                 parsed_width: width, | ||||
|                 expected_signed: type_is_signed, | ||||
|                 expected_width, | ||||
|             }); | ||||
|         } | ||||
|         width | ||||
|     } else { | ||||
|         if !s.is_empty() { | ||||
|             return Err(ParseIntValueError::InvalidDigit); | ||||
|         } | ||||
|         type_width.expect("checked earlier") | ||||
|     }; | ||||
|     if !type_is_signed && negative { | ||||
|         return Err(ParseIntValueError::InvalidDigit); | ||||
|     } | ||||
|     if radix == 10 { | ||||
|         let mut value: BigInt = digits | ||||
|             .replace("_", "") | ||||
|             .parse() | ||||
|             .expect("checked that the digits are valid already"); | ||||
|         if negative { | ||||
|             value = -value; | ||||
|         } | ||||
|         let uint_value: UIntValue = UInt::new(width).from_bigint_wrapping(&value); | ||||
|         if value.is_zero() { | ||||
|             Ok(uint_value.into_bits()) | ||||
|         } else { | ||||
|             for i in 0..width { | ||||
|                 value.set_bit(i as u64, type_is_signed && negative); | ||||
|             } | ||||
|             if value.is_zero() { | ||||
|                 Ok(uint_value.into_bits()) | ||||
|             } else if type_is_signed && negative { | ||||
|                 if value.sign() == Sign::Minus && value.magnitude().is_one() { | ||||
|                     Ok(uint_value.into_bits()) | ||||
|                 } else { | ||||
|                     Err(ParseIntValueError::NegOverflow) | ||||
|                 } | ||||
|             } else { | ||||
|                 Err(ParseIntValueError::PosOverflow) | ||||
|             } | ||||
|         } | ||||
|     } else { | ||||
|         let mut value = BitVec::repeat(false, width); | ||||
|         let bits_per_digit = match radix { | ||||
|             2 => 1, | ||||
|             8 => 3, | ||||
|             16 => 4, | ||||
|             _ => unreachable!(), | ||||
|         }; | ||||
|         let mut digits = digits | ||||
|             .bytes() | ||||
|             .rev() | ||||
|             .filter_map(|ch| (ch as char).to_digit(radix)); | ||||
|         let overflow_error = if negative { | ||||
|             ParseIntValueError::NegOverflow | ||||
|         } else { | ||||
|             ParseIntValueError::PosOverflow | ||||
|         }; | ||||
|         for chunk in value.chunks_mut(bits_per_digit) { | ||||
|             if let Some(mut digit) = digits.next() { | ||||
|                 let digit_bits = &mut digit.view_bits_mut::<Lsb0>()[..chunk.len()]; | ||||
|                 chunk.clone_from_bitslice(digit_bits); | ||||
|                 digit_bits.fill(false); | ||||
|                 if digit != 0 { | ||||
|                     return Err(overflow_error); | ||||
|                 } | ||||
|             } else { | ||||
|                 break; | ||||
|             } | ||||
|         } | ||||
|         for digit in digits { | ||||
|             if digit != 0 { | ||||
|                 return Err(overflow_error); | ||||
|             } | ||||
|         } | ||||
|         let negative_zero = if negative { | ||||
|             // negating a value happens in three regions:
 | ||||
|             // * the least-significant zeros, which are left as zeros
 | ||||
|             // * the least-significant one bit, which is left as a one bit
 | ||||
|             // * all the most-significant bits, which are inverted
 | ||||
|             // e.g.:
 | ||||
|             const { | ||||
|                 let inp = 0b1010_1_000_u8; | ||||
|                 let out = 0b0101_1_000_u8; | ||||
|                 assert!(inp.wrapping_neg() == out); | ||||
|             }; | ||||
|             if let Some(first_one) = value.first_one() { | ||||
|                 let most_significant_bits = &mut value[first_one + 1..]; | ||||
|                 // modifies in-place despite using `Not::not`
 | ||||
|                 let _ = Not::not(most_significant_bits); | ||||
|                 false | ||||
|             } else { | ||||
|                 true | ||||
|             } | ||||
|         } else { | ||||
|             false | ||||
|         }; | ||||
|         if !negative_zero && type_is_signed && negative != value[value.len() - 1] { | ||||
|             Err(overflow_error) | ||||
|         } else { | ||||
|             Ok(Arc::new(value)) | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| fn deserialize_int_value<'de, D: Deserializer<'de>>( | ||||
|     deserializer: D, | ||||
|     type_is_signed: bool, | ||||
|     type_width: Option<usize>, | ||||
| ) -> Result<Arc<BitVec>, D::Error> { | ||||
|     struct IntValueVisitor { | ||||
|         type_is_signed: bool, | ||||
|         type_width: Option<usize>, | ||||
|     } | ||||
|     impl<'de> Visitor<'de> for IntValueVisitor { | ||||
|         type Value = Arc<BitVec>; | ||||
| 
 | ||||
|         fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||||
|             f.write_str(if self.type_is_signed { | ||||
|                 "SIntValue" | ||||
|             } else { | ||||
|                 "UIntValue" | ||||
|             })?; | ||||
|             if let Some(type_width) = self.type_width { | ||||
|                 write!(f, "<{type_width}>")?; | ||||
|             } | ||||
|             Ok(()) | ||||
|         } | ||||
| 
 | ||||
|         fn visit_str<E: Error>(self, v: &str) -> Result<Self::Value, E> { | ||||
|             parse_int_value(v, self.type_is_signed, self.type_width, true).map_err(E::custom) | ||||
|         } | ||||
| 
 | ||||
|         fn visit_bytes<E: Error>(self, v: &[u8]) -> Result<Self::Value, E> { | ||||
|             match std::str::from_utf8(v) { | ||||
|                 Ok(v) => self.visit_str(v), | ||||
|                 Err(_) => Err(Error::invalid_value(serde::de::Unexpected::Bytes(v), &self)), | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|     deserializer.deserialize_str(IntValueVisitor { | ||||
|         type_is_signed, | ||||
|         type_width, | ||||
|     }) | ||||
| } | ||||
| 
 | ||||
| macro_rules! impl_int { | ||||
|     ($pretty_name:ident, $name:ident, $generic_name:ident, $value:ident, $SIGNED:literal) => { | ||||
|         #[derive(Copy, Clone, PartialEq, Eq, Hash)] | ||||
|  | @ -188,19 +554,14 @@ macro_rules! impl_int { | |||
|         pub const $name: $generic_name = $generic_name; | ||||
| 
 | ||||
|         impl<Width: Size> $name<Width> { | ||||
|             pub fn new(width: Width::SizeType) -> Self { | ||||
|             pub const fn new(width: Width::SizeType) -> Self { | ||||
|                 Self { width } | ||||
|             } | ||||
|             pub fn width(self) -> usize { | ||||
|                 Width::as_usize(self.width) | ||||
|             } | ||||
|             pub fn type_properties(self) -> TypeProperties { | ||||
|                 TypeProperties { | ||||
|                     is_passive: true, | ||||
|                     is_storable: true, | ||||
|                     is_castable_from_bits: true, | ||||
|                     bit_width: self.width(), | ||||
|                 } | ||||
|                 self.as_dyn_int().type_properties_dyn() | ||||
|             } | ||||
|             pub fn bits_from_bigint_wrapping(self, v: &BigInt) -> BitVec { | ||||
|                 BoolOrIntType::bits_from_bigint_wrapping(self, v) | ||||
|  | @ -263,6 +624,12 @@ macro_rules! impl_int { | |||
|                 } | ||||
|                 Expr::from_dyn_int(MemoizeBitsToExpr.get_cow(bits)) | ||||
|             } | ||||
|             fn from_str_without_ty( | ||||
|                 self, | ||||
|                 s: &str, | ||||
|             ) -> Result<Self::Value, <Self::Value as FromStr>::Err> { | ||||
|                 parse_int_value(s, $SIGNED, Some(self.width()), false).map(Self::Value::new) | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         impl<Width: Size> IntType for $name<Width> { | ||||
|  | @ -270,12 +637,20 @@ macro_rules! impl_int { | |||
|         } | ||||
| 
 | ||||
|         impl $name { | ||||
|             pub fn new_dyn(width: usize) -> Self { | ||||
|             pub const fn new_dyn(width: usize) -> Self { | ||||
|                 Self { width } | ||||
|             } | ||||
|             pub fn bits_to_bigint(bits: &BitSlice) -> BigInt { | ||||
|                 <Self as BoolOrIntType>::bits_to_bigint(bits) | ||||
|             } | ||||
|             pub const fn type_properties_dyn(self) -> TypeProperties { | ||||
|                 TypeProperties { | ||||
|                     is_passive: true, | ||||
|                     is_storable: true, | ||||
|                     is_castable_from_bits: true, | ||||
|                     bit_width: self.width, | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         impl<Width: KnownSize> $name<Width> { | ||||
|  | @ -287,6 +662,7 @@ macro_rules! impl_int { | |||
|         impl<Width: Size> Type for $name<Width> { | ||||
|             type BaseType = $pretty_name; | ||||
|             type MaskType = Bool; | ||||
|             type SimValue = $value<Width>; | ||||
|             impl_match_variant_as_self!(); | ||||
|             fn mask_type(&self) -> Self::MaskType { | ||||
|                 Bool | ||||
|  | @ -297,7 +673,7 @@ macro_rules! impl_int { | |||
|             #[track_caller] | ||||
|             fn from_canonical(canonical_type: CanonicalType) -> Self { | ||||
|                 let CanonicalType::$pretty_name(retval) = canonical_type else { | ||||
|                     panic!("expected {}", stringify!($name)); | ||||
|                     panic!("expected {}", stringify!($pretty_name)); | ||||
|                 }; | ||||
|                 $name { | ||||
|                     width: Width::from_usize(retval.width), | ||||
|  | @ -306,20 +682,78 @@ macro_rules! impl_int { | |||
|             fn source_location() -> SourceLocation { | ||||
|                 SourceLocation::builtin() | ||||
|             } | ||||
|             fn sim_value_from_bits(&self, bits: &BitSlice) -> Self::SimValue { | ||||
|                 assert_eq!(bits.len(), self.width()); | ||||
|                 $value::new(Arc::new(bits.to_bitvec())) | ||||
|             } | ||||
|             fn sim_value_clone_from_bits(&self, value: &mut Self::SimValue, bits: &BitSlice) { | ||||
|                 assert_eq!(bits.len(), self.width()); | ||||
|                 assert_eq!(value.width(), self.width()); | ||||
|                 value.bits_mut().copy_from_bitslice(bits); | ||||
|             } | ||||
|             fn sim_value_to_bits(&self, value: &Self::SimValue, bits: &mut BitSlice) { | ||||
|                 assert_eq!(bits.len(), self.width()); | ||||
|                 assert_eq!(value.width(), self.width()); | ||||
|                 bits.copy_from_bitslice(value.bits()); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         impl<Width: KnownSize> Default for $name<Width> { | ||||
|             fn default() -> Self { | ||||
|                 Self::TYPE | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         impl<Width: KnownSize> StaticType for $name<Width> { | ||||
|             const TYPE: Self = Self { width: Width::SIZE }; | ||||
|             const MASK_TYPE: Self::MaskType = Bool; | ||||
|             const TYPE_PROPERTIES: TypeProperties = TypeProperties { | ||||
|                 is_passive: true, | ||||
|                 is_storable: true, | ||||
|                 is_castable_from_bits: true, | ||||
|                 bit_width: Width::VALUE, | ||||
|             }; | ||||
|             const TYPE_PROPERTIES: TypeProperties = $name { | ||||
|                 width: Width::VALUE, | ||||
|             } | ||||
|             .type_properties_dyn(); | ||||
|             const MASK_TYPE_PROPERTIES: TypeProperties = Bool::TYPE_PROPERTIES; | ||||
|         } | ||||
| 
 | ||||
|         impl<Width: Size> Serialize for $name<Width> { | ||||
|             fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> | ||||
|             where | ||||
|                 S: Serializer, | ||||
|             { | ||||
|                 self.canonical().serialize(serializer) | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         impl<'de, Width: Size> Deserialize<'de> for $name<Width> { | ||||
|             fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> | ||||
|             where | ||||
|                 D: Deserializer<'de>, | ||||
|             { | ||||
|                 let name = |width| -> String { | ||||
|                     if let Some(width) = width { | ||||
|                         format!("a {}<{width}>", stringify!($pretty_name)) | ||||
|                     } else { | ||||
|                         format!("a {}", stringify!($pretty_name)) | ||||
|                     } | ||||
|                 }; | ||||
|                 match CanonicalType::deserialize(deserializer)? { | ||||
|                     CanonicalType::$pretty_name(retval) => { | ||||
|                         if let Some(width) = Width::try_from_usize(retval.width()) { | ||||
|                             Ok($name { width }) | ||||
|                         } else { | ||||
|                             Err(Error::invalid_value( | ||||
|                                 serde::de::Unexpected::Other(&name(Some(retval.width()))), | ||||
|                                 &&*name(Width::KNOWN_VALUE), | ||||
|                             )) | ||||
|                         } | ||||
|                     } | ||||
|                     ty => Err(Error::invalid_value( | ||||
|                         serde::de::Unexpected::Other(ty.as_serde_unexpected_str()), | ||||
|                         &&*name(Width::KNOWN_VALUE), | ||||
|                     )), | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, Default)] | ||||
|         pub struct $generic_name; | ||||
| 
 | ||||
|  | @ -337,7 +771,7 @@ macro_rules! impl_int { | |||
|             _phantom: PhantomData<Width>, | ||||
|         } | ||||
| 
 | ||||
|         impl<Width: Size> fmt::Debug for $value<Width> { | ||||
|         impl<Width: Size> fmt::Display for $value<Width> { | ||||
|             fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||||
|                 let value = self.to_bigint(); | ||||
|                 let (sign, magnitude) = value.into_parts(); | ||||
|  | @ -351,15 +785,38 @@ macro_rules! impl_int { | |||
|             } | ||||
|         } | ||||
| 
 | ||||
|         impl<Width: Size> PartialOrd for $value<Width> { | ||||
|             fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> { | ||||
|                 Some(self.cmp(other)) | ||||
|         impl<Width: Size> fmt::Debug for $value<Width> { | ||||
|             fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||||
|                 fmt::Display::fmt(self, f) | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         impl<Width: Size> Ord for $value<Width> { | ||||
|             fn cmp(&self, other: &Self) -> std::cmp::Ordering { | ||||
|                 self.to_bigint().cmp(&other.to_bigint()) | ||||
|         impl<Width: Size> std::str::FromStr for $value<Width> { | ||||
|             type Err = ParseIntValueError; | ||||
| 
 | ||||
|             fn from_str(s: &str) -> Result<Self, Self::Err> { | ||||
|                 parse_int_value(s, $SIGNED, Width::KNOWN_VALUE, true).map(Self::new) | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         impl<Width: Size> Serialize for $value<Width> { | ||||
|             fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> { | ||||
|                 self.to_string().serialize(serializer) | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         impl<'de, Width: Size> Deserialize<'de> for $value<Width> { | ||||
|             fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> { | ||||
|                 deserialize_int_value(deserializer, $SIGNED, Width::KNOWN_VALUE).map(Self::new) | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         impl<Width: Size> PartialOrd for $value<Width> { | ||||
|             fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> { | ||||
|                 if self.width() != other.width() { | ||||
|                     return None; | ||||
|                 } | ||||
|                 Some(self.to_bigint().cmp(&other.to_bigint())) | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|  | @ -401,6 +858,9 @@ macro_rules! impl_int { | |||
|             pub fn bits(&self) -> &Arc<BitVec> { | ||||
|                 &self.bits | ||||
|             } | ||||
|             pub fn bits_mut(&mut self) -> &mut BitSlice { | ||||
|                 Arc::<BitVec>::make_mut(&mut self.bits) | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         impl<Width: Size> ToLiteralBits for $value<Width> { | ||||
|  | @ -455,6 +915,10 @@ impl UInt { | |||
|         let v: BigUint = v.into(); | ||||
|         Self::new(v.bits().try_into().expect("too big")) | ||||
|     } | ||||
|     /// gets the smallest `UInt` that fits `v` losslessly
 | ||||
|     pub const fn for_value_usize(v: usize) -> Self { | ||||
|         Self::new((usize::BITS - v.leading_zeros()) as usize) | ||||
|     } | ||||
|     /// gets the smallest `UInt` that fits `r` losslessly, panics if `r` is empty
 | ||||
|     #[track_caller] | ||||
|     pub fn range(r: Range<impl Into<BigUint>>) -> Self { | ||||
|  | @ -465,6 +929,12 @@ impl UInt { | |||
|     } | ||||
|     /// gets the smallest `UInt` that fits `r` losslessly, panics if `r` is empty
 | ||||
|     #[track_caller] | ||||
|     pub const fn range_usize(r: Range<usize>) -> Self { | ||||
|         assert!(r.end != 0, "empty range"); | ||||
|         Self::range_inclusive_usize(r.start..=(r.end - 1)) | ||||
|     } | ||||
|     /// gets the smallest `UInt` that fits `r` losslessly, panics if `r` is empty
 | ||||
|     #[track_caller] | ||||
|     pub fn range_inclusive(r: RangeInclusive<impl Into<BigUint>>) -> Self { | ||||
|         let (start, end) = r.into_inner(); | ||||
|         let start: BigUint = start.into(); | ||||
|  | @ -474,6 +944,16 @@ impl UInt { | |||
|         // so must not take more bits than `end`
 | ||||
|         Self::for_value(end) | ||||
|     } | ||||
|     /// gets the smallest `UInt` that fits `r` losslessly, panics if `r` is empty
 | ||||
|     #[track_caller] | ||||
|     pub const fn range_inclusive_usize(r: RangeInclusive<usize>) -> Self { | ||||
|         let start = *r.start(); | ||||
|         let end = *r.end(); | ||||
|         assert!(start <= end, "empty range"); | ||||
|         // no need to check `start`` since it's no larger than `end`
 | ||||
|         // so must not take more bits than `end`
 | ||||
|         Self::for_value_usize(end) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl SInt { | ||||
|  | @ -580,14 +1060,17 @@ pub trait BoolOrIntType: Type + sealed::BoolOrIntTypeSealed { | |||
|     type Width: Size; | ||||
|     type Signed: GenericConstBool; | ||||
|     type Value: Clone | ||||
|         + Ord | ||||
|         + PartialOrd | ||||
|         + Eq | ||||
|         + std::hash::Hash | ||||
|         + fmt::Debug | ||||
|         + fmt::Display | ||||
|         + Send | ||||
|         + Sync | ||||
|         + 'static | ||||
|         + ToExpr<Type = Self> | ||||
|         + Into<BigInt>; | ||||
|         + Into<BigInt> | ||||
|         + std::str::FromStr; | ||||
|     fn width(self) -> usize; | ||||
|     fn new(width: <Self::Width as Size>::SizeType) -> Self; | ||||
|     fn new_static() -> Self | ||||
|  | @ -621,6 +1104,12 @@ pub trait BoolOrIntType: Type + sealed::BoolOrIntTypeSealed { | |||
|         let bitslice = &BitSlice::<u8, Lsb0>::from_slice(&bytes)[..width]; | ||||
|         bits.clone_from_bitslice(bitslice); | ||||
|     } | ||||
|     fn bits_equal_bigint_wrapping(v: &BigInt, bits: &BitSlice) -> bool { | ||||
|         bits.iter() | ||||
|             .by_vals() | ||||
|             .enumerate() | ||||
|             .all(|(bit_index, bit): (usize, bool)| v.bit(bit_index as u64) == bit) | ||||
|     } | ||||
|     fn bits_to_bigint(bits: &BitSlice) -> BigInt { | ||||
|         let sign_byte = if Self::Signed::VALUE && bits.last().as_deref().copied().unwrap_or(false) { | ||||
|             0xFF | ||||
|  | @ -654,9 +1143,12 @@ pub trait BoolOrIntType: Type + sealed::BoolOrIntTypeSealed { | |||
|             bytes, bit_width, | ||||
|         ))) | ||||
|     } | ||||
|     fn from_str_without_ty(self, s: &str) -> Result<Self::Value, <Self::Value as FromStr>::Err>; | ||||
| } | ||||
| 
 | ||||
| pub trait IntType: BoolOrIntType<BaseType = <Self as IntType>::Dyn> { | ||||
| pub trait IntType: | ||||
|     BoolOrIntType<BaseType = <Self as IntType>::Dyn, Value: FromStr<Err = ParseIntValueError>> | ||||
| { | ||||
|     type Dyn: IntType<Dyn = Self::Dyn, Signed = Self::Signed, Width = DynSize>; | ||||
|     fn as_dyn_int(self) -> Self::Dyn { | ||||
|         Self::new_dyn(self.width()) | ||||
|  | @ -696,7 +1188,7 @@ pub trait IntType: BoolOrIntType<BaseType = <Self as IntType>::Dyn> { | |||
|     } | ||||
| } | ||||
| 
 | ||||
| #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] | ||||
| #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, Default)] | ||||
| pub struct Bool; | ||||
| 
 | ||||
| impl sealed::BoolOrIntTypeSealed for Bool {} | ||||
|  | @ -728,6 +1220,10 @@ impl BoolOrIntType for Bool { | |||
|         assert_eq!(bits.len(), 1); | ||||
|         bits[0] | ||||
|     } | ||||
| 
 | ||||
|     fn from_str_without_ty(self, s: &str) -> Result<Self::Value, <Self::Value as FromStr>::Err> { | ||||
|         FromStr::from_str(s) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl Bool { | ||||
|  | @ -742,6 +1238,7 @@ impl Bool { | |||
| impl Type for Bool { | ||||
|     type BaseType = Bool; | ||||
|     type MaskType = Bool; | ||||
|     type SimValue = bool; | ||||
|     impl_match_variant_as_self!(); | ||||
|     fn mask_type(&self) -> Self::MaskType { | ||||
|         Bool | ||||
|  | @ -759,6 +1256,18 @@ impl Type for Bool { | |||
|     fn source_location() -> SourceLocation { | ||||
|         SourceLocation::builtin() | ||||
|     } | ||||
|     fn sim_value_from_bits(&self, bits: &BitSlice) -> Self::SimValue { | ||||
|         assert_eq!(bits.len(), 1); | ||||
|         bits[0] | ||||
|     } | ||||
|     fn sim_value_clone_from_bits(&self, value: &mut Self::SimValue, bits: &BitSlice) { | ||||
|         assert_eq!(bits.len(), 1); | ||||
|         *value = bits[0]; | ||||
|     } | ||||
|     fn sim_value_to_bits(&self, value: &Self::SimValue, bits: &mut BitSlice) { | ||||
|         assert_eq!(bits.len(), 1); | ||||
|         bits.set(0, *value); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl StaticType for Bool { | ||||
|  | @ -783,6 +1292,13 @@ impl ToLiteralBits for bool { | |||
| mod tests { | ||||
|     use super::*; | ||||
| 
 | ||||
|     #[test] | ||||
|     fn test_different_value_widths_compare_ne() { | ||||
|         // interning relies on [SU]IntValue with different `width` comparing not equal
 | ||||
|         assert_ne!(UInt[3].from_int_wrapping(0), UInt[4].from_int_wrapping(0)); | ||||
|         assert_ne!(SInt[3].from_int_wrapping(0), SInt[4].from_int_wrapping(0)); | ||||
|     } | ||||
| 
 | ||||
|     #[test] | ||||
|     fn test_uint_for_value() { | ||||
|         assert_eq!(UInt::for_value(0u8).width, 0); | ||||
|  | @ -805,4 +1321,104 @@ mod tests { | |||
|         assert_eq!(SInt::for_value(3).width, 3); | ||||
|         assert_eq!(SInt::for_value(4).width, 4); | ||||
|     } | ||||
| 
 | ||||
|     #[test] | ||||
|     fn test_serde_round_trip() { | ||||
|         use serde_json::json; | ||||
|         #[track_caller] | ||||
|         fn check<T: Serialize + DeserializeOwned + PartialEq + fmt::Debug>( | ||||
|             value: T, | ||||
|             expected: serde_json::Value, | ||||
|         ) { | ||||
|             assert_eq!(serde_json::to_value(&value).unwrap(), expected); | ||||
|             assert_eq!(value, T::deserialize(expected).unwrap()); | ||||
|         } | ||||
|         check(UInt[0], json! { { "UInt": { "width": 0 } } }); | ||||
|         check(UInt::<0>::TYPE, json! { { "UInt": { "width": 0 } } }); | ||||
|         check(UInt::<35>::TYPE, json! { { "UInt": { "width": 35 } } }); | ||||
|         check(SInt[0], json! { { "SInt": { "width": 0 } } }); | ||||
|         check(SInt::<0>::TYPE, json! { { "SInt": { "width": 0 } } }); | ||||
|         check(SInt::<35>::TYPE, json! { { "SInt": { "width": 35 } } }); | ||||
|         check(Bool, json! { "Bool" }); | ||||
|         check(UIntValue::from(0u8), json! { "0x0_u8" }); | ||||
|         check(SIntValue::from(-128i8), json! { "-0x80_i8" }); | ||||
|         check(UInt[3].from_int_wrapping(5), json! { "0x5_u3" }); | ||||
|         check(UInt[12].from_int_wrapping(0x1123), json! { "0x123_u12" }); | ||||
|         check(SInt[12].from_int_wrapping(0xFEE), json! { "-0x12_i12" }); | ||||
|         check(SInt[12].from_int_wrapping(0x7EE), json! { "0x7EE_i12" }); | ||||
|     } | ||||
| 
 | ||||
|     #[test] | ||||
|     fn test_deserialize() { | ||||
|         use serde_json::json; | ||||
|         #[track_caller] | ||||
|         fn check<T: DeserializeOwned + fmt::Debug + PartialEq>( | ||||
|             expected: Result<T, &str>, | ||||
|             input: serde_json::Value, | ||||
|         ) { | ||||
|             let mut error = String::new(); | ||||
|             let value = T::deserialize(input).map_err(|e| -> &str { | ||||
|                 error = e.to_string(); | ||||
|                 &error | ||||
|             }); | ||||
|             assert_eq!(value, expected); | ||||
|         } | ||||
|         check::<UInt<0>>( | ||||
|             Err("invalid value: a UInt<2>, expected a UInt<0>"), | ||||
|             json! { { "UInt": { "width": 2 } } }, | ||||
|         ); | ||||
|         check::<UInt<0>>( | ||||
|             Err("invalid value: a Bool, expected a UInt<0>"), | ||||
|             json! { "Bool" }, | ||||
|         ); | ||||
|         check::<SInt<0>>( | ||||
|             Err("invalid value: a Bool, expected a SInt<0>"), | ||||
|             json! { "Bool" }, | ||||
|         ); | ||||
|         check::<UInt>( | ||||
|             Err("invalid value: a Bool, expected a UInt"), | ||||
|             json! { "Bool" }, | ||||
|         ); | ||||
|         check::<SInt>( | ||||
|             Err("invalid value: a Bool, expected a SInt"), | ||||
|             json! { "Bool" }, | ||||
|         ); | ||||
|         check::<UIntValue>(Err("value too large to fit in type"), json! { "2_u1" }); | ||||
|         check::<UIntValue>(Err("value too large to fit in type"), json! { "10_u1" }); | ||||
|         check::<UIntValue>(Err("value too large to fit in type"), json! { "0x2_u1" }); | ||||
|         check::<UIntValue>(Err("value too large to fit in type"), json! { "0b10_u1" }); | ||||
|         check::<UIntValue>(Err("value too large to fit in type"), json! { "0o2_u1" }); | ||||
|         check::<SIntValue>(Err("value too large to fit in type"), json! { "0o377_i8" }); | ||||
|         check::<SIntValue>(Err("value too large to fit in type"), json! { "0o200_i8" }); | ||||
|         check(Ok(SInt[8].from_int_wrapping(i8::MAX)), json! { "0o177_i8" }); | ||||
|         check::<SIntValue>(Err("value too small to fit in type"), json! { "-0o201_i8" }); | ||||
|         check::<SIntValue>(Err("value too small to fit in type"), json! { "-0o377_i8" }); | ||||
|         check::<SIntValue>(Err("value too small to fit in type"), json! { "-0o400_i8" }); | ||||
|         check::<SIntValue>( | ||||
|             Err("value too small to fit in type"), | ||||
|             json! { "-0o4000_i8" }, | ||||
|         ); | ||||
|         check(Ok(UIntValue::from(0u8)), json! { "0_u8" }); | ||||
|         check(Ok(UIntValue::from(0u8)), json! { "0b0_u8" }); | ||||
|         check(Ok(UIntValue::from(0u8)), json! { "00_u8" }); | ||||
|         check(Ok(UIntValue::from(0u8)), json! { "0x0_u8" }); | ||||
|         check(Ok(UIntValue::from(0u8)), json! { "0o0_u8" }); | ||||
|         check(Ok(SIntValue::from(-128i8)), json! { "-0x000_80_i8" }); | ||||
|         check(Ok(SIntValue::from(-128i8)), json! { "-0o002_00_hdl_i8" }); | ||||
|         check(Ok(SIntValue::from(-128i8)), json! { "-0b1__000_0000_i8" }); | ||||
|         check(Ok(UInt[3].from_int_wrapping(5)), json! { " + 0x5_u3 " }); | ||||
|         check( | ||||
|             Ok(UInt[12].from_int_wrapping(0x1123)), | ||||
|             json! { "0x1_2_3_hdl_u12" }, | ||||
|         ); | ||||
|         check(Ok(SInt[12].from_int_wrapping(0xFEE)), json! { "-0x12_i12" }); | ||||
|         check( | ||||
|             Ok(SInt[12].from_int_wrapping(0x7EE)), | ||||
|             json! { " + \t0x7__E_e_i012\n" }, | ||||
|         ); | ||||
|         check(Ok(SInt[0].from_int_wrapping(0)), json! { "-0i0" }); | ||||
|         check(Ok(SInt[1].from_int_wrapping(0)), json! { "-0i1" }); | ||||
|         check(Ok(SInt[0].from_int_wrapping(0)), json! { "-0x0i0" }); | ||||
|         check(Ok(SInt[1].from_int_wrapping(0)), json! { "-0x0i1" }); | ||||
|     } | ||||
| } | ||||
|  |  | |||
							
								
								
									
										614
									
								
								crates/fayalite/src/int/uint_in_range.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										614
									
								
								crates/fayalite/src/int/uint_in_range.rs
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,614 @@ | |||
| // SPDX-License-Identifier: LGPL-3.0-or-later
 | ||||
| // See Notices.txt for copyright information
 | ||||
| 
 | ||||
| use crate::{ | ||||
|     bundle::{Bundle, BundleField, BundleType, BundleTypePropertiesBuilder, NoBuilder}, | ||||
|     expr::{ | ||||
|         ops::{ExprCastTo, ExprPartialEq, ExprPartialOrd}, | ||||
|         CastBitsTo, CastTo, CastToBits, Expr, HdlPartialEq, HdlPartialOrd, | ||||
|     }, | ||||
|     int::{Bool, DynSize, KnownSize, Size, SizeType, UInt, UIntType}, | ||||
|     intern::{Intern, Interned}, | ||||
|     phantom_const::PhantomConst, | ||||
|     sim::value::{SimValue, SimValuePartialEq, ToSimValueWithType}, | ||||
|     source_location::SourceLocation, | ||||
|     ty::{impl_match_variant_as_self, CanonicalType, StaticType, Type, TypeProperties}, | ||||
| }; | ||||
| use bitvec::{order::Lsb0, slice::BitSlice, view::BitView}; | ||||
| use serde::{ | ||||
|     de::{value::UsizeDeserializer, Error, Visitor}, | ||||
|     Deserialize, Deserializer, Serialize, Serializer, | ||||
| }; | ||||
| use std::{fmt, marker::PhantomData, ops::Index}; | ||||
| 
 | ||||
| const UINT_IN_RANGE_TYPE_FIELD_NAMES: [&'static str; 2] = ["value", "range"]; | ||||
| 
 | ||||
| #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, Default)] | ||||
| pub struct UIntInRangeMaskType { | ||||
|     value: Bool, | ||||
|     range: PhantomConstRangeMaskType, | ||||
| } | ||||
| 
 | ||||
| impl Type for UIntInRangeMaskType { | ||||
|     type BaseType = Bundle; | ||||
|     type MaskType = Self; | ||||
|     type SimValue = bool; | ||||
|     impl_match_variant_as_self!(); | ||||
| 
 | ||||
|     fn mask_type(&self) -> Self::MaskType { | ||||
|         *self | ||||
|     } | ||||
| 
 | ||||
|     fn canonical(&self) -> CanonicalType { | ||||
|         CanonicalType::Bundle(Bundle::new(self.fields())) | ||||
|     } | ||||
| 
 | ||||
|     fn from_canonical(canonical_type: CanonicalType) -> Self { | ||||
|         let fields = Bundle::from_canonical(canonical_type).fields(); | ||||
|         let [BundleField { | ||||
|             name: value_name, | ||||
|             flipped: false, | ||||
|             ty: value, | ||||
|         }, BundleField { | ||||
|             name: range_name, | ||||
|             flipped: false, | ||||
|             ty: range, | ||||
|         }] = *fields | ||||
|         else { | ||||
|             panic!("expected UIntInRangeMaskType"); | ||||
|         }; | ||||
|         assert_eq!([&*value_name, &*range_name], UINT_IN_RANGE_TYPE_FIELD_NAMES); | ||||
|         let value = Bool::from_canonical(value); | ||||
|         let range = PhantomConstRangeMaskType::from_canonical(range); | ||||
|         Self { value, range } | ||||
|     } | ||||
| 
 | ||||
|     fn source_location() -> SourceLocation { | ||||
|         SourceLocation::builtin() | ||||
|     } | ||||
| 
 | ||||
|     fn sim_value_from_bits(&self, bits: &BitSlice) -> Self::SimValue { | ||||
|         Bool.sim_value_from_bits(bits) | ||||
|     } | ||||
| 
 | ||||
|     fn sim_value_clone_from_bits(&self, value: &mut Self::SimValue, bits: &BitSlice) { | ||||
|         Bool.sim_value_clone_from_bits(value, bits); | ||||
|     } | ||||
| 
 | ||||
|     fn sim_value_to_bits(&self, value: &Self::SimValue, bits: &mut BitSlice) { | ||||
|         Bool.sim_value_to_bits(value, bits); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl BundleType for UIntInRangeMaskType { | ||||
|     type Builder = NoBuilder; | ||||
|     type FilledBuilder = Expr<UIntInRangeMaskType>; | ||||
| 
 | ||||
|     fn fields(&self) -> Interned<[BundleField]> { | ||||
|         let [value_name, range_name] = UINT_IN_RANGE_TYPE_FIELD_NAMES; | ||||
|         let Self { value, range } = self; | ||||
|         [ | ||||
|             BundleField { | ||||
|                 name: value_name.intern(), | ||||
|                 flipped: false, | ||||
|                 ty: value.canonical(), | ||||
|             }, | ||||
|             BundleField { | ||||
|                 name: range_name.intern(), | ||||
|                 flipped: false, | ||||
|                 ty: range.canonical(), | ||||
|             }, | ||||
|         ][..] | ||||
|             .intern() | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl StaticType for UIntInRangeMaskType { | ||||
|     const TYPE: Self = Self { | ||||
|         value: Bool, | ||||
|         range: PhantomConstRangeMaskType::TYPE, | ||||
|     }; | ||||
|     const MASK_TYPE: Self::MaskType = Self::TYPE; | ||||
|     const TYPE_PROPERTIES: TypeProperties = BundleTypePropertiesBuilder::new() | ||||
|         .field(false, Bool::TYPE_PROPERTIES) | ||||
|         .field(false, PhantomConstRangeMaskType::TYPE_PROPERTIES) | ||||
|         .finish(); | ||||
|     const MASK_TYPE_PROPERTIES: TypeProperties = Self::TYPE_PROPERTIES; | ||||
| } | ||||
| 
 | ||||
| impl ToSimValueWithType<UIntInRangeMaskType> for bool { | ||||
|     fn to_sim_value_with_type(&self, ty: UIntInRangeMaskType) -> SimValue<UIntInRangeMaskType> { | ||||
|         SimValue::from_value(ty, *self) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl ExprCastTo<Bool> for UIntInRangeMaskType { | ||||
|     fn cast_to(src: Expr<Self>, to_type: Bool) -> Expr<Bool> { | ||||
|         src.cast_to_bits().cast_to(to_type) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl ExprCastTo<UIntInRangeMaskType> for Bool { | ||||
|     fn cast_to(src: Expr<Self>, to_type: UIntInRangeMaskType) -> Expr<UIntInRangeMaskType> { | ||||
|         src.cast_to_static::<UInt<1>>().cast_bits_to(to_type) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl ExprPartialEq<Self> for UIntInRangeMaskType { | ||||
|     fn cmp_eq(lhs: Expr<Self>, rhs: Expr<Self>) -> Expr<Bool> { | ||||
|         lhs.cast_to_bits().cmp_eq(rhs.cast_to_bits()) | ||||
|     } | ||||
|     fn cmp_ne(lhs: Expr<Self>, rhs: Expr<Self>) -> Expr<Bool> { | ||||
|         lhs.cast_to_bits().cmp_ne(rhs.cast_to_bits()) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl SimValuePartialEq<Self> for UIntInRangeMaskType { | ||||
|     fn sim_value_eq(this: &SimValue<Self>, other: &SimValue<Self>) -> bool { | ||||
|         **this == **other | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| type PhantomConstRangeMaskType = <PhantomConst<SerdeRange<DynSize, DynSize>> as Type>::MaskType; | ||||
| 
 | ||||
| #[derive(Default, Copy, Clone, Debug)] | ||||
| struct RangeParseError; | ||||
| 
 | ||||
| macro_rules! define_uint_in_range_type { | ||||
|     ( | ||||
|         $UIntInRange:ident, | ||||
|         $UIntInRangeType:ident, | ||||
|         $UIntInRangeTypeWithoutGenerics:ident, | ||||
|         $UIntInRangeTypeWithStart:ident, | ||||
|         $SerdeRange:ident, | ||||
|         $range_operator_str:literal, | ||||
|         |$uint_range_usize_start:ident, $uint_range_usize_end:ident| $uint_range_usize:expr, | ||||
|     ) => { | ||||
|         #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] | ||||
|         struct $SerdeRange<Start: Size, End: Size> { | ||||
|             start: Start::SizeType, | ||||
|             end: End::SizeType, | ||||
|         } | ||||
| 
 | ||||
|         impl<Start: KnownSize, End: KnownSize> Default for $SerdeRange<Start, End> { | ||||
|             fn default() -> Self { | ||||
|                 Self { | ||||
|                     start: Start::SIZE, | ||||
|                     end: End::SIZE, | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         impl std::str::FromStr for $SerdeRange<DynSize, DynSize> { | ||||
|             type Err = RangeParseError; | ||||
| 
 | ||||
|             fn from_str(s: &str) -> Result<Self, Self::Err> { | ||||
|                 let Some((start, end)) = s.split_once($range_operator_str) else { | ||||
|                     return Err(RangeParseError); | ||||
|                 }; | ||||
|                 if start.is_empty() | ||||
|                     || start.bytes().any(|b| !b.is_ascii_digit()) | ||||
|                     || end.is_empty() | ||||
|                     || end.bytes().any(|b| !b.is_ascii_digit()) | ||||
|                 { | ||||
|                     return Err(RangeParseError); | ||||
|                 } | ||||
|                 let start = start.parse().map_err(|_| RangeParseError)?; | ||||
|                 let end = end.parse().map_err(|_| RangeParseError)?; | ||||
|                 let retval = Self { start, end }; | ||||
|                 if retval.is_empty() { | ||||
|                     Err(RangeParseError) | ||||
|                 } else { | ||||
|                     Ok(retval) | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         impl<Start: Size, End: Size> fmt::Display for $SerdeRange<Start, End> { | ||||
|             fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||||
|                 let Self { start, end } = *self; | ||||
|                 write!( | ||||
|                     f, | ||||
|                     "{}{}{}", | ||||
|                     Start::as_usize(start), | ||||
|                     $range_operator_str, | ||||
|                     End::as_usize(end), | ||||
|                 ) | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         impl<Start: Size, End: Size> Serialize for $SerdeRange<Start, End> { | ||||
|             fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> { | ||||
|                 serializer.collect_str(self) | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         impl<'de, Start: Size, End: Size> Deserialize<'de> for $SerdeRange<Start, End> { | ||||
|             fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> { | ||||
|                 struct SerdeRangeVisitor<Start: Size, End: Size>(PhantomData<(Start, End)>); | ||||
|                 impl<'de, Start: Size, End: Size> Visitor<'de> for SerdeRangeVisitor<Start, End> { | ||||
|                     type Value = $SerdeRange<Start, End>; | ||||
| 
 | ||||
|                     fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||||
|                         f.write_str("a string with format \"")?; | ||||
|                         if let Some(start) = Start::KNOWN_VALUE { | ||||
|                             write!(f, "{start}")?; | ||||
|                         } else { | ||||
|                             f.write_str("<int>")?; | ||||
|                         }; | ||||
|                         f.write_str($range_operator_str)?; | ||||
|                         if let Some(end) = End::KNOWN_VALUE { | ||||
|                             write!(f, "{end}")?; | ||||
|                         } else { | ||||
|                             f.write_str("<int>")?; | ||||
|                         }; | ||||
|                         f.write_str("\" that is a non-empty range") | ||||
|                     } | ||||
| 
 | ||||
|                     fn visit_str<E: Error>(self, v: &str) -> Result<Self::Value, E> { | ||||
|                         let $SerdeRange::<DynSize, DynSize> { start, end } = | ||||
|                             v.parse().map_err(|_| { | ||||
|                                 Error::invalid_value(serde::de::Unexpected::Str(v), &self) | ||||
|                             })?; | ||||
|                         let start = | ||||
|                             Start::SizeType::deserialize(UsizeDeserializer::<E>::new(start))?; | ||||
|                         let end = End::SizeType::deserialize(UsizeDeserializer::<E>::new(end))?; | ||||
|                         Ok($SerdeRange { start, end }) | ||||
|                     } | ||||
| 
 | ||||
|                     fn visit_bytes<E: Error>(self, v: &[u8]) -> Result<Self::Value, E> { | ||||
|                         match std::str::from_utf8(v) { | ||||
|                             Ok(v) => self.visit_str(v), | ||||
|                             Err(_) => { | ||||
|                                 Err(Error::invalid_value(serde::de::Unexpected::Bytes(v), &self)) | ||||
|                             } | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|                 deserializer.deserialize_str(SerdeRangeVisitor(PhantomData)) | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         #[derive(Copy, Clone, PartialEq, Eq, Hash)] | ||||
|         pub struct $UIntInRangeType<Start: Size, End: Size> { | ||||
|             value: UInt, | ||||
|             range: PhantomConst<$SerdeRange<Start, End>>, | ||||
|         } | ||||
| 
 | ||||
|         impl<Start: Size, End: Size> $UIntInRangeType<Start, End> { | ||||
|             fn from_phantom_const_range(range: PhantomConst<$SerdeRange<Start, End>>) -> Self { | ||||
|                 let $SerdeRange { start, end } = *range.get(); | ||||
|                 let $uint_range_usize_start = Start::as_usize(start); | ||||
|                 let $uint_range_usize_end = End::as_usize(end); | ||||
|                 Self { | ||||
|                     value: $uint_range_usize, | ||||
|                     range, | ||||
|                 } | ||||
|             } | ||||
|             pub fn new(start: Start::SizeType, end: End::SizeType) -> Self { | ||||
|                 Self::from_phantom_const_range(PhantomConst::new( | ||||
|                     $SerdeRange { start, end }.intern_sized(), | ||||
|                 )) | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         impl<Start: Size, End: Size> fmt::Debug for $UIntInRangeType<Start, End> { | ||||
|             fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||||
|                 let Self { value, range } = self; | ||||
|                 let $SerdeRange { start, end } = *range.get(); | ||||
|                 f.debug_struct(&format!( | ||||
|                     "{}<{}, {}>", | ||||
|                     stringify!($UIntInRange), | ||||
|                     Start::as_usize(start), | ||||
|                     End::as_usize(end), | ||||
|                 )) | ||||
|                 .field("value", value) | ||||
|                 .finish_non_exhaustive() | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         impl<Start: Size, End: Size> Type for $UIntInRangeType<Start, End> { | ||||
|             type BaseType = Bundle; | ||||
|             type MaskType = UIntInRangeMaskType; | ||||
|             type SimValue = usize; | ||||
|             impl_match_variant_as_self!(); | ||||
| 
 | ||||
|             fn mask_type(&self) -> Self::MaskType { | ||||
|                 UIntInRangeMaskType::TYPE | ||||
|             } | ||||
| 
 | ||||
|             fn canonical(&self) -> CanonicalType { | ||||
|                 CanonicalType::Bundle(Bundle::new(self.fields())) | ||||
|             } | ||||
| 
 | ||||
|             fn from_canonical(canonical_type: CanonicalType) -> Self { | ||||
|                 let fields = Bundle::from_canonical(canonical_type).fields(); | ||||
|                 let [BundleField { | ||||
|                     name: value_name, | ||||
|                     flipped: false, | ||||
|                     ty: value, | ||||
|                 }, BundleField { | ||||
|                     name: range_name, | ||||
|                     flipped: false, | ||||
|                     ty: range, | ||||
|                 }] = *fields | ||||
|                 else { | ||||
|                     panic!("expected {}", stringify!($UIntInRange)); | ||||
|                 }; | ||||
|                 assert_eq!([&*value_name, &*range_name], UINT_IN_RANGE_TYPE_FIELD_NAMES); | ||||
|                 let value = UInt::from_canonical(value); | ||||
|                 let range = PhantomConst::<$SerdeRange<Start, End>>::from_canonical(range); | ||||
|                 let retval = Self::from_phantom_const_range(range); | ||||
|                 assert_eq!(retval, Self { value, range }); | ||||
|                 retval | ||||
|             } | ||||
| 
 | ||||
|             fn source_location() -> SourceLocation { | ||||
|                 SourceLocation::builtin() | ||||
|             } | ||||
| 
 | ||||
|             fn sim_value_from_bits(&self, bits: &BitSlice) -> Self::SimValue { | ||||
|                 let mut retval = 0usize; | ||||
|                 retval.view_bits_mut::<Lsb0>()[..bits.len()].clone_from_bitslice(bits); | ||||
|                 retval | ||||
|             } | ||||
| 
 | ||||
|             fn sim_value_clone_from_bits(&self, value: &mut Self::SimValue, bits: &BitSlice) { | ||||
|                 *value = self.sim_value_from_bits(bits); | ||||
|             } | ||||
| 
 | ||||
|             fn sim_value_to_bits(&self, value: &Self::SimValue, bits: &mut BitSlice) { | ||||
|                 bits.clone_from_bitslice(&value.view_bits::<Lsb0>()[..bits.len()]); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         impl<Start: Size, End: Size> BundleType for $UIntInRangeType<Start, End> { | ||||
|             type Builder = NoBuilder; | ||||
|             type FilledBuilder = Expr<Self>; | ||||
| 
 | ||||
|             fn fields(&self) -> Interned<[BundleField]> { | ||||
|                 let [value_name, range_name] = UINT_IN_RANGE_TYPE_FIELD_NAMES; | ||||
|                 let Self { value, range } = self; | ||||
|                 [ | ||||
|                     BundleField { | ||||
|                         name: value_name.intern(), | ||||
|                         flipped: false, | ||||
|                         ty: value.canonical(), | ||||
|                     }, | ||||
|                     BundleField { | ||||
|                         name: range_name.intern(), | ||||
|                         flipped: false, | ||||
|                         ty: range.canonical(), | ||||
|                     }, | ||||
|                 ][..] | ||||
|                     .intern() | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         impl<Start: KnownSize, End: KnownSize> Default for $UIntInRangeType<Start, End> { | ||||
|             fn default() -> Self { | ||||
|                 Self::TYPE | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         impl<Start: KnownSize, End: KnownSize> StaticType for $UIntInRangeType<Start, End> { | ||||
|             const TYPE: Self = { | ||||
|                 let $uint_range_usize_start = Start::VALUE; | ||||
|                 let $uint_range_usize_end = End::VALUE; | ||||
|                 Self { | ||||
|                     value: $uint_range_usize, | ||||
|                     range: PhantomConst::<$SerdeRange<Start, End>>::TYPE, | ||||
|                 } | ||||
|             }; | ||||
|             const MASK_TYPE: Self::MaskType = UIntInRangeMaskType::TYPE; | ||||
|             const TYPE_PROPERTIES: TypeProperties = BundleTypePropertiesBuilder::new() | ||||
|                 .field(false, Self::TYPE.value.type_properties_dyn()) | ||||
|                 .field( | ||||
|                     false, | ||||
|                     PhantomConst::<$SerdeRange<Start, End>>::TYPE_PROPERTIES, | ||||
|                 ) | ||||
|                 .finish(); | ||||
|             const MASK_TYPE_PROPERTIES: TypeProperties = UIntInRangeMaskType::TYPE_PROPERTIES; | ||||
|         } | ||||
| 
 | ||||
|         impl<Start: Size, End: Size> ToSimValueWithType<$UIntInRangeType<Start, End>> for usize { | ||||
|             fn to_sim_value_with_type( | ||||
|                 &self, | ||||
|                 ty: $UIntInRangeType<Start, End>, | ||||
|             ) -> SimValue<$UIntInRangeType<Start, End>> { | ||||
|                 SimValue::from_value(ty, *self) | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, Default)] | ||||
|         pub struct $UIntInRangeTypeWithoutGenerics; | ||||
| 
 | ||||
|         #[allow(non_upper_case_globals)] | ||||
|         pub const $UIntInRangeType: $UIntInRangeTypeWithoutGenerics = | ||||
|             $UIntInRangeTypeWithoutGenerics; | ||||
| 
 | ||||
|         impl<StartSize: SizeType> Index<StartSize> for $UIntInRangeTypeWithoutGenerics { | ||||
|             type Output = $UIntInRangeTypeWithStart<StartSize::Size>; | ||||
| 
 | ||||
|             fn index(&self, start: StartSize) -> &Self::Output { | ||||
|                 Interned::into_inner($UIntInRangeTypeWithStart(start).intern_sized()) | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] | ||||
|         pub struct $UIntInRangeTypeWithStart<Start: Size>(Start::SizeType); | ||||
| 
 | ||||
|         impl<Start: Size, EndSize: SizeType<Size = End>, End: Size<SizeType = EndSize>> | ||||
|             Index<EndSize> for $UIntInRangeTypeWithStart<Start> | ||||
|         { | ||||
|             type Output = $UIntInRangeType<Start, End>; | ||||
| 
 | ||||
|             fn index(&self, end: EndSize) -> &Self::Output { | ||||
|                 Interned::into_inner($UIntInRangeType::new(self.0, end).intern_sized()) | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         impl<Start: Size, End: Size> ExprCastTo<UInt> for $UIntInRangeType<Start, End> { | ||||
|             fn cast_to(src: Expr<Self>, to_type: UInt) -> Expr<UInt> { | ||||
|                 src.cast_to_bits().cast_to(to_type) | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         impl<Start: Size, End: Size> ExprCastTo<$UIntInRangeType<Start, End>> for UInt { | ||||
|             fn cast_to( | ||||
|                 src: Expr<Self>, | ||||
|                 to_type: $UIntInRangeType<Start, End>, | ||||
|             ) -> Expr<$UIntInRangeType<Start, End>> { | ||||
|                 src.cast_bits_to(to_type) | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         impl<LhsStart: Size, LhsEnd: Size, RhsStart: Size, RhsEnd: Size> | ||||
|             ExprPartialEq<$UIntInRangeType<RhsStart, RhsEnd>> | ||||
|             for $UIntInRangeType<LhsStart, LhsEnd> | ||||
|         { | ||||
|             fn cmp_eq( | ||||
|                 lhs: Expr<Self>, | ||||
|                 rhs: Expr<$UIntInRangeType<RhsStart, RhsEnd>>, | ||||
|             ) -> Expr<Bool> { | ||||
|                 lhs.cast_to_bits().cmp_eq(rhs.cast_to_bits()) | ||||
|             } | ||||
|             fn cmp_ne( | ||||
|                 lhs: Expr<Self>, | ||||
|                 rhs: Expr<$UIntInRangeType<RhsStart, RhsEnd>>, | ||||
|             ) -> Expr<Bool> { | ||||
|                 lhs.cast_to_bits().cmp_ne(rhs.cast_to_bits()) | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         impl<LhsStart: Size, LhsEnd: Size, RhsStart: Size, RhsEnd: Size> | ||||
|             ExprPartialOrd<$UIntInRangeType<RhsStart, RhsEnd>> | ||||
|             for $UIntInRangeType<LhsStart, LhsEnd> | ||||
|         { | ||||
|             fn cmp_lt( | ||||
|                 lhs: Expr<Self>, | ||||
|                 rhs: Expr<$UIntInRangeType<RhsStart, RhsEnd>>, | ||||
|             ) -> Expr<Bool> { | ||||
|                 lhs.cast_to_bits().cmp_lt(rhs.cast_to_bits()) | ||||
|             } | ||||
|             fn cmp_le( | ||||
|                 lhs: Expr<Self>, | ||||
|                 rhs: Expr<$UIntInRangeType<RhsStart, RhsEnd>>, | ||||
|             ) -> Expr<Bool> { | ||||
|                 lhs.cast_to_bits().cmp_le(rhs.cast_to_bits()) | ||||
|             } | ||||
|             fn cmp_gt( | ||||
|                 lhs: Expr<Self>, | ||||
|                 rhs: Expr<$UIntInRangeType<RhsStart, RhsEnd>>, | ||||
|             ) -> Expr<Bool> { | ||||
|                 lhs.cast_to_bits().cmp_gt(rhs.cast_to_bits()) | ||||
|             } | ||||
|             fn cmp_ge( | ||||
|                 lhs: Expr<Self>, | ||||
|                 rhs: Expr<$UIntInRangeType<RhsStart, RhsEnd>>, | ||||
|             ) -> Expr<Bool> { | ||||
|                 lhs.cast_to_bits().cmp_ge(rhs.cast_to_bits()) | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         impl<LhsStart: Size, LhsEnd: Size, RhsStart: Size, RhsEnd: Size> | ||||
|             SimValuePartialEq<$UIntInRangeType<RhsStart, RhsEnd>> | ||||
|             for $UIntInRangeType<LhsStart, LhsEnd> | ||||
|         { | ||||
|             fn sim_value_eq( | ||||
|                 this: &SimValue<Self>, | ||||
|                 other: &SimValue<$UIntInRangeType<RhsStart, RhsEnd>>, | ||||
|             ) -> bool { | ||||
|                 **this == **other | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         impl<Start: Size, End: Size, Width: Size> ExprPartialEq<UIntType<Width>> | ||||
|             for $UIntInRangeType<Start, End> | ||||
|         { | ||||
|             fn cmp_eq(lhs: Expr<Self>, rhs: Expr<UIntType<Width>>) -> Expr<Bool> { | ||||
|                 lhs.cast_to_bits().cmp_eq(rhs) | ||||
|             } | ||||
|             fn cmp_ne(lhs: Expr<Self>, rhs: Expr<UIntType<Width>>) -> Expr<Bool> { | ||||
|                 lhs.cast_to_bits().cmp_ne(rhs) | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         impl<Start: Size, End: Size, Width: Size> ExprPartialEq<$UIntInRangeType<Start, End>> | ||||
|             for UIntType<Width> | ||||
|         { | ||||
|             fn cmp_eq(lhs: Expr<Self>, rhs: Expr<$UIntInRangeType<Start, End>>) -> Expr<Bool> { | ||||
|                 lhs.cmp_eq(rhs.cast_to_bits()) | ||||
|             } | ||||
|             fn cmp_ne(lhs: Expr<Self>, rhs: Expr<$UIntInRangeType<Start, End>>) -> Expr<Bool> { | ||||
|                 lhs.cmp_ne(rhs.cast_to_bits()) | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         impl<Start: Size, End: Size, Width: Size> ExprPartialOrd<UIntType<Width>> | ||||
|             for $UIntInRangeType<Start, End> | ||||
|         { | ||||
|             fn cmp_lt(lhs: Expr<Self>, rhs: Expr<UIntType<Width>>) -> Expr<Bool> { | ||||
|                 lhs.cast_to_bits().cmp_lt(rhs) | ||||
|             } | ||||
|             fn cmp_le(lhs: Expr<Self>, rhs: Expr<UIntType<Width>>) -> Expr<Bool> { | ||||
|                 lhs.cast_to_bits().cmp_le(rhs) | ||||
|             } | ||||
|             fn cmp_gt(lhs: Expr<Self>, rhs: Expr<UIntType<Width>>) -> Expr<Bool> { | ||||
|                 lhs.cast_to_bits().cmp_gt(rhs) | ||||
|             } | ||||
|             fn cmp_ge(lhs: Expr<Self>, rhs: Expr<UIntType<Width>>) -> Expr<Bool> { | ||||
|                 lhs.cast_to_bits().cmp_ge(rhs) | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         impl<Start: Size, End: Size, Width: Size> ExprPartialOrd<$UIntInRangeType<Start, End>> | ||||
|             for UIntType<Width> | ||||
|         { | ||||
|             fn cmp_lt(lhs: Expr<Self>, rhs: Expr<$UIntInRangeType<Start, End>>) -> Expr<Bool> { | ||||
|                 lhs.cmp_lt(rhs.cast_to_bits()) | ||||
|             } | ||||
|             fn cmp_le(lhs: Expr<Self>, rhs: Expr<$UIntInRangeType<Start, End>>) -> Expr<Bool> { | ||||
|                 lhs.cmp_le(rhs.cast_to_bits()) | ||||
|             } | ||||
|             fn cmp_gt(lhs: Expr<Self>, rhs: Expr<$UIntInRangeType<Start, End>>) -> Expr<Bool> { | ||||
|                 lhs.cmp_gt(rhs.cast_to_bits()) | ||||
|             } | ||||
|             fn cmp_ge(lhs: Expr<Self>, rhs: Expr<$UIntInRangeType<Start, End>>) -> Expr<Bool> { | ||||
|                 lhs.cmp_ge(rhs.cast_to_bits()) | ||||
|             } | ||||
|         } | ||||
|     }; | ||||
| } | ||||
| 
 | ||||
| define_uint_in_range_type! { | ||||
|     UIntInRange, | ||||
|     UIntInRangeType, | ||||
|     UIntInRangeTypeWithoutGenerics, | ||||
|     UIntInRangeTypeWithStart, | ||||
|     SerdeRange, | ||||
|     "..", | ||||
|     |start, end| UInt::range_usize(start..end), | ||||
| } | ||||
| 
 | ||||
| define_uint_in_range_type! { | ||||
|     UIntInRangeInclusive, | ||||
|     UIntInRangeInclusiveType, | ||||
|     UIntInRangeInclusiveTypeWithoutGenerics, | ||||
|     UIntInRangeInclusiveTypeWithStart, | ||||
|     SerdeRangeInclusive, | ||||
|     "..=", | ||||
|     |start, end| UInt::range_inclusive_usize(start..=end), | ||||
| } | ||||
| 
 | ||||
| impl SerdeRange<DynSize, DynSize> { | ||||
|     fn is_empty(self) -> bool { | ||||
|         self.start >= self.end | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl SerdeRangeInclusive<DynSize, DynSize> { | ||||
|     fn is_empty(self) -> bool { | ||||
|         self.start > self.end | ||||
|     } | ||||
| } | ||||
|  | @ -1,9 +1,9 @@ | |||
| // SPDX-License-Identifier: LGPL-3.0-or-later
 | ||||
| // See Notices.txt for copyright information
 | ||||
| #![allow(clippy::type_complexity)] | ||||
| use crate::intern::type_map::TypeIdMap; | ||||
| use crate::{intern::type_map::TypeIdMap, util::DefaultBuildHasher}; | ||||
| use bitvec::{ptr::BitPtr, slice::BitSlice, vec::BitVec}; | ||||
| use hashbrown::{hash_map::RawEntryMut, HashMap, HashTable}; | ||||
| use hashbrown::HashTable; | ||||
| use serde::{Deserialize, Serialize}; | ||||
| use std::{ | ||||
|     any::{Any, TypeId}, | ||||
|  | @ -17,7 +17,7 @@ use std::{ | |||
|     sync::{Mutex, RwLock}, | ||||
| }; | ||||
| 
 | ||||
| pub mod type_map; | ||||
| mod type_map; | ||||
| 
 | ||||
| pub trait LazyInternedTrait<T: ?Sized + Send + Sync + 'static>: Send + Sync + Any { | ||||
|     fn get(&self) -> Interned<T>; | ||||
|  | @ -316,8 +316,13 @@ pub trait Intern: Any + Send + Sync { | |||
|     } | ||||
| } | ||||
| 
 | ||||
| struct InternerState<T: ?Sized + 'static + Send + Sync> { | ||||
|     table: HashTable<&'static T>, | ||||
|     hasher: DefaultBuildHasher, | ||||
| } | ||||
| 
 | ||||
| pub struct Interner<T: ?Sized + 'static + Send + Sync> { | ||||
|     map: Mutex<HashMap<&'static T, ()>>, | ||||
|     state: Mutex<InternerState<T>>, | ||||
| } | ||||
| 
 | ||||
| impl<T: ?Sized + 'static + Send + Sync> Interner<T> { | ||||
|  | @ -330,7 +335,10 @@ impl<T: ?Sized + 'static + Send + Sync> Interner<T> { | |||
| impl<T: ?Sized + 'static + Send + Sync> Default for Interner<T> { | ||||
|     fn default() -> Self { | ||||
|         Self { | ||||
|             map: Default::default(), | ||||
|             state: Mutex::new(InternerState { | ||||
|                 table: HashTable::new(), | ||||
|                 hasher: Default::default(), | ||||
|             }), | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | @ -341,17 +349,16 @@ impl<T: ?Sized + 'static + Send + Sync + Hash + Eq + ToOwned> Interner<T> { | |||
|         alloc: F, | ||||
|         value: Cow<'_, T>, | ||||
|     ) -> Interned<T> { | ||||
|         let mut map = self.map.lock().unwrap(); | ||||
|         let hasher = map.hasher().clone(); | ||||
|         let hash = hasher.hash_one(&*value); | ||||
|         let inner = match map.raw_entry_mut().from_hash(hash, |k| **k == *value) { | ||||
|             RawEntryMut::Occupied(entry) => *entry.key(), | ||||
|             RawEntryMut::Vacant(entry) => { | ||||
|                 *entry | ||||
|                     .insert_with_hasher(hash, alloc(value), (), |k| hasher.hash_one(&**k)) | ||||
|                     .0 | ||||
|             } | ||||
|         }; | ||||
|         let mut state = self.state.lock().unwrap(); | ||||
|         let InternerState { table, hasher } = &mut *state; | ||||
|         let inner = *table | ||||
|             .entry( | ||||
|                 hasher.hash_one(&*value), | ||||
|                 |k| **k == *value, | ||||
|                 |k| hasher.hash_one(&**k), | ||||
|             ) | ||||
|             .or_insert_with(|| alloc(value)) | ||||
|             .get(); | ||||
|         Interned { inner } | ||||
|     } | ||||
| } | ||||
|  | @ -742,7 +749,7 @@ pub trait MemoizeGeneric: 'static + Send + Sync + Hash + Eq + Copy { | |||
|     fn get_cow(self, input: Self::InputCow<'_>) -> Self::Output { | ||||
|         static TYPE_ID_MAP: TypeIdMap = TypeIdMap::new(); | ||||
|         let map: &RwLock<( | ||||
|             hashbrown::hash_map::DefaultHashBuilder, | ||||
|             DefaultBuildHasher, | ||||
|             HashTable<(Self, Self::InputOwned, Self::Output)>, | ||||
|         )> = TYPE_ID_MAP.get_or_insert_default(); | ||||
|         fn hash_eq_key<'a, 'b, T: MemoizeGeneric>( | ||||
|  |  | |||
|  | @ -1,10 +1,8 @@ | |||
| // SPDX-License-Identifier: LGPL-3.0-or-later
 | ||||
| // See Notices.txt for copyright information
 | ||||
| use hashbrown::HashMap; | ||||
| use std::{ | ||||
|     any::{Any, TypeId}, | ||||
|     hash::{BuildHasher, Hasher}, | ||||
|     ptr::NonNull, | ||||
|     sync::RwLock, | ||||
| }; | ||||
| 
 | ||||
|  | @ -75,59 +73,36 @@ impl BuildHasher for TypeIdBuildHasher { | |||
|     } | ||||
| } | ||||
| 
 | ||||
| struct Value(NonNull<dyn Any + Send + Sync>); | ||||
| 
 | ||||
| impl Value { | ||||
|     unsafe fn get_transmute_lifetime<'b>(&self) -> &'b (dyn Any + Send + Sync) { | ||||
|         unsafe { &*self.0.as_ptr() } | ||||
|     } | ||||
|     fn new(v: Box<dyn Any + Send + Sync>) -> Self { | ||||
|         unsafe { Self(NonNull::new_unchecked(Box::into_raw(v))) } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| unsafe impl Send for Value {} | ||||
| unsafe impl Sync for Value {} | ||||
| 
 | ||||
| impl Drop for Value { | ||||
|     fn drop(&mut self) { | ||||
|         unsafe { std::ptr::drop_in_place(self.0.as_ptr()) } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| pub struct TypeIdMap(RwLock<HashMap<TypeId, Value, TypeIdBuildHasher>>); | ||||
| pub(crate) struct TypeIdMap( | ||||
|     RwLock<hashbrown::HashMap<TypeId, &'static (dyn Any + Send + Sync), TypeIdBuildHasher>>, | ||||
| ); | ||||
| 
 | ||||
| impl TypeIdMap { | ||||
|     pub const fn new() -> Self { | ||||
|         Self(RwLock::new(HashMap::with_hasher(TypeIdBuildHasher))) | ||||
|     pub(crate) const fn new() -> Self { | ||||
|         Self(RwLock::new(hashbrown::HashMap::with_hasher( | ||||
|             TypeIdBuildHasher, | ||||
|         ))) | ||||
|     } | ||||
|     #[cold] | ||||
|     unsafe fn insert_slow( | ||||
|     fn insert_slow( | ||||
|         &self, | ||||
|         type_id: TypeId, | ||||
|         make: fn() -> Box<dyn Any + Sync + Send>, | ||||
|     ) -> &(dyn Any + Sync + Send) { | ||||
|         let value = Value::new(make()); | ||||
|     ) -> &'static (dyn Any + Sync + Send) { | ||||
|         let value = Box::leak(make()); | ||||
|         let mut write_guard = self.0.write().unwrap(); | ||||
|         unsafe { | ||||
|             write_guard | ||||
|                 .entry(type_id) | ||||
|                 .or_insert(value) | ||||
|                 .get_transmute_lifetime() | ||||
|         *write_guard.entry(type_id).or_insert(value) | ||||
|     } | ||||
|     } | ||||
|     pub fn get_or_insert_default<T: Sized + Any + Send + Sync + Default>(&self) -> &T { | ||||
|     pub(crate) fn get_or_insert_default<T: Sized + Any + Send + Sync + Default>(&self) -> &T { | ||||
|         let type_id = TypeId::of::<T>(); | ||||
|         let read_guard = self.0.read().unwrap(); | ||||
|         let retval = read_guard | ||||
|             .get(&type_id) | ||||
|             .map(|v| unsafe { Value::get_transmute_lifetime(v) }); | ||||
|         let retval = read_guard.get(&type_id).map(|v| *v); | ||||
|         drop(read_guard); | ||||
|         let retval = match retval { | ||||
|             Some(retval) => retval, | ||||
|             None => unsafe { self.insert_slow(type_id, move || Box::new(T::default())) }, | ||||
|             None => self.insert_slow(type_id, move || Box::new(T::default())), | ||||
|         }; | ||||
|         unsafe { &*(retval as *const dyn Any as *const T) } | ||||
|         retval.downcast_ref().expect("known to have correct TypeId") | ||||
|     } | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -76,6 +76,8 @@ pub use fayalite_proc_macros::hdl_module; | |||
| #[doc(inline)] | ||||
| pub use fayalite_proc_macros::hdl; | ||||
| 
 | ||||
| pub use bitvec; | ||||
| 
 | ||||
| /// struct used as a placeholder when applying defaults
 | ||||
| #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Default)] | ||||
| pub struct __; | ||||
|  | @ -96,6 +98,7 @@ pub mod int; | |||
| pub mod intern; | ||||
| pub mod memory; | ||||
| pub mod module; | ||||
| pub mod phantom_const; | ||||
| pub mod prelude; | ||||
| pub mod reg; | ||||
| pub mod reset; | ||||
|  |  | |||
|  | @ -470,7 +470,7 @@ pub enum ReadUnderWrite { | |||
|     Undefined, | ||||
| } | ||||
| 
 | ||||
| #[derive(Copy, Clone, PartialEq, Eq, Hash)] | ||||
| #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] | ||||
| struct MemImpl<Element: Type, Len: Size, P> { | ||||
|     scoped_name: ScopedNameId, | ||||
|     source_location: SourceLocation, | ||||
|  | @ -1082,6 +1082,7 @@ pub fn splat_mask<T: Type>(ty: T, value: Expr<Bool>) -> Expr<AsMask<T>> { | |||
|             ) | ||||
|             .to_expr(), | ||||
|         )), | ||||
|         CanonicalType::PhantomConst(_) => Expr::from_canonical(Expr::canonical(().to_expr())), | ||||
|     } | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -21,18 +21,20 @@ use crate::{ | |||
|     memory::{Mem, MemBuilder, MemBuilderTarget, PortName}, | ||||
|     reg::Reg, | ||||
|     reset::{AsyncReset, Reset, ResetType, ResetTypeDispatch, SyncReset}, | ||||
|     sim::{ExternModuleSimGenerator, ExternModuleSimulation}, | ||||
|     source_location::SourceLocation, | ||||
|     ty::{CanonicalType, Type}, | ||||
|     util::ScopedRef, | ||||
|     util::{HashMap, HashSet, ScopedRef}, | ||||
|     wire::{IncompleteWire, Wire}, | ||||
| }; | ||||
| use hashbrown::{hash_map::Entry, HashMap, HashSet}; | ||||
| use hashbrown::hash_map::Entry; | ||||
| use num_bigint::BigInt; | ||||
| use std::{ | ||||
|     cell::RefCell, | ||||
|     collections::VecDeque, | ||||
|     convert::Infallible, | ||||
|     fmt, | ||||
|     future::IntoFuture, | ||||
|     hash::{Hash, Hasher}, | ||||
|     iter::FusedIterator, | ||||
|     marker::PhantomData, | ||||
|  | @ -1081,6 +1083,7 @@ pub struct ExternModuleBody< | |||
| > { | ||||
|     pub verilog_name: Interned<str>, | ||||
|     pub parameters: P, | ||||
|     pub simulation: Option<ExternModuleSimulation>, | ||||
| } | ||||
| 
 | ||||
| impl From<ExternModuleBody<Vec<ExternModuleParameter>>> for ExternModuleBody { | ||||
|  | @ -1088,11 +1091,13 @@ impl From<ExternModuleBody<Vec<ExternModuleParameter>>> for ExternModuleBody { | |||
|         let ExternModuleBody { | ||||
|             verilog_name, | ||||
|             parameters, | ||||
|             simulation, | ||||
|         } = value; | ||||
|         let parameters = Intern::intern_owned(parameters); | ||||
|         Self { | ||||
|             verilog_name, | ||||
|             parameters, | ||||
|             simulation, | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | @ -1283,10 +1288,12 @@ impl<T: BundleType> fmt::Debug for DebugModuleBody<T> { | |||
|             ModuleBody::Extern(ExternModuleBody { | ||||
|                 verilog_name, | ||||
|                 parameters, | ||||
|                 simulation, | ||||
|             }) => { | ||||
|                 debug_struct | ||||
|                     .field("verilog_name", verilog_name) | ||||
|                     .field("parameters", parameters); | ||||
|                     .field("parameters", parameters) | ||||
|                     .field("simulation", simulation); | ||||
|             } | ||||
|         } | ||||
|         debug_struct.finish_non_exhaustive() | ||||
|  | @ -1490,6 +1497,9 @@ impl TargetState { | |||
|                         }) | ||||
|                         .collect(), | ||||
|                 }, | ||||
|                 CanonicalType::PhantomConst(_) => TargetStateInner::Decomposed { | ||||
|                     subtargets: HashMap::default(), | ||||
|                 }, | ||||
|                 CanonicalType::Array(ty) => TargetStateInner::Decomposed { | ||||
|                     subtargets: (0..ty.len()) | ||||
|                         .map(|index| { | ||||
|  | @ -1758,6 +1768,7 @@ impl AssertValidityState { | |||
|             ModuleBody::Extern(ExternModuleBody { | ||||
|                 verilog_name: _, | ||||
|                 parameters: _, | ||||
|                 simulation: _, | ||||
|             }) => {} | ||||
|             ModuleBody::Normal(NormalModuleBody { body }) => { | ||||
|                 let body = self.make_block_index(body); | ||||
|  | @ -1853,7 +1864,7 @@ impl<T: BundleType> Module<T> { | |||
|         AssertValidityState { | ||||
|             module: self.canonical(), | ||||
|             blocks: vec![], | ||||
|             target_states: HashMap::with_capacity(64), | ||||
|             target_states: HashMap::with_capacity_and_hasher(64, Default::default()), | ||||
|         } | ||||
|         .assert_validity(); | ||||
|     } | ||||
|  | @ -2105,6 +2116,7 @@ impl ModuleBuilder { | |||
|             ModuleKind::Extern => ModuleBody::Extern(ExternModuleBody { | ||||
|                 verilog_name: name.0, | ||||
|                 parameters: vec![], | ||||
|                 simulation: None, | ||||
|             }), | ||||
|             ModuleKind::Normal => ModuleBody::Normal(NormalModuleBody { | ||||
|                 body: BuilderModuleBody { | ||||
|  | @ -2113,8 +2125,8 @@ impl ModuleBuilder { | |||
|                         incomplete_declarations: vec![], | ||||
|                         stmts: vec![], | ||||
|                     }], | ||||
|                     annotations_map: HashMap::new(), | ||||
|                     memory_map: HashMap::new(), | ||||
|                     annotations_map: HashMap::default(), | ||||
|                     memory_map: HashMap::default(), | ||||
|                 }, | ||||
|             }), | ||||
|         }; | ||||
|  | @ -2124,7 +2136,7 @@ impl ModuleBuilder { | |||
|             impl_: RefCell::new(ModuleBuilderImpl { | ||||
|                 body, | ||||
|                 io: vec![], | ||||
|                 io_indexes: HashMap::new(), | ||||
|                 io_indexes: HashMap::default(), | ||||
|                 module_annotations: vec![], | ||||
|             }), | ||||
|         }; | ||||
|  | @ -2171,6 +2183,7 @@ impl ModuleBuilder { | |||
|             .builder_extern_body() | ||||
|             .verilog_name = name.intern(); | ||||
|     } | ||||
|     #[track_caller] | ||||
|     pub fn parameter(&self, name: impl AsRef<str>, value: ExternModuleParameterValue) { | ||||
|         let name = name.as_ref(); | ||||
|         self.impl_ | ||||
|  | @ -2183,6 +2196,7 @@ impl ModuleBuilder { | |||
|                 value, | ||||
|             }); | ||||
|     } | ||||
|     #[track_caller] | ||||
|     pub fn parameter_int(&self, name: impl AsRef<str>, value: impl Into<BigInt>) { | ||||
|         let name = name.as_ref(); | ||||
|         let value = value.into(); | ||||
|  | @ -2196,6 +2210,7 @@ impl ModuleBuilder { | |||
|                 value: ExternModuleParameterValue::Integer(value), | ||||
|             }); | ||||
|     } | ||||
|     #[track_caller] | ||||
|     pub fn parameter_str(&self, name: impl AsRef<str>, value: impl AsRef<str>) { | ||||
|         let name = name.as_ref(); | ||||
|         let value = value.as_ref(); | ||||
|  | @ -2209,6 +2224,7 @@ impl ModuleBuilder { | |||
|                 value: ExternModuleParameterValue::String(value.intern()), | ||||
|             }); | ||||
|     } | ||||
|     #[track_caller] | ||||
|     pub fn parameter_raw_verilog(&self, name: impl AsRef<str>, raw_verilog: impl AsRef<str>) { | ||||
|         let name = name.as_ref(); | ||||
|         let raw_verilog = raw_verilog.as_ref(); | ||||
|  | @ -2222,6 +2238,26 @@ impl ModuleBuilder { | |||
|                 value: ExternModuleParameterValue::RawVerilog(raw_verilog.intern()), | ||||
|             }); | ||||
|     } | ||||
|     #[track_caller] | ||||
|     pub fn extern_module_simulation<G: ExternModuleSimGenerator>(&self, generator: G) { | ||||
|         let mut impl_ = self.impl_.borrow_mut(); | ||||
|         let simulation = &mut impl_.body.builder_extern_body().simulation; | ||||
|         if simulation.is_some() { | ||||
|             panic!("already added an extern module simulation"); | ||||
|         } | ||||
|         *simulation = Some(ExternModuleSimulation::new(generator)); | ||||
|     } | ||||
|     #[track_caller] | ||||
|     pub fn extern_module_simulation_fn< | ||||
|         Args: fmt::Debug + Clone + Hash + Eq + Send + Sync + 'static, | ||||
|         Fut: IntoFuture<Output = ()> + 'static, | ||||
|     >( | ||||
|         &self, | ||||
|         args: Args, | ||||
|         f: fn(Args, crate::sim::ExternModuleSimulationState) -> Fut, | ||||
|     ) { | ||||
|         self.extern_module_simulation(crate::sim::SimGeneratorFn { args, f }); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[track_caller] | ||||
|  |  | |||
|  | @ -24,8 +24,9 @@ use crate::{ | |||
|     }, | ||||
|     prelude::*, | ||||
|     reset::{ResetType, ResetTypeDispatch}, | ||||
|     util::{HashMap, HashSet}, | ||||
| }; | ||||
| use hashbrown::{hash_map::Entry, HashMap, HashSet}; | ||||
| use hashbrown::hash_map::Entry; | ||||
| use num_bigint::BigInt; | ||||
| use petgraph::unionfind::UnionFind; | ||||
| use std::{fmt, marker::PhantomData}; | ||||
|  | @ -155,6 +156,7 @@ impl ResetsLayout { | |||
|                     CanonicalType::SyncReset(_) => ResetsLayout::SyncReset, | ||||
|                     CanonicalType::Reset(_) => ResetsLayout::Reset, | ||||
|                     CanonicalType::Clock(_) => ResetsLayout::NoResets, | ||||
|                     CanonicalType::PhantomConst(_) => ResetsLayout::NoResets, | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | @ -407,7 +409,8 @@ impl Resets { | |||
|             | CanonicalType::Bool(_) | ||||
|             | CanonicalType::AsyncReset(_) | ||||
|             | CanonicalType::SyncReset(_) | ||||
|             | CanonicalType::Clock(_) => Ok(self.ty), | ||||
|             | CanonicalType::Clock(_) | ||||
|             | CanonicalType::PhantomConst(_) => Ok(self.ty), | ||||
|             CanonicalType::Array(ty) => Ok(CanonicalType::Array(Array::new_dyn( | ||||
|                 self.array_elements().substituted_type( | ||||
|                     reset_graph, | ||||
|  | @ -998,7 +1001,8 @@ fn cast_bit_op<P: Pass, T: Type, A: Type>( | |||
|                             CanonicalType::Array(_) | ||||
|                             | CanonicalType::Enum(_) | ||||
|                             | CanonicalType::Bundle(_) | ||||
|                             | CanonicalType::Reset(_) => unreachable!(), | ||||
|                             | CanonicalType::Reset(_) | ||||
|                             | CanonicalType::PhantomConst(_) => unreachable!(), | ||||
|                             $(CanonicalType::$Variant(ty) => Expr::expr_enum($arg.cast_to(ty)),)* | ||||
|                         } | ||||
|                     }; | ||||
|  | @ -1010,6 +1014,7 @@ fn cast_bit_op<P: Pass, T: Type, A: Type>( | |||
|                             | CanonicalType::Enum(_) | ||||
|                             | CanonicalType::Bundle(_) | ||||
|                             | CanonicalType::Reset(_) => unreachable!(), | ||||
|                             CanonicalType::PhantomConst(_) => Expr::expr_enum(arg), | ||||
|                             $(CanonicalType::$Variant(_) => { | ||||
|                                 let arg = Expr::<$Variant>::from_canonical(arg); | ||||
|                                 match_expr_ty!(arg, UInt, SInt, Bool, AsyncReset, SyncReset, Clock) | ||||
|  | @ -1040,6 +1045,7 @@ impl<P: Pass> RunPass<P> for ExprEnum { | |||
|             ExprEnum::UIntLiteral(expr) => Ok(expr.run_pass(pass_args)?.map(ExprEnum::from)), | ||||
|             ExprEnum::SIntLiteral(expr) => Ok(expr.run_pass(pass_args)?.map(ExprEnum::from)), | ||||
|             ExprEnum::BoolLiteral(expr) => Ok(expr.run_pass(pass_args)?.map(ExprEnum::from)), | ||||
|             ExprEnum::PhantomConst(expr) => Ok(expr.run_pass(pass_args)?.map(ExprEnum::from)), | ||||
|             ExprEnum::BundleLiteral(expr) => Ok(expr.run_pass(pass_args)?.map(ExprEnum::from)), | ||||
|             ExprEnum::ArrayLiteral(expr) => Ok(expr.run_pass(pass_args)?.map(ExprEnum::from)), | ||||
|             ExprEnum::EnumLiteral(expr) => Ok(expr.run_pass(pass_args)?.map(ExprEnum::from)), | ||||
|  | @ -1670,7 +1676,8 @@ impl RunPassDispatch for AnyReg { | |||
|                 | CanonicalType::Enum(_) | ||||
|                 | CanonicalType::Bundle(_) | ||||
|                 | CanonicalType::Reset(_) | ||||
|                 | CanonicalType::Clock(_) => unreachable!(), | ||||
|                 | CanonicalType::Clock(_) | ||||
|                 | CanonicalType::PhantomConst(_) => unreachable!(), | ||||
|             } | ||||
|         }) | ||||
|     } | ||||
|  | @ -1769,6 +1776,7 @@ impl_run_pass_copy!([] SVAttributeAnnotation); | |||
| impl_run_pass_copy!([] UInt); | ||||
| impl_run_pass_copy!([] usize); | ||||
| impl_run_pass_copy!([] FormalKind); | ||||
| impl_run_pass_copy!([] PhantomConst); | ||||
| 
 | ||||
| macro_rules! impl_run_pass_for_struct { | ||||
|     ( | ||||
|  | @ -2244,9 +2252,9 @@ pub fn deduce_resets( | |||
|     fallback_to_sync_reset: bool, | ||||
| ) -> Result<Interned<Module<Bundle>>, DeduceResetsError> { | ||||
|     let mut state = State { | ||||
|         modules_added_to_graph: HashSet::new(), | ||||
|         substituted_modules: HashMap::new(), | ||||
|         expr_resets: HashMap::new(), | ||||
|         modules_added_to_graph: HashSet::default(), | ||||
|         substituted_modules: HashMap::default(), | ||||
|         expr_resets: HashMap::default(), | ||||
|         reset_graph: ResetGraph::default(), | ||||
|         fallback_to_sync_reset, | ||||
|     }; | ||||
|  |  | |||
|  | @ -18,10 +18,10 @@ use crate::{ | |||
|     }, | ||||
|     source_location::SourceLocation, | ||||
|     ty::{CanonicalType, Type}, | ||||
|     util::HashMap, | ||||
|     wire::Wire, | ||||
| }; | ||||
| use core::fmt; | ||||
| use hashbrown::HashMap; | ||||
| 
 | ||||
| #[derive(Debug)] | ||||
| pub enum SimplifyEnumsError { | ||||
|  | @ -69,7 +69,8 @@ fn contains_any_enum_types(ty: CanonicalType) -> bool { | |||
|                 | CanonicalType::AsyncReset(_) | ||||
|                 | CanonicalType::SyncReset(_) | ||||
|                 | CanonicalType::Reset(_) | ||||
|                 | CanonicalType::Clock(_) => false, | ||||
|                 | CanonicalType::Clock(_) | ||||
|                 | CanonicalType::PhantomConst(_) => false, | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | @ -512,7 +513,8 @@ impl State { | |||
|             | CanonicalType::AsyncReset(_) | ||||
|             | CanonicalType::SyncReset(_) | ||||
|             | CanonicalType::Reset(_) | ||||
|             | CanonicalType::Clock(_) => unreachable!(), | ||||
|             | CanonicalType::Clock(_) | ||||
|             | CanonicalType::PhantomConst(_) => unreachable!(), | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | @ -577,7 +579,8 @@ fn connect_port( | |||
|         | (CanonicalType::Clock(_), _) | ||||
|         | (CanonicalType::AsyncReset(_), _) | ||||
|         | (CanonicalType::SyncReset(_), _) | ||||
|         | (CanonicalType::Reset(_), _) => unreachable!( | ||||
|         | (CanonicalType::Reset(_), _) | ||||
|         | (CanonicalType::PhantomConst(_), _) => unreachable!( | ||||
|             "trying to connect memory ports:\n{:?}\n{:?}", | ||||
|             Expr::ty(lhs), | ||||
|             Expr::ty(rhs), | ||||
|  | @ -665,6 +668,7 @@ impl Folder for State { | |||
|             ExprEnum::UIntLiteral(_) | ||||
|             | ExprEnum::SIntLiteral(_) | ||||
|             | ExprEnum::BoolLiteral(_) | ||||
|             | ExprEnum::PhantomConst(_) | ||||
|             | ExprEnum::BundleLiteral(_) | ||||
|             | ExprEnum::ArrayLiteral(_) | ||||
|             | ExprEnum::Uninit(_) | ||||
|  | @ -923,7 +927,8 @@ impl Folder for State { | |||
|             | CanonicalType::Clock(_) | ||||
|             | CanonicalType::AsyncReset(_) | ||||
|             | CanonicalType::SyncReset(_) | ||||
|             | CanonicalType::Reset(_) => canonical_type.default_fold(self), | ||||
|             | CanonicalType::Reset(_) | ||||
|             | CanonicalType::PhantomConst(_) => canonical_type.default_fold(self), | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|  | @ -960,8 +965,8 @@ pub fn simplify_enums( | |||
|     kind: SimplifyEnumsKind, | ||||
| ) -> Result<Interned<Module<Bundle>>, SimplifyEnumsError> { | ||||
|     module.fold(&mut State { | ||||
|         enum_types: HashMap::new(), | ||||
|         replacement_mem_ports: HashMap::new(), | ||||
|         enum_types: HashMap::default(), | ||||
|         replacement_mem_ports: HashMap::default(), | ||||
|         kind, | ||||
|         module_state_stack: vec![], | ||||
|     }) | ||||
|  |  | |||
|  | @ -14,11 +14,10 @@ use crate::{ | |||
|     }, | ||||
|     source_location::SourceLocation, | ||||
|     ty::{CanonicalType, Type}, | ||||
|     util::MakeMutSlice, | ||||
|     util::{HashMap, MakeMutSlice}, | ||||
|     wire::Wire, | ||||
| }; | ||||
| use bitvec::{slice::BitSlice, vec::BitVec}; | ||||
| use hashbrown::HashMap; | ||||
| use std::{ | ||||
|     convert::Infallible, | ||||
|     fmt::Write, | ||||
|  | @ -62,6 +61,7 @@ enum MemSplit { | |||
|     Bundle { | ||||
|         fields: Rc<[MemSplit]>, | ||||
|     }, | ||||
|     PhantomConst, | ||||
|     Single { | ||||
|         output_mem: Option<Mem>, | ||||
|         element_type: SingleType, | ||||
|  | @ -76,6 +76,7 @@ impl MemSplit { | |||
|     fn mark_changed_element_type(self) -> Self { | ||||
|         match self { | ||||
|             MemSplit::Bundle { fields: _ } => self, | ||||
|             MemSplit::PhantomConst => self, | ||||
|             MemSplit::Single { | ||||
|                 output_mem, | ||||
|                 element_type, | ||||
|  | @ -97,6 +98,7 @@ impl MemSplit { | |||
|                     .map(|field| Self::new(field.ty).mark_changed_element_type()) | ||||
|                     .collect(), | ||||
|             }, | ||||
|             CanonicalType::PhantomConst(_) => MemSplit::PhantomConst, | ||||
|             CanonicalType::Array(ty) => { | ||||
|                 let element = MemSplit::new(ty.element()); | ||||
|                 if let Self::Single { | ||||
|  | @ -339,6 +341,7 @@ impl SplitMemState<'_, '_> { | |||
|                     self.split_state_stack.pop(); | ||||
|                 } | ||||
|             } | ||||
|             MemSplit::PhantomConst => {} | ||||
|             MemSplit::Single { | ||||
|                 output_mem, | ||||
|                 element_type: single_type, | ||||
|  | @ -538,7 +541,12 @@ impl ModuleState { | |||
|             }; | ||||
|         loop { | ||||
|             match input_element_type { | ||||
|                 CanonicalType::Bundle(_) => unreachable!("bundle types are always split"), | ||||
|                 CanonicalType::Bundle(_) => { | ||||
|                     unreachable!("bundle types are always split") | ||||
|                 } | ||||
|                 CanonicalType::PhantomConst(_) => { | ||||
|                     unreachable!("PhantomConst are always removed") | ||||
|                 } | ||||
|                 CanonicalType::Enum(_) | ||||
|                     if input_array_types | ||||
|                         .first() | ||||
|  | @ -743,7 +751,8 @@ impl ModuleState { | |||
|                 .. | ||||
|             } | ||||
|             | MemSplit::Bundle { .. } | ||||
|             | MemSplit::Array { .. } => { | ||||
|             | MemSplit::Array { .. } | ||||
|             | MemSplit::PhantomConst => { | ||||
|                 let mut replacement_ports = Vec::with_capacity(input_mem.ports().len()); | ||||
|                 let mut wire_port_rdata = Vec::with_capacity(input_mem.ports().len()); | ||||
|                 let mut wire_port_wdata = Vec::with_capacity(input_mem.ports().len()); | ||||
|  | @ -887,7 +896,7 @@ impl Folder for State { | |||
|             module, | ||||
|             ModuleState { | ||||
|                 output_module: None, | ||||
|                 memories: HashMap::new(), | ||||
|                 memories: HashMap::default(), | ||||
|             }, | ||||
|         ); | ||||
|         let mut this = PushedState::push_module(self, module); | ||||
|  |  | |||
|  | @ -28,8 +28,10 @@ use crate::{ | |||
|         NormalModuleBody, ScopedNameId, Stmt, StmtConnect, StmtDeclaration, StmtFormal, StmtIf, | ||||
|         StmtInstance, StmtMatch, StmtReg, StmtWire, | ||||
|     }, | ||||
|     phantom_const::PhantomConst, | ||||
|     reg::Reg, | ||||
|     reset::{AsyncReset, Reset, ResetType, SyncReset}, | ||||
|     sim::ExternModuleSimulation, | ||||
|     source_location::SourceLocation, | ||||
|     ty::{CanonicalType, Type}, | ||||
|     wire::Wire, | ||||
|  |  | |||
							
								
								
									
										410
									
								
								crates/fayalite/src/phantom_const.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										410
									
								
								crates/fayalite/src/phantom_const.rs
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,410 @@ | |||
| // SPDX-License-Identifier: LGPL-3.0-or-later
 | ||||
| // See Notices.txt for copyright information
 | ||||
| 
 | ||||
| use crate::{ | ||||
|     expr::{ | ||||
|         ops::{ExprPartialEq, ExprPartialOrd}, | ||||
|         Expr, ToExpr, | ||||
|     }, | ||||
|     int::Bool, | ||||
|     intern::{Intern, Interned, InternedCompare, LazyInterned, LazyInternedTrait, Memoize}, | ||||
|     sim::value::{SimValue, SimValuePartialEq, ToSimValue, ToSimValueWithType}, | ||||
|     source_location::SourceLocation, | ||||
|     ty::{ | ||||
|         impl_match_variant_as_self, | ||||
|         serde_impls::{SerdeCanonicalType, SerdePhantomConst}, | ||||
|         CanonicalType, StaticType, Type, TypeProperties, | ||||
|     }, | ||||
| }; | ||||
| use bitvec::slice::BitSlice; | ||||
| use serde::{ | ||||
|     de::{DeserializeOwned, Error}, | ||||
|     Deserialize, Deserializer, Serialize, Serializer, | ||||
| }; | ||||
| use std::{ | ||||
|     any::Any, | ||||
|     fmt, | ||||
|     hash::{Hash, Hasher}, | ||||
|     marker::PhantomData, | ||||
|     ops::Index, | ||||
| }; | ||||
| 
 | ||||
| #[derive(Clone)] | ||||
| pub struct PhantomConstCanonicalValue { | ||||
|     parsed: serde_json::Value, | ||||
|     serialized: Interned<str>, | ||||
| } | ||||
| 
 | ||||
| impl PhantomConstCanonicalValue { | ||||
|     pub fn from_json_value(parsed: serde_json::Value) -> Self { | ||||
|         let serialized = Intern::intern_owned( | ||||
|             serde_json::to_string(&parsed) | ||||
|                 .expect("conversion from json value to text shouldn't fail"), | ||||
|         ); | ||||
|         Self { parsed, serialized } | ||||
|     } | ||||
|     pub fn as_json_value(&self) -> &serde_json::Value { | ||||
|         &self.parsed | ||||
|     } | ||||
|     pub fn as_str(&self) -> Interned<str> { | ||||
|         self.serialized | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl fmt::Debug for PhantomConstCanonicalValue { | ||||
|     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||||
|         f.write_str(&self.serialized) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl fmt::Display for PhantomConstCanonicalValue { | ||||
|     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||||
|         f.write_str(&self.serialized) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl PartialEq for PhantomConstCanonicalValue { | ||||
|     fn eq(&self, other: &Self) -> bool { | ||||
|         self.serialized == other.serialized | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl Eq for PhantomConstCanonicalValue {} | ||||
| 
 | ||||
| impl Hash for PhantomConstCanonicalValue { | ||||
|     fn hash<H: Hasher>(&self, state: &mut H) { | ||||
|         self.serialized.hash(state); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl Serialize for PhantomConstCanonicalValue { | ||||
|     fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> | ||||
|     where | ||||
|         S: serde::Serializer, | ||||
|     { | ||||
|         self.parsed.serialize(serializer) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<'de> Deserialize<'de> for PhantomConstCanonicalValue { | ||||
|     fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> | ||||
|     where | ||||
|         D: serde::Deserializer<'de>, | ||||
|     { | ||||
|         Ok(Self::from_json_value(serde_json::Value::deserialize( | ||||
|             deserializer, | ||||
|         )?)) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| pub trait PhantomConstValue: Intern + InternedCompare + Serialize + fmt::Debug { | ||||
|     fn deserialize_value<'de, D>(deserializer: D) -> Result<Interned<Self>, D::Error> | ||||
|     where | ||||
|         D: serde::Deserializer<'de>; | ||||
| } | ||||
| 
 | ||||
| impl<T> PhantomConstValue for T | ||||
| where | ||||
|     T: ?Sized + Intern + InternedCompare + Serialize + fmt::Debug, | ||||
|     Interned<T>: DeserializeOwned, | ||||
| { | ||||
|     fn deserialize_value<'de, D>(deserializer: D) -> Result<Interned<Self>, D::Error> | ||||
|     where | ||||
|         D: serde::Deserializer<'de>, | ||||
|     { | ||||
|         <Interned<T> as Deserialize<'de>>::deserialize(deserializer) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| /// Wrapper type that allows any Rust value to be smuggled as a HDL [`Type`].
 | ||||
| /// This only works for values that can be [serialized][Serialize] to and [deserialized][Deserialize] from [JSON][serde_json].
 | ||||
| pub struct PhantomConst<T: ?Sized + PhantomConstValue = PhantomConstCanonicalValue> { | ||||
|     value: LazyInterned<T>, | ||||
| } | ||||
| 
 | ||||
| #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, Default)] | ||||
| pub struct PhantomConstWithoutGenerics; | ||||
| 
 | ||||
| #[allow(non_upper_case_globals)] | ||||
| pub const PhantomConst: PhantomConstWithoutGenerics = PhantomConstWithoutGenerics; | ||||
| 
 | ||||
| impl<T: Type + PhantomConstValue> Index<T> for PhantomConstWithoutGenerics { | ||||
|     type Output = PhantomConst<T>; | ||||
| 
 | ||||
|     fn index(&self, value: T) -> &Self::Output { | ||||
|         Interned::into_inner(PhantomConst::new(value.intern()).intern_sized()) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<T: ?Sized + PhantomConstValue> fmt::Debug for PhantomConst<T> { | ||||
|     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||||
|         f.debug_tuple("PhantomConst").field(&self.get()).finish() | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<T: ?Sized + PhantomConstValue> Clone for PhantomConst<T> { | ||||
|     fn clone(&self) -> Self { | ||||
|         *self | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<T: ?Sized + PhantomConstValue> Copy for PhantomConst<T> {} | ||||
| 
 | ||||
| impl<T: ?Sized + PhantomConstValue> PartialEq for PhantomConst<T> { | ||||
|     fn eq(&self, other: &Self) -> bool { | ||||
|         self.get() == other.get() | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<T: ?Sized + PhantomConstValue> Eq for PhantomConst<T> {} | ||||
| 
 | ||||
| impl<T: ?Sized + PhantomConstValue> Hash for PhantomConst<T> { | ||||
|     fn hash<H: Hasher>(&self, state: &mut H) { | ||||
|         self.get().hash(state); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| struct PhantomConstCanonicalMemoize<T: ?Sized, const IS_FROM_CANONICAL: bool>(PhantomData<T>); | ||||
| 
 | ||||
| impl<T: ?Sized, const IS_FROM_CANONICAL: bool> Copy | ||||
|     for PhantomConstCanonicalMemoize<T, IS_FROM_CANONICAL> | ||||
| { | ||||
| } | ||||
| 
 | ||||
| impl<T: ?Sized, const IS_FROM_CANONICAL: bool> Clone | ||||
|     for PhantomConstCanonicalMemoize<T, IS_FROM_CANONICAL> | ||||
| { | ||||
|     fn clone(&self) -> Self { | ||||
|         *self | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<T: ?Sized, const IS_FROM_CANONICAL: bool> Eq | ||||
|     for PhantomConstCanonicalMemoize<T, IS_FROM_CANONICAL> | ||||
| { | ||||
| } | ||||
| 
 | ||||
| impl<T: ?Sized, const IS_FROM_CANONICAL: bool> PartialEq | ||||
|     for PhantomConstCanonicalMemoize<T, IS_FROM_CANONICAL> | ||||
| { | ||||
|     fn eq(&self, _other: &Self) -> bool { | ||||
|         true | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<T: ?Sized, const IS_FROM_CANONICAL: bool> Hash | ||||
|     for PhantomConstCanonicalMemoize<T, IS_FROM_CANONICAL> | ||||
| { | ||||
|     fn hash<H: Hasher>(&self, _state: &mut H) {} | ||||
| } | ||||
| 
 | ||||
| impl<T: ?Sized + PhantomConstValue> Memoize for PhantomConstCanonicalMemoize<T, false> { | ||||
|     type Input = Interned<T>; | ||||
|     type InputOwned = Interned<T>; | ||||
|     type Output = Interned<PhantomConstCanonicalValue>; | ||||
| 
 | ||||
|     fn inner(self, input: &Self::Input) -> Self::Output { | ||||
|         Intern::intern_sized(PhantomConstCanonicalValue::from_json_value( | ||||
|             serde_json::to_value(input) | ||||
|                 .expect("serialization failed when constructing a canonical PhantomConst"), | ||||
|         )) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<T: ?Sized + PhantomConstValue> Memoize for PhantomConstCanonicalMemoize<T, true> { | ||||
|     type Input = Interned<PhantomConstCanonicalValue>; | ||||
|     type InputOwned = Interned<PhantomConstCanonicalValue>; | ||||
|     type Output = Interned<T>; | ||||
| 
 | ||||
|     fn inner(self, input: &Self::Input) -> Self::Output { | ||||
|         PhantomConstValue::deserialize_value(input.as_json_value()) | ||||
|             .expect("deserialization failed ") | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<T: ?Sized + PhantomConstValue> PhantomConst<T> { | ||||
|     pub fn new(value: Interned<T>) -> Self { | ||||
|         Self { | ||||
|             value: LazyInterned::Interned(value), | ||||
|         } | ||||
|     } | ||||
|     pub const fn new_lazy(v: &'static dyn LazyInternedTrait<T>) -> Self { | ||||
|         Self { | ||||
|             value: LazyInterned::new_lazy(v), | ||||
|         } | ||||
|     } | ||||
|     pub fn get(self) -> Interned<T> { | ||||
|         self.value.interned() | ||||
|     } | ||||
|     pub fn type_properties(self) -> TypeProperties { | ||||
|         <()>::TYPE_PROPERTIES | ||||
|     } | ||||
|     pub fn can_connect(self, other: Self) -> bool { | ||||
|         self == other | ||||
|     } | ||||
|     pub fn canonical_phantom_const(self) -> PhantomConst { | ||||
|         if let Some(&retval) = <dyn Any>::downcast_ref::<PhantomConst>(&self) { | ||||
|             return retval; | ||||
|         } | ||||
|         <PhantomConst>::new( | ||||
|             PhantomConstCanonicalMemoize::<T, false>(PhantomData).get_owned(self.get()), | ||||
|         ) | ||||
|     } | ||||
|     pub fn from_canonical_phantom_const(canonical_type: PhantomConst) -> Self { | ||||
|         if let Some(&retval) = <dyn Any>::downcast_ref::<Self>(&canonical_type) { | ||||
|             return retval; | ||||
|         } | ||||
|         Self::new( | ||||
|             PhantomConstCanonicalMemoize::<T, true>(PhantomData).get_owned(canonical_type.get()), | ||||
|         ) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<T: ?Sized + PhantomConstValue> Type for PhantomConst<T> { | ||||
|     type BaseType = PhantomConst; | ||||
|     type MaskType = (); | ||||
|     type SimValue = PhantomConst<T>; | ||||
|     impl_match_variant_as_self!(); | ||||
| 
 | ||||
|     fn mask_type(&self) -> Self::MaskType { | ||||
|         () | ||||
|     } | ||||
| 
 | ||||
|     fn canonical(&self) -> CanonicalType { | ||||
|         CanonicalType::PhantomConst(self.canonical_phantom_const()) | ||||
|     } | ||||
| 
 | ||||
|     fn from_canonical(canonical_type: CanonicalType) -> Self { | ||||
|         let CanonicalType::PhantomConst(phantom_const) = canonical_type else { | ||||
|             panic!("expected PhantomConst"); | ||||
|         }; | ||||
|         Self::from_canonical_phantom_const(phantom_const) | ||||
|     } | ||||
| 
 | ||||
|     fn source_location() -> SourceLocation { | ||||
|         SourceLocation::builtin() | ||||
|     } | ||||
| 
 | ||||
|     fn sim_value_from_bits(&self, bits: &BitSlice) -> Self::SimValue { | ||||
|         assert!(bits.is_empty()); | ||||
|         *self | ||||
|     } | ||||
| 
 | ||||
|     fn sim_value_clone_from_bits(&self, value: &mut Self::SimValue, bits: &BitSlice) { | ||||
|         assert!(bits.is_empty()); | ||||
|         assert_eq!(*value, *self); | ||||
|     } | ||||
| 
 | ||||
|     fn sim_value_to_bits(&self, value: &Self::SimValue, bits: &mut BitSlice) { | ||||
|         assert!(bits.is_empty()); | ||||
|         assert_eq!(*value, *self); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<T: ?Sized + PhantomConstValue> Default for PhantomConst<T> | ||||
| where | ||||
|     Interned<T>: Default, | ||||
| { | ||||
|     fn default() -> Self { | ||||
|         Self::TYPE | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<T: ?Sized + PhantomConstValue> StaticType for PhantomConst<T> | ||||
| where | ||||
|     Interned<T>: Default, | ||||
| { | ||||
|     const TYPE: Self = PhantomConst { | ||||
|         value: LazyInterned::new_lazy(&Interned::<T>::default), | ||||
|     }; | ||||
|     const MASK_TYPE: Self::MaskType = (); | ||||
|     const TYPE_PROPERTIES: TypeProperties = <()>::TYPE_PROPERTIES; | ||||
|     const MASK_TYPE_PROPERTIES: TypeProperties = <()>::TYPE_PROPERTIES; | ||||
| } | ||||
| 
 | ||||
| type SerdeType<T> = SerdeCanonicalType<CanonicalType, SerdePhantomConst<Interned<T>>>; | ||||
| 
 | ||||
| impl<T: ?Sized + PhantomConstValue> Serialize for PhantomConst<T> { | ||||
|     fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> | ||||
|     where | ||||
|         S: Serializer, | ||||
|     { | ||||
|         SerdeType::<T>::PhantomConst(SerdePhantomConst(self.get())).serialize(serializer) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<'de, T: ?Sized + PhantomConstValue> Deserialize<'de> for PhantomConst<T> { | ||||
|     fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> | ||||
|     where | ||||
|         D: Deserializer<'de>, | ||||
|     { | ||||
|         match SerdeType::<T>::deserialize(deserializer)? { | ||||
|             SerdeCanonicalType::PhantomConst(SerdePhantomConst(value)) => Ok(Self::new(value)), | ||||
|             ty => Err(Error::invalid_value( | ||||
|                 serde::de::Unexpected::Other(ty.as_serde_unexpected_str()), | ||||
|                 &"a PhantomConst", | ||||
|             )), | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<T: ?Sized + PhantomConstValue> ExprPartialEq<Self> for PhantomConst<T> { | ||||
|     fn cmp_eq(lhs: Expr<Self>, rhs: Expr<Self>) -> Expr<Bool> { | ||||
|         assert_eq!(Expr::ty(lhs), Expr::ty(rhs)); | ||||
|         true.to_expr() | ||||
|     } | ||||
| 
 | ||||
|     fn cmp_ne(lhs: Expr<Self>, rhs: Expr<Self>) -> Expr<Bool> { | ||||
|         assert_eq!(Expr::ty(lhs), Expr::ty(rhs)); | ||||
|         false.to_expr() | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<T: ?Sized + PhantomConstValue> ExprPartialOrd<Self> for PhantomConst<T> { | ||||
|     fn cmp_lt(lhs: Expr<Self>, rhs: Expr<Self>) -> Expr<Bool> { | ||||
|         assert_eq!(Expr::ty(lhs), Expr::ty(rhs)); | ||||
|         false.to_expr() | ||||
|     } | ||||
| 
 | ||||
|     fn cmp_le(lhs: Expr<Self>, rhs: Expr<Self>) -> Expr<Bool> { | ||||
|         assert_eq!(Expr::ty(lhs), Expr::ty(rhs)); | ||||
|         true.to_expr() | ||||
|     } | ||||
| 
 | ||||
|     fn cmp_gt(lhs: Expr<Self>, rhs: Expr<Self>) -> Expr<Bool> { | ||||
|         assert_eq!(Expr::ty(lhs), Expr::ty(rhs)); | ||||
|         false.to_expr() | ||||
|     } | ||||
| 
 | ||||
|     fn cmp_ge(lhs: Expr<Self>, rhs: Expr<Self>) -> Expr<Bool> { | ||||
|         assert_eq!(Expr::ty(lhs), Expr::ty(rhs)); | ||||
|         true.to_expr() | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<T: ?Sized + PhantomConstValue> SimValuePartialEq<Self> for PhantomConst<T> { | ||||
|     fn sim_value_eq(this: &SimValue<Self>, other: &SimValue<Self>) -> bool { | ||||
|         assert_eq!(SimValue::ty(this), SimValue::ty(other)); | ||||
|         true | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<T: ?Sized + PhantomConstValue> ToSimValue for PhantomConst<T> { | ||||
|     type Type = PhantomConst<T>; | ||||
| 
 | ||||
|     fn to_sim_value(&self) -> SimValue<Self::Type> { | ||||
|         SimValue::from_value(*self, *self) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<T: ?Sized + PhantomConstValue> ToSimValueWithType<PhantomConst<T>> for PhantomConst<T> { | ||||
|     fn to_sim_value_with_type(&self, ty: PhantomConst<T>) -> SimValue<PhantomConst<T>> { | ||||
|         SimValue::from_value(ty, *self) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<T: ?Sized + PhantomConstValue> ToSimValueWithType<CanonicalType> for PhantomConst<T> { | ||||
|     fn to_sim_value_with_type(&self, ty: CanonicalType) -> SimValue<CanonicalType> { | ||||
|         SimValue::into_canonical(SimValue::from_value(Self::from_canonical(ty), *self)) | ||||
|     } | ||||
| } | ||||
|  | @ -20,17 +20,24 @@ pub use crate::{ | |||
|         hdl_cover_with_enable, MakeFormalExpr, | ||||
|     }, | ||||
|     hdl, hdl_module, | ||||
|     int::{Bool, DynSize, KnownSize, SInt, SIntType, Size, UInt, UIntType}, | ||||
|     int::{Bool, DynSize, KnownSize, SInt, SIntType, SIntValue, Size, UInt, UIntType, UIntValue}, | ||||
|     memory::{Mem, MemBuilder, ReadUnderWrite}, | ||||
|     module::{ | ||||
|         annotate, connect, connect_any, incomplete_wire, instance, memory, memory_array, | ||||
|         memory_with_init, reg_builder, wire, Instance, Module, ModuleBuilder, | ||||
|     }, | ||||
|     phantom_const::PhantomConst, | ||||
|     reg::Reg, | ||||
|     reset::{AsyncReset, Reset, SyncReset, ToAsyncReset, ToReset, ToSyncReset}, | ||||
|     sim::{ | ||||
|         time::{SimDuration, SimInstant}, | ||||
|         value::{SimValue, ToSimValue, ToSimValueWithType}, | ||||
|         ExternModuleSimulationState, Simulation, | ||||
|     }, | ||||
|     source_location::SourceLocation, | ||||
|     ty::{AsMask, CanonicalType, Type}, | ||||
|     util::{ConstUsize, GenericConstUsize}, | ||||
|     wire::Wire, | ||||
|     __, | ||||
| }; | ||||
| pub use bitvec::{slice::BitSlice, vec::BitVec}; | ||||
|  |  | |||
|  | @ -7,6 +7,7 @@ use crate::{ | |||
|     source_location::SourceLocation, | ||||
|     ty::{impl_match_variant_as_self, CanonicalType, StaticType, Type, TypeProperties}, | ||||
| }; | ||||
| use bitvec::slice::BitSlice; | ||||
| 
 | ||||
| mod sealed { | ||||
|     pub trait ResetTypeSealed {} | ||||
|  | @ -45,6 +46,7 @@ macro_rules! reset_type { | |||
|         impl Type for $name { | ||||
|             type BaseType = $name; | ||||
|             type MaskType = Bool; | ||||
|             type SimValue = bool; | ||||
| 
 | ||||
|             impl_match_variant_as_self!(); | ||||
| 
 | ||||
|  | @ -66,6 +68,21 @@ macro_rules! reset_type { | |||
|                 }; | ||||
|                 retval | ||||
|             } | ||||
| 
 | ||||
|             fn sim_value_from_bits(&self, bits: &BitSlice) -> Self::SimValue { | ||||
|                 assert_eq!(bits.len(), 1); | ||||
|                 bits[0] | ||||
|             } | ||||
| 
 | ||||
|             fn sim_value_clone_from_bits(&self, value: &mut Self::SimValue, bits: &BitSlice) { | ||||
|                 assert_eq!(bits.len(), 1); | ||||
|                 *value = bits[0]; | ||||
|             } | ||||
| 
 | ||||
|             fn sim_value_to_bits(&self, value: &Self::SimValue, bits: &mut BitSlice) { | ||||
|                 assert_eq!(bits.len(), 1); | ||||
|                 bits.set(0, *value); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         impl $name { | ||||
|  |  | |||
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							|  | @ -7,10 +7,9 @@ use crate::{ | |||
|     intern::{Intern, Interned, Memoize}, | ||||
|     source_location::SourceLocation, | ||||
|     ty::CanonicalType, | ||||
|     util::get_many_mut, | ||||
|     util::{get_many_mut, HashMap, HashSet}, | ||||
| }; | ||||
| use bitvec::{boxed::BitBox, slice::BitSlice}; | ||||
| use hashbrown::{HashMap, HashSet}; | ||||
| use num_bigint::BigInt; | ||||
| use num_traits::{One, Signed, ToPrimitive, Zero}; | ||||
| use std::{ | ||||
|  |  | |||
							
								
								
									
										913
									
								
								crates/fayalite/src/sim/value.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										913
									
								
								crates/fayalite/src/sim/value.rs
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,913 @@ | |||
| // SPDX-License-Identifier: LGPL-3.0-or-later
 | ||||
| // See Notices.txt for copyright information
 | ||||
| 
 | ||||
| use crate::{ | ||||
|     array::{Array, ArrayType}, | ||||
|     bundle::{Bundle, BundleType}, | ||||
|     clock::Clock, | ||||
|     enum_::{Enum, EnumType}, | ||||
|     expr::{CastBitsTo, Expr, ToExpr}, | ||||
|     int::{Bool, IntType, KnownSize, SInt, SIntType, SIntValue, Size, UInt, UIntType, UIntValue}, | ||||
|     reset::{AsyncReset, Reset, SyncReset}, | ||||
|     ty::{CanonicalType, StaticType, Type}, | ||||
|     util::{ | ||||
|         alternating_cell::{AlternatingCell, AlternatingCellMethods}, | ||||
|         ConstUsize, | ||||
|     }, | ||||
| }; | ||||
| use bitvec::{slice::BitSlice, vec::BitVec}; | ||||
| use serde::{Deserialize, Deserializer, Serialize, Serializer}; | ||||
| use std::{ | ||||
|     fmt, | ||||
|     ops::{Deref, DerefMut}, | ||||
|     sync::Arc, | ||||
| }; | ||||
| 
 | ||||
| #[derive(Copy, Clone, Eq, PartialEq)] | ||||
| enum ValidFlags { | ||||
|     BothValid = 0, | ||||
|     OnlyValueValid = 1, | ||||
|     OnlyBitsValid = 2, | ||||
| } | ||||
| 
 | ||||
| #[derive(Clone)] | ||||
| struct SimValueInner<T: Type> { | ||||
|     value: T::SimValue, | ||||
|     bits: UIntValue, | ||||
|     valid_flags: ValidFlags, | ||||
|     ty: T, | ||||
| } | ||||
| 
 | ||||
| impl<T: Type> SimValueInner<T> { | ||||
|     fn fill_bits(&mut self) { | ||||
|         match self.valid_flags { | ||||
|             ValidFlags::BothValid | ValidFlags::OnlyBitsValid => {} | ||||
|             ValidFlags::OnlyValueValid => { | ||||
|                 self.ty.sim_value_to_bits(&self.value, self.bits.bits_mut()); | ||||
|                 self.valid_flags = ValidFlags::BothValid; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|     fn into_bits(mut self) -> UIntValue { | ||||
|         self.fill_bits(); | ||||
|         self.bits | ||||
|     } | ||||
|     fn bits_mut(&mut self) -> &mut UIntValue { | ||||
|         self.fill_bits(); | ||||
|         self.valid_flags = ValidFlags::OnlyBitsValid; | ||||
|         &mut self.bits | ||||
|     } | ||||
|     fn fill_value(&mut self) { | ||||
|         match self.valid_flags { | ||||
|             ValidFlags::BothValid | ValidFlags::OnlyValueValid => {} | ||||
|             ValidFlags::OnlyBitsValid => { | ||||
|                 self.ty | ||||
|                     .sim_value_clone_from_bits(&mut self.value, self.bits.bits()); | ||||
|                 self.valid_flags = ValidFlags::BothValid; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|     fn into_value(mut self) -> T::SimValue { | ||||
|         self.fill_value(); | ||||
|         self.value | ||||
|     } | ||||
|     fn value_mut(&mut self) -> &mut T::SimValue { | ||||
|         self.fill_value(); | ||||
|         self.valid_flags = ValidFlags::OnlyValueValid; | ||||
|         &mut self.value | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<T: Type> AlternatingCellMethods for SimValueInner<T> { | ||||
|     fn unique_to_shared(&mut self) { | ||||
|         match self.valid_flags { | ||||
|             ValidFlags::BothValid => return, | ||||
|             ValidFlags::OnlyValueValid => { | ||||
|                 self.ty.sim_value_to_bits(&self.value, self.bits.bits_mut()) | ||||
|             } | ||||
|             ValidFlags::OnlyBitsValid => self | ||||
|                 .ty | ||||
|                 .sim_value_clone_from_bits(&mut self.value, self.bits.bits()), | ||||
|         } | ||||
|         self.valid_flags = ValidFlags::BothValid; | ||||
|     } | ||||
| 
 | ||||
|     fn shared_to_unique(&mut self) {} | ||||
| } | ||||
| 
 | ||||
| #[derive(Serialize, Deserialize)] | ||||
| #[serde(rename = "SimValue")] | ||||
| #[serde(bound(
 | ||||
|     serialize = "T: Type<SimValue: Serialize> + Serialize", | ||||
|     deserialize = "T: Type<SimValue: Deserialize<'de>> + Deserialize<'de>" | ||||
| ))] | ||||
| struct SerdeSimValue<'a, T: Type> { | ||||
|     ty: T, | ||||
|     value: std::borrow::Cow<'a, T::SimValue>, | ||||
| } | ||||
| 
 | ||||
| impl<T: Type<SimValue: Serialize> + Serialize> Serialize for SimValue<T> { | ||||
|     fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> | ||||
|     where | ||||
|         S: Serializer, | ||||
|     { | ||||
|         SerdeSimValue { | ||||
|             ty: SimValue::ty(self), | ||||
|             value: std::borrow::Cow::Borrowed(&*self), | ||||
|         } | ||||
|         .serialize(serializer) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<'de, T: Type<SimValue: Deserialize<'de>> + Deserialize<'de>> Deserialize<'de> for SimValue<T> { | ||||
|     fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> | ||||
|     where | ||||
|         D: Deserializer<'de>, | ||||
|     { | ||||
|         let SerdeSimValue { ty, value } = SerdeSimValue::<T>::deserialize(deserializer)?; | ||||
|         Ok(SimValue::from_value(ty, value.into_owned())) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| pub struct SimValue<T: Type> { | ||||
|     inner: AlternatingCell<SimValueInner<T>>, | ||||
| } | ||||
| 
 | ||||
| impl<T: Type + Clone> Clone for SimValue<T> { | ||||
|     fn clone(&self) -> Self { | ||||
|         Self { | ||||
|             inner: AlternatingCell::new_unique(self.inner.share().clone()), | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<T: Type> SimValue<T> { | ||||
|     #[track_caller] | ||||
|     pub fn from_bits(ty: T, bits: UIntValue) -> Self { | ||||
|         assert_eq!(ty.canonical().bit_width(), bits.width()); | ||||
|         let inner = SimValueInner { | ||||
|             value: ty.sim_value_from_bits(bits.bits()), | ||||
|             bits, | ||||
|             valid_flags: ValidFlags::BothValid, | ||||
|             ty, | ||||
|         }; | ||||
|         Self { | ||||
|             inner: AlternatingCell::new_shared(inner), | ||||
|         } | ||||
|     } | ||||
|     #[track_caller] | ||||
|     pub fn from_bitslice(ty: T, bits: &BitSlice) -> Self { | ||||
|         Self::from_bits(ty, UIntValue::new(Arc::new(bits.to_bitvec()))) | ||||
|     } | ||||
|     pub fn from_value(ty: T, value: T::SimValue) -> Self { | ||||
|         let inner = SimValueInner { | ||||
|             bits: UIntValue::new_dyn(Arc::new(BitVec::repeat(false, ty.canonical().bit_width()))), | ||||
|             value, | ||||
|             valid_flags: ValidFlags::OnlyValueValid, | ||||
|             ty, | ||||
|         }; | ||||
|         Self { | ||||
|             inner: AlternatingCell::new_unique(inner), | ||||
|         } | ||||
|     } | ||||
|     pub fn ty(this: &Self) -> T { | ||||
|         this.inner.share().ty | ||||
|     } | ||||
|     pub fn into_bits(this: Self) -> UIntValue { | ||||
|         this.inner.into_inner().into_bits() | ||||
|     } | ||||
|     pub fn into_ty_and_bits(this: Self) -> (T, UIntValue) { | ||||
|         let inner = this.inner.into_inner(); | ||||
|         (inner.ty, inner.into_bits()) | ||||
|     } | ||||
|     pub fn bits(this: &Self) -> &UIntValue { | ||||
|         &this.inner.share().bits | ||||
|     } | ||||
|     pub fn bits_mut(this: &mut Self) -> &mut UIntValue { | ||||
|         this.inner.unique().bits_mut() | ||||
|     } | ||||
|     pub fn into_value(this: Self) -> T::SimValue { | ||||
|         this.inner.into_inner().into_value() | ||||
|     } | ||||
|     pub fn value(this: &Self) -> &T::SimValue { | ||||
|         &this.inner.share().value | ||||
|     } | ||||
|     pub fn value_mut(this: &mut Self) -> &mut T::SimValue { | ||||
|         this.inner.unique().value_mut() | ||||
|     } | ||||
|     #[track_caller] | ||||
|     pub fn from_canonical(v: SimValue<CanonicalType>) -> Self { | ||||
|         let (ty, bits) = SimValue::into_ty_and_bits(v); | ||||
|         Self::from_bits(T::from_canonical(ty), bits) | ||||
|     } | ||||
|     pub fn into_canonical(this: Self) -> SimValue<CanonicalType> { | ||||
|         let (ty, bits) = Self::into_ty_and_bits(this); | ||||
|         SimValue::from_bits(ty.canonical(), bits) | ||||
|     } | ||||
|     pub fn canonical(this: &Self) -> SimValue<CanonicalType> { | ||||
|         SimValue::from_bits(Self::ty(this).canonical(), Self::bits(this).clone()) | ||||
|     } | ||||
|     #[track_caller] | ||||
|     pub fn from_dyn_int(v: SimValue<T::Dyn>) -> Self | ||||
|     where | ||||
|         T: IntType, | ||||
|     { | ||||
|         let (ty, bits) = SimValue::into_ty_and_bits(v); | ||||
|         SimValue::from_bits(T::from_dyn_int(ty), bits) | ||||
|     } | ||||
|     pub fn into_dyn_int(this: Self) -> SimValue<T::Dyn> | ||||
|     where | ||||
|         T: IntType, | ||||
|     { | ||||
|         let (ty, bits) = Self::into_ty_and_bits(this); | ||||
|         SimValue::from_bits(ty.as_dyn_int(), bits) | ||||
|     } | ||||
|     pub fn to_dyn_int(this: &Self) -> SimValue<T::Dyn> | ||||
|     where | ||||
|         T: IntType, | ||||
|     { | ||||
|         SimValue::from_bits(Self::ty(this).as_dyn_int(), Self::bits(&this).clone()) | ||||
|     } | ||||
|     #[track_caller] | ||||
|     pub fn from_bundle(v: SimValue<Bundle>) -> Self | ||||
|     where | ||||
|         T: BundleType, | ||||
|     { | ||||
|         let (ty, bits) = SimValue::into_ty_and_bits(v); | ||||
|         SimValue::from_bits(T::from_canonical(CanonicalType::Bundle(ty)), bits) | ||||
|     } | ||||
|     pub fn into_bundle(this: Self) -> SimValue<Bundle> | ||||
|     where | ||||
|         T: BundleType, | ||||
|     { | ||||
|         let (ty, bits) = Self::into_ty_and_bits(this); | ||||
|         SimValue::from_bits(Bundle::from_canonical(ty.canonical()), bits) | ||||
|     } | ||||
|     pub fn to_bundle(this: &Self) -> SimValue<Bundle> | ||||
|     where | ||||
|         T: BundleType, | ||||
|     { | ||||
|         SimValue::from_bits( | ||||
|             Bundle::from_canonical(Self::ty(this).canonical()), | ||||
|             Self::bits(&this).clone(), | ||||
|         ) | ||||
|     } | ||||
|     #[track_caller] | ||||
|     pub fn from_enum(v: SimValue<Enum>) -> Self | ||||
|     where | ||||
|         T: EnumType, | ||||
|     { | ||||
|         let (ty, bits) = SimValue::into_ty_and_bits(v); | ||||
|         SimValue::from_bits(T::from_canonical(CanonicalType::Enum(ty)), bits) | ||||
|     } | ||||
|     pub fn into_enum(this: Self) -> SimValue<Enum> | ||||
|     where | ||||
|         T: EnumType, | ||||
|     { | ||||
|         let (ty, bits) = Self::into_ty_and_bits(this); | ||||
|         SimValue::from_bits(Enum::from_canonical(ty.canonical()), bits) | ||||
|     } | ||||
|     pub fn to_enum(this: &Self) -> SimValue<Enum> | ||||
|     where | ||||
|         T: EnumType, | ||||
|     { | ||||
|         SimValue::from_bits( | ||||
|             Enum::from_canonical(Self::ty(this).canonical()), | ||||
|             Self::bits(&this).clone(), | ||||
|         ) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<T: Type> Deref for SimValue<T> { | ||||
|     type Target = T::SimValue; | ||||
| 
 | ||||
|     fn deref(&self) -> &Self::Target { | ||||
|         Self::value(self) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<T: Type> DerefMut for SimValue<T> { | ||||
|     fn deref_mut(&mut self) -> &mut Self::Target { | ||||
|         Self::value_mut(self) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<T: Type> fmt::Debug for SimValue<T> { | ||||
|     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||||
|         let inner = self.inner.share(); | ||||
|         f.debug_struct("SimValue") | ||||
|             .field("ty", &inner.ty) | ||||
|             .field("value", &inner.value) | ||||
|             .finish() | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<T: Type> ToExpr for SimValue<T> { | ||||
|     type Type = T; | ||||
| 
 | ||||
|     #[track_caller] | ||||
|     fn to_expr(&self) -> Expr<Self::Type> { | ||||
|         let inner = self.inner.share(); | ||||
|         inner.bits.cast_bits_to(inner.ty) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| pub trait SimValuePartialEq<T: Type = Self>: Type { | ||||
|     #[track_caller] | ||||
|     fn sim_value_eq(this: &SimValue<Self>, other: &SimValue<T>) -> bool; | ||||
| } | ||||
| 
 | ||||
| impl<T: SimValuePartialEq<U>, U: Type> PartialEq<SimValue<U>> for SimValue<T> { | ||||
|     #[track_caller] | ||||
|     fn eq(&self, other: &SimValue<U>) -> bool { | ||||
|         T::sim_value_eq(self, other) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<Width: Size> SimValuePartialEq<Self> for UIntType<Width> { | ||||
|     fn sim_value_eq(this: &SimValue<Self>, other: &SimValue<Self>) -> bool { | ||||
|         **this == **other | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<Width: Size> SimValuePartialEq<Self> for SIntType<Width> { | ||||
|     fn sim_value_eq(this: &SimValue<Self>, other: &SimValue<Self>) -> bool { | ||||
|         **this == **other | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl SimValuePartialEq<Bool> for Bool { | ||||
|     fn sim_value_eq(this: &SimValue<Self>, other: &SimValue<Bool>) -> bool { | ||||
|         **this == **other | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| pub trait ToSimValue: ToSimValueWithType<<Self as ToSimValue>::Type> { | ||||
|     type Type: Type; | ||||
| 
 | ||||
|     #[track_caller] | ||||
|     fn to_sim_value(&self) -> SimValue<Self::Type>; | ||||
|     #[track_caller] | ||||
|     fn into_sim_value(self) -> SimValue<Self::Type> | ||||
|     where | ||||
|         Self: Sized, | ||||
|     { | ||||
|         self.to_sim_value() | ||||
|     } | ||||
|     #[track_caller] | ||||
|     fn arc_into_sim_value(self: Arc<Self>) -> SimValue<Self::Type> { | ||||
|         self.to_sim_value() | ||||
|     } | ||||
|     #[track_caller] | ||||
|     fn arc_to_sim_value(self: &Arc<Self>) -> SimValue<Self::Type> { | ||||
|         self.to_sim_value() | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| pub trait ToSimValueWithType<T: Type> { | ||||
|     #[track_caller] | ||||
|     fn to_sim_value_with_type(&self, ty: T) -> SimValue<T>; | ||||
|     #[track_caller] | ||||
|     fn into_sim_value_with_type(self, ty: T) -> SimValue<T> | ||||
|     where | ||||
|         Self: Sized, | ||||
|     { | ||||
|         self.to_sim_value_with_type(ty) | ||||
|     } | ||||
|     #[track_caller] | ||||
|     fn arc_into_sim_value_with_type(self: Arc<Self>, ty: T) -> SimValue<T> { | ||||
|         self.to_sim_value_with_type(ty) | ||||
|     } | ||||
|     #[track_caller] | ||||
|     fn arc_to_sim_value_with_type(self: &Arc<Self>, ty: T) -> SimValue<T> { | ||||
|         self.to_sim_value_with_type(ty) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| macro_rules! forward_to_sim_value_with_type { | ||||
|     ([$($generics:tt)*] $ty:ty) => { | ||||
|         impl<$($generics)*> ToSimValueWithType<<Self as ToSimValue>::Type> for $ty { | ||||
|             fn to_sim_value_with_type(&self, ty: <Self as ToSimValue>::Type) -> SimValue<<Self as ToSimValue>::Type> { | ||||
|                 let retval = Self::to_sim_value(self); | ||||
|                 assert_eq!(SimValue::ty(&retval), ty); | ||||
|                 retval | ||||
|             } | ||||
|             #[track_caller] | ||||
|             fn into_sim_value_with_type(self, ty: <Self as ToSimValue>::Type) -> SimValue<<Self as ToSimValue>::Type> | ||||
|             where | ||||
|                 Self: Sized, | ||||
|             { | ||||
|                 let retval = Self::into_sim_value(self); | ||||
|                 assert_eq!(SimValue::ty(&retval), ty); | ||||
|                 retval | ||||
|             } | ||||
|             #[track_caller] | ||||
|             fn arc_into_sim_value_with_type(self: Arc<Self>, ty: <Self as ToSimValue>::Type) -> SimValue<<Self as ToSimValue>::Type> { | ||||
|                 let retval = Self::arc_into_sim_value(self); | ||||
|                 assert_eq!(SimValue::ty(&retval), ty); | ||||
|                 retval | ||||
|             } | ||||
|             #[track_caller] | ||||
|             fn arc_to_sim_value_with_type(self: &Arc<Self>, ty: <Self as ToSimValue>::Type) -> SimValue<<Self as ToSimValue>::Type> { | ||||
|                 let retval = Self::arc_to_sim_value(self); | ||||
|                 assert_eq!(SimValue::ty(&retval), ty); | ||||
|                 retval | ||||
|             } | ||||
|         } | ||||
|     }; | ||||
| } | ||||
| 
 | ||||
| impl<T: Type> ToSimValue for SimValue<T> { | ||||
|     type Type = T; | ||||
|     fn to_sim_value(&self) -> SimValue<Self::Type> { | ||||
|         self.clone() | ||||
|     } | ||||
| 
 | ||||
|     fn into_sim_value(self) -> SimValue<Self::Type> { | ||||
|         self | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| forward_to_sim_value_with_type!([T: Type] SimValue<T>); | ||||
| 
 | ||||
| impl<T: Type> ToSimValueWithType<T> for BitVec { | ||||
|     #[track_caller] | ||||
|     fn to_sim_value_with_type(&self, ty: T) -> SimValue<T> { | ||||
|         self.clone().into_sim_value_with_type(ty) | ||||
|     } | ||||
| 
 | ||||
|     #[track_caller] | ||||
|     fn into_sim_value_with_type(self, ty: T) -> SimValue<T> { | ||||
|         Arc::new(self).arc_into_sim_value_with_type(ty) | ||||
|     } | ||||
| 
 | ||||
|     #[track_caller] | ||||
|     fn arc_into_sim_value_with_type(self: Arc<Self>, ty: T) -> SimValue<T> { | ||||
|         SimValue::from_bits(ty, UIntValue::new_dyn(self)) | ||||
|     } | ||||
| 
 | ||||
|     #[track_caller] | ||||
|     fn arc_to_sim_value_with_type(self: &Arc<Self>, ty: T) -> SimValue<T> { | ||||
|         SimValue::from_bits(ty, UIntValue::new_dyn(self.clone())) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<T: Type> ToSimValueWithType<T> for bitvec::boxed::BitBox { | ||||
|     #[track_caller] | ||||
|     fn to_sim_value_with_type(&self, ty: T) -> SimValue<T> { | ||||
|         self.clone().into_sim_value_with_type(ty) | ||||
|     } | ||||
| 
 | ||||
|     #[track_caller] | ||||
|     fn into_sim_value_with_type(self, ty: T) -> SimValue<T> { | ||||
|         self.into_bitvec().into_sim_value_with_type(ty) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<T: Type> ToSimValueWithType<T> for BitSlice { | ||||
|     #[track_caller] | ||||
|     fn to_sim_value_with_type(&self, ty: T) -> SimValue<T> { | ||||
|         self.to_bitvec().into_sim_value_with_type(ty) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<This: ?Sized + ToSimValue> ToSimValue for &'_ This { | ||||
|     type Type = This::Type; | ||||
| 
 | ||||
|     fn to_sim_value(&self) -> SimValue<Self::Type> { | ||||
|         This::to_sim_value(self) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<This: ?Sized + ToSimValueWithType<T>, T: Type> ToSimValueWithType<T> for &'_ This { | ||||
|     fn to_sim_value_with_type(&self, ty: T) -> SimValue<T> { | ||||
|         This::to_sim_value_with_type(self, ty) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<This: ?Sized + ToSimValue> ToSimValue for &'_ mut This { | ||||
|     type Type = This::Type; | ||||
| 
 | ||||
|     fn to_sim_value(&self) -> SimValue<Self::Type> { | ||||
|         This::to_sim_value(self) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<This: ?Sized + ToSimValueWithType<T>, T: Type> ToSimValueWithType<T> for &'_ mut This { | ||||
|     fn to_sim_value_with_type(&self, ty: T) -> SimValue<T> { | ||||
|         This::to_sim_value_with_type(self, ty) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<This: ?Sized + ToSimValue> ToSimValue for Arc<This> { | ||||
|     type Type = This::Type; | ||||
| 
 | ||||
|     fn to_sim_value(&self) -> SimValue<Self::Type> { | ||||
|         This::arc_to_sim_value(self) | ||||
|     } | ||||
|     fn into_sim_value(self) -> SimValue<Self::Type> { | ||||
|         This::arc_into_sim_value(self) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<This: ?Sized + ToSimValueWithType<T>, T: Type> ToSimValueWithType<T> for Arc<This> { | ||||
|     fn to_sim_value_with_type(&self, ty: T) -> SimValue<T> { | ||||
|         This::arc_to_sim_value_with_type(self, ty) | ||||
|     } | ||||
|     fn into_sim_value_with_type(self, ty: T) -> SimValue<T> { | ||||
|         This::arc_into_sim_value_with_type(self, ty) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<This: ?Sized + ToSimValue + Send + Sync + 'static> ToSimValue | ||||
|     for crate::intern::Interned<This> | ||||
| { | ||||
|     type Type = This::Type; | ||||
|     fn to_sim_value(&self) -> SimValue<Self::Type> { | ||||
|         This::to_sim_value(self) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<This: ?Sized + ToSimValueWithType<T> + Send + Sync + 'static, T: Type> ToSimValueWithType<T> | ||||
|     for crate::intern::Interned<This> | ||||
| { | ||||
|     fn to_sim_value_with_type(&self, ty: T) -> SimValue<T> { | ||||
|         This::to_sim_value_with_type(self, ty) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<This: ToSimValue> ToSimValue for Box<This> { | ||||
|     type Type = This::Type; | ||||
| 
 | ||||
|     fn to_sim_value(&self) -> SimValue<Self::Type> { | ||||
|         This::to_sim_value(self) | ||||
|     } | ||||
|     fn into_sim_value(self) -> SimValue<Self::Type> { | ||||
|         This::into_sim_value(*self) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<This: ToSimValueWithType<T>, T: Type> ToSimValueWithType<T> for Box<This> { | ||||
|     fn to_sim_value_with_type(&self, ty: T) -> SimValue<T> { | ||||
|         This::to_sim_value_with_type(self, ty) | ||||
|     } | ||||
|     fn into_sim_value_with_type(self, ty: T) -> SimValue<T> { | ||||
|         This::into_sim_value_with_type(*self, ty) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<T: Type, Len: Size> SimValue<ArrayType<T, Len>> { | ||||
|     #[track_caller] | ||||
|     pub fn from_array_elements<I: IntoIterator<Item: ToSimValueWithType<T>>>( | ||||
|         ty: ArrayType<T, Len>, | ||||
|         elements: I, | ||||
|     ) -> Self { | ||||
|         let element_ty = ty.element(); | ||||
|         let elements = Vec::from_iter( | ||||
|             elements | ||||
|                 .into_iter() | ||||
|                 .map(|element| element.into_sim_value_with_type(element_ty)), | ||||
|         ); | ||||
|         assert_eq!(elements.len(), ty.len()); | ||||
|         SimValue::from_value(ty, elements.try_into().ok().expect("already checked len")) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<Element: ToSimValueWithType<T>, T: Type> ToSimValueWithType<Array<T>> for [Element] { | ||||
|     #[track_caller] | ||||
|     fn to_sim_value_with_type(&self, ty: Array<T>) -> SimValue<Array<T>> { | ||||
|         SimValue::from_array_elements(ty, self) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<Element: ToSimValue<Type: StaticType>> ToSimValue for [Element] { | ||||
|     type Type = Array<Element::Type>; | ||||
| 
 | ||||
|     #[track_caller] | ||||
|     fn to_sim_value(&self) -> SimValue<Self::Type> { | ||||
|         SimValue::from_array_elements(ArrayType::new_dyn(StaticType::TYPE, self.len()), self) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<Element: ToSimValueWithType<CanonicalType>> ToSimValueWithType<CanonicalType> for [Element] { | ||||
|     #[track_caller] | ||||
|     fn to_sim_value_with_type(&self, ty: CanonicalType) -> SimValue<CanonicalType> { | ||||
|         SimValue::into_canonical(SimValue::from_array_elements( | ||||
|             <Array>::from_canonical(ty), | ||||
|             self, | ||||
|         )) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<Element: ToSimValueWithType<T>, T: Type, const N: usize> ToSimValueWithType<Array<T, N>> | ||||
|     for [Element; N] | ||||
| where | ||||
|     ConstUsize<N>: KnownSize, | ||||
| { | ||||
|     #[track_caller] | ||||
|     fn to_sim_value_with_type(&self, ty: Array<T, N>) -> SimValue<Array<T, N>> { | ||||
|         SimValue::from_array_elements(ty, self) | ||||
|     } | ||||
|     #[track_caller] | ||||
|     fn into_sim_value_with_type(self, ty: Array<T, N>) -> SimValue<Array<T, N>> { | ||||
|         SimValue::from_array_elements(ty, self) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<Element: ToSimValue<Type: StaticType>, const N: usize> ToSimValue for [Element; N] | ||||
| where | ||||
|     ConstUsize<N>: KnownSize, | ||||
| { | ||||
|     type Type = Array<Element::Type, N>; | ||||
| 
 | ||||
|     fn to_sim_value(&self) -> SimValue<Self::Type> { | ||||
|         SimValue::from_array_elements(StaticType::TYPE, self) | ||||
|     } | ||||
| 
 | ||||
|     fn into_sim_value(self) -> SimValue<Self::Type> { | ||||
|         SimValue::from_array_elements(StaticType::TYPE, self) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<Element: ToSimValueWithType<T>, T: Type, const N: usize> ToSimValueWithType<Array<T>> | ||||
|     for [Element; N] | ||||
| { | ||||
|     #[track_caller] | ||||
|     fn to_sim_value_with_type(&self, ty: Array<T>) -> SimValue<Array<T>> { | ||||
|         SimValue::from_array_elements(ty, self) | ||||
|     } | ||||
|     #[track_caller] | ||||
|     fn into_sim_value_with_type(self, ty: Array<T>) -> SimValue<Array<T>> { | ||||
|         SimValue::from_array_elements(ty, self) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<Element: ToSimValueWithType<CanonicalType>, const N: usize> ToSimValueWithType<CanonicalType> | ||||
|     for [Element; N] | ||||
| { | ||||
|     #[track_caller] | ||||
|     fn to_sim_value_with_type(&self, ty: CanonicalType) -> SimValue<CanonicalType> { | ||||
|         SimValue::into_canonical(SimValue::from_array_elements( | ||||
|             <Array>::from_canonical(ty), | ||||
|             self, | ||||
|         )) | ||||
|     } | ||||
|     #[track_caller] | ||||
|     fn into_sim_value_with_type(self, ty: CanonicalType) -> SimValue<CanonicalType> { | ||||
|         SimValue::into_canonical(SimValue::from_array_elements( | ||||
|             <Array>::from_canonical(ty), | ||||
|             self, | ||||
|         )) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<Element: ToSimValueWithType<T>, T: Type> ToSimValueWithType<Array<T>> for Vec<Element> { | ||||
|     #[track_caller] | ||||
|     fn to_sim_value_with_type(&self, ty: Array<T>) -> SimValue<Array<T>> { | ||||
|         SimValue::from_array_elements(ty, self) | ||||
|     } | ||||
|     #[track_caller] | ||||
|     fn into_sim_value_with_type(self, ty: Array<T>) -> SimValue<Array<T>> { | ||||
|         SimValue::from_array_elements(ty, self) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<Element: ToSimValue<Type: StaticType>> ToSimValue for Vec<Element> { | ||||
|     type Type = Array<Element::Type>; | ||||
| 
 | ||||
|     fn to_sim_value(&self) -> SimValue<Self::Type> { | ||||
|         SimValue::from_array_elements(ArrayType::new_dyn(StaticType::TYPE, self.len()), self) | ||||
|     } | ||||
| 
 | ||||
|     fn into_sim_value(self) -> SimValue<Self::Type> { | ||||
|         SimValue::from_array_elements(ArrayType::new_dyn(StaticType::TYPE, self.len()), self) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<Element: ToSimValueWithType<CanonicalType>> ToSimValueWithType<CanonicalType> | ||||
|     for Vec<Element> | ||||
| { | ||||
|     #[track_caller] | ||||
|     fn to_sim_value_with_type(&self, ty: CanonicalType) -> SimValue<CanonicalType> { | ||||
|         SimValue::into_canonical(SimValue::from_array_elements( | ||||
|             <Array>::from_canonical(ty), | ||||
|             self, | ||||
|         )) | ||||
|     } | ||||
|     #[track_caller] | ||||
|     fn into_sim_value_with_type(self, ty: CanonicalType) -> SimValue<CanonicalType> { | ||||
|         SimValue::into_canonical(SimValue::from_array_elements( | ||||
|             <Array>::from_canonical(ty), | ||||
|             self, | ||||
|         )) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<Element: ToSimValueWithType<T>, T: Type> ToSimValueWithType<Array<T>> for Box<[Element]> { | ||||
|     #[track_caller] | ||||
|     fn to_sim_value_with_type(&self, ty: Array<T>) -> SimValue<Array<T>> { | ||||
|         SimValue::from_array_elements(ty, self) | ||||
|     } | ||||
|     #[track_caller] | ||||
|     fn into_sim_value_with_type(self, ty: Array<T>) -> SimValue<Array<T>> { | ||||
|         SimValue::from_array_elements(ty, self) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<Element: ToSimValue<Type: StaticType>> ToSimValue for Box<[Element]> { | ||||
|     type Type = Array<Element::Type>; | ||||
| 
 | ||||
|     fn to_sim_value(&self) -> SimValue<Self::Type> { | ||||
|         SimValue::from_array_elements(ArrayType::new_dyn(StaticType::TYPE, self.len()), self) | ||||
|     } | ||||
| 
 | ||||
|     fn into_sim_value(self) -> SimValue<Self::Type> { | ||||
|         SimValue::from_array_elements(ArrayType::new_dyn(StaticType::TYPE, self.len()), self) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<Element: ToSimValueWithType<CanonicalType>> ToSimValueWithType<CanonicalType> | ||||
|     for Box<[Element]> | ||||
| { | ||||
|     #[track_caller] | ||||
|     fn to_sim_value_with_type(&self, ty: CanonicalType) -> SimValue<CanonicalType> { | ||||
|         SimValue::into_canonical(SimValue::from_array_elements( | ||||
|             <Array>::from_canonical(ty), | ||||
|             self, | ||||
|         )) | ||||
|     } | ||||
|     #[track_caller] | ||||
|     fn into_sim_value_with_type(self, ty: CanonicalType) -> SimValue<CanonicalType> { | ||||
|         SimValue::into_canonical(SimValue::from_array_elements( | ||||
|             <Array>::from_canonical(ty), | ||||
|             self, | ||||
|         )) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<T: Type> ToSimValue for Expr<T> { | ||||
|     type Type = T; | ||||
|     #[track_caller] | ||||
|     fn to_sim_value(&self) -> SimValue<Self::Type> { | ||||
|         SimValue::from_bitslice( | ||||
|             Expr::ty(*self), | ||||
|             &crate::expr::ToLiteralBits::to_literal_bits(self) | ||||
|                 .expect("must be a literal expression"), | ||||
|         ) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| forward_to_sim_value_with_type!([T: Type] Expr<T>); | ||||
| 
 | ||||
| macro_rules! impl_to_sim_value_for_bool_like { | ||||
|     ($ty:ident) => { | ||||
|         impl ToSimValueWithType<$ty> for bool { | ||||
|             fn to_sim_value_with_type(&self, ty: $ty) -> SimValue<$ty> { | ||||
|                 SimValue::from_value(ty, *self) | ||||
|             } | ||||
|         } | ||||
|     }; | ||||
| } | ||||
| 
 | ||||
| impl ToSimValue for bool { | ||||
|     type Type = Bool; | ||||
| 
 | ||||
|     fn to_sim_value(&self) -> SimValue<Self::Type> { | ||||
|         SimValue::from_value(Bool, *self) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl_to_sim_value_for_bool_like!(Bool); | ||||
| impl_to_sim_value_for_bool_like!(AsyncReset); | ||||
| impl_to_sim_value_for_bool_like!(SyncReset); | ||||
| impl_to_sim_value_for_bool_like!(Reset); | ||||
| impl_to_sim_value_for_bool_like!(Clock); | ||||
| 
 | ||||
| impl ToSimValueWithType<CanonicalType> for bool { | ||||
|     #[track_caller] | ||||
|     fn to_sim_value_with_type(&self, ty: CanonicalType) -> SimValue<CanonicalType> { | ||||
|         match ty { | ||||
|             CanonicalType::UInt(_) | ||||
|             | CanonicalType::SInt(_) | ||||
|             | CanonicalType::Array(_) | ||||
|             | CanonicalType::Enum(_) | ||||
|             | CanonicalType::Bundle(_) | ||||
|             | CanonicalType::PhantomConst(_) => { | ||||
|                 panic!("can't create SimValue from bool: expected value of type: {ty:?}"); | ||||
|             } | ||||
|             CanonicalType::Bool(_) | ||||
|             | CanonicalType::AsyncReset(_) | ||||
|             | CanonicalType::SyncReset(_) | ||||
|             | CanonicalType::Reset(_) | ||||
|             | CanonicalType::Clock(_) => { | ||||
|                 SimValue::from_bits(ty, UIntValue::new(Arc::new(BitVec::repeat(*self, 1)))) | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| macro_rules! impl_to_sim_value_for_primitive_int { | ||||
|     ($prim:ident) => { | ||||
|         impl ToSimValue for $prim { | ||||
|             type Type = <$prim as ToExpr>::Type; | ||||
| 
 | ||||
|             #[track_caller] | ||||
|             fn to_sim_value( | ||||
|                 &self, | ||||
|             ) -> SimValue<Self::Type> { | ||||
|                 SimValue::from_value(StaticType::TYPE, (*self).into()) | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         forward_to_sim_value_with_type!([] $prim); | ||||
| 
 | ||||
|         impl ToSimValueWithType<<<$prim as ToExpr>::Type as IntType>::Dyn> for $prim { | ||||
|             #[track_caller] | ||||
|             fn to_sim_value_with_type( | ||||
|                 &self, | ||||
|                 ty: <<$prim as ToExpr>::Type as IntType>::Dyn, | ||||
|             ) -> SimValue<<<$prim as ToExpr>::Type as IntType>::Dyn> { | ||||
|                 SimValue::from_value( | ||||
|                     ty, | ||||
|                     <<$prim as ToExpr>::Type as Type>::SimValue::from(*self).as_dyn_int(), | ||||
|                 ) | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         impl ToSimValueWithType<CanonicalType> for $prim { | ||||
|             #[track_caller] | ||||
|             fn to_sim_value_with_type(&self, ty: CanonicalType) -> SimValue<CanonicalType> { | ||||
|                 let ty: <<$prim as ToExpr>::Type as IntType>::Dyn = Type::from_canonical(ty); | ||||
|                 SimValue::into_canonical(self.to_sim_value_with_type(ty)) | ||||
|             } | ||||
|         } | ||||
|     }; | ||||
| } | ||||
| 
 | ||||
| impl_to_sim_value_for_primitive_int!(u8); | ||||
| impl_to_sim_value_for_primitive_int!(u16); | ||||
| impl_to_sim_value_for_primitive_int!(u32); | ||||
| impl_to_sim_value_for_primitive_int!(u64); | ||||
| impl_to_sim_value_for_primitive_int!(u128); | ||||
| impl_to_sim_value_for_primitive_int!(usize); | ||||
| impl_to_sim_value_for_primitive_int!(i8); | ||||
| impl_to_sim_value_for_primitive_int!(i16); | ||||
| impl_to_sim_value_for_primitive_int!(i32); | ||||
| impl_to_sim_value_for_primitive_int!(i64); | ||||
| impl_to_sim_value_for_primitive_int!(i128); | ||||
| impl_to_sim_value_for_primitive_int!(isize); | ||||
| 
 | ||||
| macro_rules! impl_to_sim_value_for_int_value { | ||||
|     ($IntValue:ident, $Int:ident, $IntType:ident) => { | ||||
|         impl<Width: Size> ToSimValue for $IntValue<Width> { | ||||
|             type Type = $IntType<Width>; | ||||
| 
 | ||||
|             fn to_sim_value(&self) -> SimValue<Self::Type> { | ||||
|                 SimValue::from_value(self.ty(), self.clone()) | ||||
|             } | ||||
| 
 | ||||
|             fn into_sim_value(self) -> SimValue<Self::Type> { | ||||
|                 SimValue::from_value(self.ty(), self) | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         impl<Width: Size> ToSimValueWithType<$IntType<Width>> for $IntValue<Width> { | ||||
|             fn to_sim_value_with_type(&self, ty: $IntType<Width>) -> SimValue<$IntType<Width>> { | ||||
|                 SimValue::from_value(ty, self.clone()) | ||||
|             } | ||||
| 
 | ||||
|             fn into_sim_value_with_type(self, ty: $IntType<Width>) -> SimValue<$IntType<Width>> { | ||||
|                 SimValue::from_value(ty, self) | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         impl<Width: KnownSize> ToSimValueWithType<$Int> for $IntValue<Width> { | ||||
|             fn to_sim_value_with_type(&self, ty: $Int) -> SimValue<$Int> { | ||||
|                 self.bits().to_sim_value_with_type(ty) | ||||
|             } | ||||
| 
 | ||||
|             fn into_sim_value_with_type(self, ty: $Int) -> SimValue<$Int> { | ||||
|                 self.into_bits().into_sim_value_with_type(ty) | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         impl<Width: Size> ToSimValueWithType<CanonicalType> for $IntValue<Width> { | ||||
|             #[track_caller] | ||||
|             fn to_sim_value_with_type(&self, ty: CanonicalType) -> SimValue<CanonicalType> { | ||||
|                 SimValue::into_canonical( | ||||
|                     self.to_sim_value_with_type($IntType::<Width>::from_canonical(ty)), | ||||
|                 ) | ||||
|             } | ||||
| 
 | ||||
|             #[track_caller] | ||||
|             fn into_sim_value_with_type(self, ty: CanonicalType) -> SimValue<CanonicalType> { | ||||
|                 SimValue::into_canonical( | ||||
|                     self.into_sim_value_with_type($IntType::<Width>::from_canonical(ty)), | ||||
|                 ) | ||||
|             } | ||||
|         } | ||||
|     }; | ||||
| } | ||||
| 
 | ||||
| impl_to_sim_value_for_int_value!(UIntValue, UInt, UIntType); | ||||
| impl_to_sim_value_for_int_value!(SIntValue, SInt, SIntType); | ||||
|  | @ -14,9 +14,10 @@ use crate::{ | |||
|         TraceModuleIO, TraceReg, TraceSInt, TraceScalar, TraceScalarId, TraceScope, TraceSyncReset, | ||||
|         TraceUInt, TraceWire, TraceWriter, TraceWriterDecls, | ||||
|     }, | ||||
|     util::HashMap, | ||||
| }; | ||||
| use bitvec::{order::Lsb0, slice::BitSlice}; | ||||
| use hashbrown::{hash_map::Entry, HashMap}; | ||||
| use hashbrown::hash_map::Entry; | ||||
| use std::{ | ||||
|     fmt::{self, Write as _}, | ||||
|     io, mem, | ||||
|  |  | |||
|  | @ -2,9 +2,8 @@ | |||
| // See Notices.txt for copyright information
 | ||||
| use crate::{ | ||||
|     intern::{Intern, Interned}, | ||||
|     util::DebugAsDisplay, | ||||
|     util::{DebugAsDisplay, HashMap}, | ||||
| }; | ||||
| use hashbrown::HashMap; | ||||
| use std::{cell::RefCell, fmt, num::NonZeroUsize, panic, path::Path}; | ||||
| 
 | ||||
| #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] | ||||
|  | @ -97,7 +96,7 @@ impl NormalizeFilesForTestsState { | |||
|     fn new() -> Self { | ||||
|         Self { | ||||
|             test_position: panic::Location::caller(), | ||||
|             file_pattern_matches: HashMap::new(), | ||||
|             file_pattern_matches: HashMap::default(), | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | @ -143,7 +142,7 @@ impl From<&'_ panic::Location<'_>> for SourceLocation { | |||
|                         map.entry_ref(file) | ||||
|                             .or_insert_with(|| NormalizedFileForTestState { | ||||
|                                 file_name_id: NonZeroUsize::new(len + 1).unwrap(), | ||||
|                                 positions_map: HashMap::new(), | ||||
|                                 positions_map: HashMap::default(), | ||||
|                             }); | ||||
|                     file_str = m.generate_file_name(file_state.file_name_id); | ||||
|                     file = &file_str; | ||||
|  |  | |||
|  | @ -3,9 +3,9 @@ | |||
| use crate::{ | ||||
|     cli::{FormalArgs, FormalMode, FormalOutput, RunPhase}, | ||||
|     firrtl::ExportOptions, | ||||
|     util::HashMap, | ||||
| }; | ||||
| use clap::Parser; | ||||
| use hashbrown::HashMap; | ||||
| use serde::Deserialize; | ||||
| use std::{ | ||||
|     fmt::Write, | ||||
|  | @ -87,7 +87,7 @@ fn get_assert_formal_target_path(test_name: &dyn std::fmt::Display) -> PathBuf { | |||
|     let index = *DIRS | ||||
|         .lock() | ||||
|         .unwrap() | ||||
|         .get_or_insert_with(HashMap::new) | ||||
|         .get_or_insert_with(HashMap::default) | ||||
|         .entry_ref(&dir) | ||||
|         .and_modify(|v| *v += 1) | ||||
|         .or_insert(0); | ||||
|  |  | |||
|  | @ -7,12 +7,19 @@ use crate::{ | |||
|     clock::Clock, | ||||
|     enum_::Enum, | ||||
|     expr::Expr, | ||||
|     int::{Bool, SInt, UInt}, | ||||
|     int::{Bool, SInt, UInt, UIntValue}, | ||||
|     intern::{Intern, Interned}, | ||||
|     phantom_const::PhantomConst, | ||||
|     reset::{AsyncReset, Reset, SyncReset}, | ||||
|     sim::value::{SimValue, ToSimValueWithType}, | ||||
|     source_location::SourceLocation, | ||||
|     util::ConstUsize, | ||||
| }; | ||||
| use std::{fmt, hash::Hash, iter::FusedIterator, ops::Index}; | ||||
| use bitvec::slice::BitSlice; | ||||
| use serde::{de::DeserializeOwned, Deserialize, Deserializer, Serialize, Serializer}; | ||||
| use std::{fmt, hash::Hash, iter::FusedIterator, ops::Index, sync::Arc}; | ||||
| 
 | ||||
| pub(crate) mod serde_impls; | ||||
| 
 | ||||
| #[derive(Copy, Clone, Hash, PartialEq, Eq, Debug)] | ||||
| #[non_exhaustive] | ||||
|  | @ -35,6 +42,7 @@ pub enum CanonicalType { | |||
|     SyncReset(SyncReset), | ||||
|     Reset(Reset), | ||||
|     Clock(Clock), | ||||
|     PhantomConst(PhantomConst), | ||||
| } | ||||
| 
 | ||||
| impl fmt::Debug for CanonicalType { | ||||
|  | @ -50,10 +58,29 @@ impl fmt::Debug for CanonicalType { | |||
|             Self::SyncReset(v) => v.fmt(f), | ||||
|             Self::Reset(v) => v.fmt(f), | ||||
|             Self::Clock(v) => v.fmt(f), | ||||
|             Self::PhantomConst(v) => v.fmt(f), | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl Serialize for CanonicalType { | ||||
|     fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> | ||||
|     where | ||||
|         S: Serializer, | ||||
|     { | ||||
|         serde_impls::SerdeCanonicalType::from(*self).serialize(serializer) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<'de> Deserialize<'de> for CanonicalType { | ||||
|     fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> | ||||
|     where | ||||
|         D: serde::Deserializer<'de>, | ||||
|     { | ||||
|         Ok(serde_impls::SerdeCanonicalType::deserialize(deserializer)?.into()) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl CanonicalType { | ||||
|     pub fn type_properties(self) -> TypeProperties { | ||||
|         match self { | ||||
|  | @ -67,6 +94,7 @@ impl CanonicalType { | |||
|             CanonicalType::SyncReset(v) => v.type_properties(), | ||||
|             CanonicalType::Reset(v) => v.type_properties(), | ||||
|             CanonicalType::Clock(v) => v.type_properties(), | ||||
|             CanonicalType::PhantomConst(v) => v.type_properties(), | ||||
|         } | ||||
|     } | ||||
|     pub fn is_passive(self) -> bool { | ||||
|  | @ -143,9 +171,18 @@ impl CanonicalType { | |||
|                 }; | ||||
|                 lhs.can_connect(rhs) | ||||
|             } | ||||
|             CanonicalType::PhantomConst(lhs) => { | ||||
|                 let CanonicalType::PhantomConst(rhs) = rhs else { | ||||
|                     return false; | ||||
|                 }; | ||||
|                 lhs.can_connect(rhs) | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|     pub(crate) fn as_serde_unexpected_str(self) -> &'static str { | ||||
|         serde_impls::SerdeCanonicalType::from(self).as_serde_unexpected_str() | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| pub trait MatchVariantAndInactiveScope: Sized { | ||||
|     type MatchVariant: 'static + Send + Sync; | ||||
|  | @ -166,7 +203,7 @@ impl<T: 'static + Send + Sync> MatchVariantAndInactiveScope for MatchVariantWith | |||
| } | ||||
| 
 | ||||
| pub trait FillInDefaultedGenerics { | ||||
|     type Type: Type; | ||||
|     type Type; | ||||
|     fn fill_in_defaulted_generics(self) -> Self::Type; | ||||
| } | ||||
| 
 | ||||
|  | @ -178,6 +215,22 @@ impl<T: Type> FillInDefaultedGenerics for T { | |||
|     } | ||||
| } | ||||
| 
 | ||||
| impl FillInDefaultedGenerics for usize { | ||||
|     type Type = usize; | ||||
| 
 | ||||
|     fn fill_in_defaulted_generics(self) -> Self::Type { | ||||
|         self | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<const V: usize> FillInDefaultedGenerics for ConstUsize<V> { | ||||
|     type Type = ConstUsize<V>; | ||||
| 
 | ||||
|     fn fill_in_defaulted_generics(self) -> Self::Type { | ||||
|         self | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| mod sealed { | ||||
|     pub trait TypeOrDefaultSealed {} | ||||
|     pub trait BaseTypeSealed {} | ||||
|  | @ -195,6 +248,34 @@ macro_rules! impl_base_type { | |||
|     }; | ||||
| } | ||||
| 
 | ||||
| macro_rules! impl_base_type_serde { | ||||
|     ($name:ident, $expected:literal) => { | ||||
|         impl Serialize for $name { | ||||
|             fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> | ||||
|             where | ||||
|                 S: Serializer, | ||||
|             { | ||||
|                 self.canonical().serialize(serializer) | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         impl<'de> Deserialize<'de> for $name { | ||||
|             fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> | ||||
|             where | ||||
|                 D: Deserializer<'de>, | ||||
|             { | ||||
|                 match CanonicalType::deserialize(deserializer)? { | ||||
|                     CanonicalType::$name(retval) => Ok(retval), | ||||
|                     ty => Err(serde::de::Error::invalid_value( | ||||
|                         serde::de::Unexpected::Other(ty.as_serde_unexpected_str()), | ||||
|                         &$expected, | ||||
|                     )), | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     }; | ||||
| } | ||||
| 
 | ||||
| impl_base_type!(UInt); | ||||
| impl_base_type!(SInt); | ||||
| impl_base_type!(Bool); | ||||
|  | @ -205,6 +286,15 @@ impl_base_type!(AsyncReset); | |||
| impl_base_type!(SyncReset); | ||||
| impl_base_type!(Reset); | ||||
| impl_base_type!(Clock); | ||||
| impl_base_type!(PhantomConst); | ||||
| 
 | ||||
| impl_base_type_serde!(Bool, "a Bool"); | ||||
| impl_base_type_serde!(Enum, "an Enum"); | ||||
| impl_base_type_serde!(Bundle, "a Bundle"); | ||||
| impl_base_type_serde!(AsyncReset, "an AsyncReset"); | ||||
| impl_base_type_serde!(SyncReset, "a SyncReset"); | ||||
| impl_base_type_serde!(Reset, "a Reset"); | ||||
| impl_base_type_serde!(Clock, "a Clock"); | ||||
| 
 | ||||
| impl sealed::BaseTypeSealed for CanonicalType {} | ||||
| 
 | ||||
|  | @ -240,6 +330,7 @@ pub trait Type: | |||
| { | ||||
|     type BaseType: BaseType; | ||||
|     type MaskType: Type<MaskType = Self::MaskType>; | ||||
|     type SimValue: fmt::Debug + Clone + 'static + ToSimValueWithType<Self>; | ||||
|     type MatchVariant: 'static + Send + Sync; | ||||
|     type MatchActiveScope; | ||||
|     type MatchVariantAndInactiveScope: MatchVariantAndInactiveScope< | ||||
|  | @ -257,9 +348,22 @@ pub trait Type: | |||
|     fn canonical(&self) -> CanonicalType; | ||||
|     fn from_canonical(canonical_type: CanonicalType) -> Self; | ||||
|     fn source_location() -> SourceLocation; | ||||
|     fn sim_value_from_bits(&self, bits: &BitSlice) -> Self::SimValue; | ||||
|     fn sim_value_clone_from_bits(&self, value: &mut Self::SimValue, bits: &BitSlice); | ||||
|     fn sim_value_to_bits(&self, value: &Self::SimValue, bits: &mut BitSlice); | ||||
| } | ||||
| 
 | ||||
| pub trait BaseType: Type<BaseType = Self> + sealed::BaseTypeSealed + Into<CanonicalType> {} | ||||
| pub trait BaseType: | ||||
|     Type< | ||||
|         BaseType = Self, | ||||
|         MaskType: Serialize + DeserializeOwned, | ||||
|         SimValue: Serialize + DeserializeOwned, | ||||
|     > + sealed::BaseTypeSealed | ||||
|     + Into<CanonicalType> | ||||
|     + Serialize | ||||
|     + DeserializeOwned | ||||
| { | ||||
| } | ||||
| 
 | ||||
| macro_rules! impl_match_variant_as_self { | ||||
|     () => { | ||||
|  | @ -286,6 +390,7 @@ pub trait TypeWithDeref: Type { | |||
| impl Type for CanonicalType { | ||||
|     type BaseType = CanonicalType; | ||||
|     type MaskType = CanonicalType; | ||||
|     type SimValue = OpaqueSimValue; | ||||
|     impl_match_variant_as_self!(); | ||||
|     fn mask_type(&self) -> Self::MaskType { | ||||
|         match self { | ||||
|  | @ -299,6 +404,7 @@ impl Type for CanonicalType { | |||
|             CanonicalType::SyncReset(v) => v.mask_type().canonical(), | ||||
|             CanonicalType::Reset(v) => v.mask_type().canonical(), | ||||
|             CanonicalType::Clock(v) => v.mask_type().canonical(), | ||||
|             CanonicalType::PhantomConst(v) => v.mask_type().canonical(), | ||||
|         } | ||||
|     } | ||||
|     fn canonical(&self) -> CanonicalType { | ||||
|  | @ -310,9 +416,60 @@ impl Type for CanonicalType { | |||
|     fn source_location() -> SourceLocation { | ||||
|         SourceLocation::builtin() | ||||
|     } | ||||
|     fn sim_value_from_bits(&self, bits: &BitSlice) -> Self::SimValue { | ||||
|         assert_eq!(bits.len(), self.bit_width()); | ||||
|         OpaqueSimValue::from_bitslice(bits) | ||||
|     } | ||||
|     fn sim_value_clone_from_bits(&self, value: &mut Self::SimValue, bits: &BitSlice) { | ||||
|         assert_eq!(bits.len(), self.bit_width()); | ||||
|         assert_eq!(value.bit_width(), self.bit_width()); | ||||
|         value.bits_mut().bits_mut().copy_from_bitslice(bits); | ||||
|     } | ||||
|     fn sim_value_to_bits(&self, value: &Self::SimValue, bits: &mut BitSlice) { | ||||
|         assert_eq!(bits.len(), self.bit_width()); | ||||
|         assert_eq!(value.bit_width(), self.bit_width()); | ||||
|         bits.copy_from_bitslice(value.bits().bits()); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| pub trait StaticType: Type { | ||||
| #[derive(Clone, PartialEq, Eq, Hash, Debug, Serialize, Deserialize)] | ||||
| pub struct OpaqueSimValue { | ||||
|     bits: UIntValue, | ||||
| } | ||||
| 
 | ||||
| impl OpaqueSimValue { | ||||
|     pub fn bit_width(&self) -> usize { | ||||
|         self.bits.width() | ||||
|     } | ||||
|     pub fn bits(&self) -> &UIntValue { | ||||
|         &self.bits | ||||
|     } | ||||
|     pub fn bits_mut(&mut self) -> &mut UIntValue { | ||||
|         &mut self.bits | ||||
|     } | ||||
|     pub fn into_bits(self) -> UIntValue { | ||||
|         self.bits | ||||
|     } | ||||
|     pub fn from_bits(bits: UIntValue) -> Self { | ||||
|         Self { bits } | ||||
|     } | ||||
|     pub fn from_bitslice(v: &BitSlice) -> Self { | ||||
|         Self { | ||||
|             bits: UIntValue::new(Arc::new(v.to_bitvec())), | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<T: Type<SimValue = OpaqueSimValue>> ToSimValueWithType<T> for OpaqueSimValue { | ||||
|     fn to_sim_value_with_type(&self, ty: T) -> SimValue<T> { | ||||
|         SimValue::from_value(ty, self.clone()) | ||||
|     } | ||||
|     fn into_sim_value_with_type(self, ty: T) -> SimValue<T> { | ||||
|         SimValue::from_value(ty, self) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| pub trait StaticType: Type + Default { | ||||
|     const TYPE: Self; | ||||
|     const MASK_TYPE: Self::MaskType; | ||||
|     const TYPE_PROPERTIES: TypeProperties; | ||||
|  |  | |||
							
								
								
									
										130
									
								
								crates/fayalite/src/ty/serde_impls.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										130
									
								
								crates/fayalite/src/ty/serde_impls.rs
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,130 @@ | |||
| // SPDX-License-Identifier: LGPL-3.0-or-later
 | ||||
| // See Notices.txt for copyright information
 | ||||
| 
 | ||||
| use crate::{ | ||||
|     array::Array, | ||||
|     bundle::{Bundle, BundleType}, | ||||
|     clock::Clock, | ||||
|     enum_::{Enum, EnumType}, | ||||
|     int::{Bool, SInt, UInt}, | ||||
|     intern::Interned, | ||||
|     phantom_const::{PhantomConstCanonicalValue, PhantomConstValue}, | ||||
|     prelude::PhantomConst, | ||||
|     reset::{AsyncReset, Reset, SyncReset}, | ||||
|     ty::{BaseType, CanonicalType}, | ||||
| }; | ||||
| use serde::{Deserialize, Deserializer, Serialize, Serializer}; | ||||
| 
 | ||||
| pub(crate) struct SerdePhantomConst<T>(pub T); | ||||
| 
 | ||||
| impl<T: ?Sized + PhantomConstValue> Serialize for SerdePhantomConst<Interned<T>> { | ||||
|     fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> | ||||
|     where | ||||
|         S: Serializer, | ||||
|     { | ||||
|         self.0.serialize(serializer) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<'de, T: ?Sized + PhantomConstValue> Deserialize<'de> for SerdePhantomConst<Interned<T>> { | ||||
|     fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> | ||||
|     where | ||||
|         D: Deserializer<'de>, | ||||
|     { | ||||
|         T::deserialize_value(deserializer).map(Self) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[derive(Serialize, Deserialize)] | ||||
| #[serde(rename = "CanonicalType")] | ||||
| pub(crate) enum SerdeCanonicalType< | ||||
|     ArrayElement = CanonicalType, | ||||
|     ThePhantomConst = SerdePhantomConst<Interned<PhantomConstCanonicalValue>>, | ||||
| > { | ||||
|     UInt { | ||||
|         width: usize, | ||||
|     }, | ||||
|     SInt { | ||||
|         width: usize, | ||||
|     }, | ||||
|     Bool, | ||||
|     Array { | ||||
|         element: ArrayElement, | ||||
|         len: usize, | ||||
|     }, | ||||
|     Enum { | ||||
|         variants: Interned<[crate::enum_::EnumVariant]>, | ||||
|     }, | ||||
|     Bundle { | ||||
|         fields: Interned<[crate::bundle::BundleField]>, | ||||
|     }, | ||||
|     AsyncReset, | ||||
|     SyncReset, | ||||
|     Reset, | ||||
|     Clock, | ||||
|     PhantomConst(ThePhantomConst), | ||||
| } | ||||
| 
 | ||||
| impl<ArrayElement, PhantomConstInner> SerdeCanonicalType<ArrayElement, PhantomConstInner> { | ||||
|     pub(crate) fn as_serde_unexpected_str(&self) -> &'static str { | ||||
|         match self { | ||||
|             Self::UInt { .. } => "a UInt", | ||||
|             Self::SInt { .. } => "a SInt", | ||||
|             Self::Bool => "a Bool", | ||||
|             Self::Array { .. } => "an Array", | ||||
|             Self::Enum { .. } => "an Enum", | ||||
|             Self::Bundle { .. } => "a Bundle", | ||||
|             Self::AsyncReset => "an AsyncReset", | ||||
|             Self::SyncReset => "a SyncReset", | ||||
|             Self::Reset => "a Reset", | ||||
|             Self::Clock => "a Clock", | ||||
|             Self::PhantomConst(_) => "a PhantomConst", | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<T: BaseType> From<T> for SerdeCanonicalType { | ||||
|     fn from(ty: T) -> Self { | ||||
|         let ty: CanonicalType = ty.into(); | ||||
|         match ty { | ||||
|             CanonicalType::UInt(ty) => Self::UInt { width: ty.width() }, | ||||
|             CanonicalType::SInt(ty) => Self::SInt { width: ty.width() }, | ||||
|             CanonicalType::Bool(Bool {}) => Self::Bool, | ||||
|             CanonicalType::Array(ty) => Self::Array { | ||||
|                 element: ty.element(), | ||||
|                 len: ty.len(), | ||||
|             }, | ||||
|             CanonicalType::Enum(ty) => Self::Enum { | ||||
|                 variants: ty.variants(), | ||||
|             }, | ||||
|             CanonicalType::Bundle(ty) => Self::Bundle { | ||||
|                 fields: ty.fields(), | ||||
|             }, | ||||
|             CanonicalType::AsyncReset(AsyncReset {}) => Self::AsyncReset, | ||||
|             CanonicalType::SyncReset(SyncReset {}) => Self::SyncReset, | ||||
|             CanonicalType::Reset(Reset {}) => Self::Reset, | ||||
|             CanonicalType::Clock(Clock {}) => Self::Clock, | ||||
|             CanonicalType::PhantomConst(ty) => Self::PhantomConst(SerdePhantomConst(ty.get())), | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl From<SerdeCanonicalType> for CanonicalType { | ||||
|     fn from(ty: SerdeCanonicalType) -> Self { | ||||
|         match ty { | ||||
|             SerdeCanonicalType::UInt { width } => Self::UInt(UInt::new(width)), | ||||
|             SerdeCanonicalType::SInt { width } => Self::SInt(SInt::new(width)), | ||||
|             SerdeCanonicalType::Bool => Self::Bool(Bool), | ||||
|             SerdeCanonicalType::Array { element, len } => Self::Array(Array::new(element, len)), | ||||
|             SerdeCanonicalType::Enum { variants } => Self::Enum(Enum::new(variants)), | ||||
|             SerdeCanonicalType::Bundle { fields } => Self::Bundle(Bundle::new(fields)), | ||||
|             SerdeCanonicalType::AsyncReset => Self::AsyncReset(AsyncReset), | ||||
|             SerdeCanonicalType::SyncReset => Self::SyncReset(SyncReset), | ||||
|             SerdeCanonicalType::Reset => Self::Reset(Reset), | ||||
|             SerdeCanonicalType::Clock => Self::Clock(Clock), | ||||
|             SerdeCanonicalType::PhantomConst(value) => { | ||||
|                 Self::PhantomConst(PhantomConst::new(value.0)) | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | @ -1,12 +1,23 @@ | |||
| // SPDX-License-Identifier: LGPL-3.0-or-later
 | ||||
| // See Notices.txt for copyright information
 | ||||
| 
 | ||||
| pub(crate) mod alternating_cell; | ||||
| mod const_bool; | ||||
| mod const_cmp; | ||||
| mod const_usize; | ||||
| mod misc; | ||||
| mod scoped_ref; | ||||
| pub(crate) mod streaming_read_utf8; | ||||
| mod test_hasher; | ||||
| 
 | ||||
| // allow easily switching the hasher crate-wide for testing
 | ||||
| #[cfg(feature = "unstable-test-hasher")] | ||||
| pub type DefaultBuildHasher = test_hasher::DefaultBuildHasher; | ||||
| #[cfg(not(feature = "unstable-test-hasher"))] | ||||
| pub(crate) type DefaultBuildHasher = hashbrown::DefaultHashBuilder; | ||||
| 
 | ||||
| pub(crate) type HashMap<K, V> = hashbrown::HashMap<K, V, DefaultBuildHasher>; | ||||
| pub(crate) type HashSet<T> = hashbrown::HashSet<T, DefaultBuildHasher>; | ||||
| 
 | ||||
| #[doc(inline)] | ||||
| pub use const_bool::{ConstBool, ConstBoolDispatch, ConstBoolDispatchTag, GenericConstBool}; | ||||
|  | @ -29,4 +40,5 @@ pub use misc::{ | |||
| }; | ||||
| 
 | ||||
| pub mod job_server; | ||||
| pub mod prefix_sum; | ||||
| pub mod ready_valid; | ||||
|  |  | |||
							
								
								
									
										122
									
								
								crates/fayalite/src/util/alternating_cell.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										122
									
								
								crates/fayalite/src/util/alternating_cell.rs
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,122 @@ | |||
| // SPDX-License-Identifier: LGPL-3.0-or-later
 | ||||
| // See Notices.txt for copyright information
 | ||||
| 
 | ||||
| use crate::util::DebugAsDisplay; | ||||
| use std::{ | ||||
|     cell::{Cell, UnsafeCell}, | ||||
|     fmt, | ||||
| }; | ||||
| 
 | ||||
| pub(crate) trait AlternatingCellMethods { | ||||
|     fn unique_to_shared(&mut self); | ||||
|     fn shared_to_unique(&mut self); | ||||
| } | ||||
| 
 | ||||
| #[derive(Copy, Clone, Debug)] | ||||
| enum State { | ||||
|     Unique, | ||||
|     Shared, | ||||
|     Locked, | ||||
| } | ||||
| 
 | ||||
| pub(crate) struct AlternatingCell<T: ?Sized> { | ||||
|     state: Cell<State>, | ||||
|     value: UnsafeCell<T>, | ||||
| } | ||||
| 
 | ||||
| impl<T: ?Sized + fmt::Debug + AlternatingCellMethods> fmt::Debug for AlternatingCell<T> { | ||||
|     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||||
|         f.debug_tuple("AlternatingCell") | ||||
|             .field( | ||||
|                 self.try_share() | ||||
|                     .as_ref() | ||||
|                     .map(|v| -> &dyn fmt::Debug { v }) | ||||
|                     .unwrap_or(&DebugAsDisplay("<...>")), | ||||
|             ) | ||||
|             .finish() | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<T: ?Sized> AlternatingCell<T> { | ||||
|     pub(crate) const fn new_shared(value: T) -> Self | ||||
|     where | ||||
|         T: Sized, | ||||
|     { | ||||
|         Self { | ||||
|             state: Cell::new(State::Shared), | ||||
|             value: UnsafeCell::new(value), | ||||
|         } | ||||
|     } | ||||
|     pub(crate) const fn new_unique(value: T) -> Self | ||||
|     where | ||||
|         T: Sized, | ||||
|     { | ||||
|         Self { | ||||
|             state: Cell::new(State::Unique), | ||||
|             value: UnsafeCell::new(value), | ||||
|         } | ||||
|     } | ||||
|     pub(crate) fn is_unique(&self) -> bool { | ||||
|         matches!(self.state.get(), State::Unique) | ||||
|     } | ||||
|     pub(crate) fn is_shared(&self) -> bool { | ||||
|         matches!(self.state.get(), State::Shared) | ||||
|     } | ||||
|     pub(crate) fn into_inner(self) -> T | ||||
|     where | ||||
|         T: Sized, | ||||
|     { | ||||
|         self.value.into_inner() | ||||
|     } | ||||
|     pub(crate) fn try_share(&self) -> Option<&T> | ||||
|     where | ||||
|         T: AlternatingCellMethods, | ||||
|     { | ||||
|         match self.state.get() { | ||||
|             State::Shared => {} | ||||
|             State::Unique => { | ||||
|                 struct Locked<'a>(&'a Cell<State>); | ||||
|                 impl Drop for Locked<'_> { | ||||
|                     fn drop(&mut self) { | ||||
|                         self.0.set(State::Shared); | ||||
|                     } | ||||
|                 } | ||||
|                 self.state.set(State::Locked); | ||||
|                 let lock = Locked(&self.state); | ||||
|                 // Safety: state is Locked, so nothing else will
 | ||||
|                 // access value while calling unique_to_shared.
 | ||||
|                 unsafe { &mut *self.value.get() }.unique_to_shared(); | ||||
|                 drop(lock); | ||||
|             } | ||||
|             State::Locked => return None, | ||||
|         } | ||||
| 
 | ||||
|         // Safety: state is Shared so nothing will create any mutable
 | ||||
|         // references until the returned reference's lifetime expires.
 | ||||
|         Some(unsafe { &*self.value.get() }) | ||||
|     } | ||||
|     #[track_caller] | ||||
|     pub(crate) fn share(&self) -> &T | ||||
|     where | ||||
|         T: AlternatingCellMethods, | ||||
|     { | ||||
|         let Some(retval) = self.try_share() else { | ||||
|             panic!("`share` called recursively"); | ||||
|         }; | ||||
|         retval | ||||
|     } | ||||
|     pub(crate) fn unique(&mut self) -> &mut T | ||||
|     where | ||||
|         T: AlternatingCellMethods, | ||||
|     { | ||||
|         match self.state.get() { | ||||
|             State::Shared => { | ||||
|                 self.state.set(State::Unique); | ||||
|                 self.value.get_mut().shared_to_unique(); | ||||
|             } | ||||
|             State::Unique => {} | ||||
|             State::Locked => unreachable!(), | ||||
|         } | ||||
|         self.value.get_mut() | ||||
|     } | ||||
| } | ||||
|  | @ -1,5 +1,9 @@ | |||
| // SPDX-License-Identifier: LGPL-3.0-or-later
 | ||||
| // See Notices.txt for copyright information
 | ||||
| use serde::{ | ||||
|     de::{DeserializeOwned, Error, Unexpected}, | ||||
|     Deserialize, Deserializer, Serialize, Serializer, | ||||
| }; | ||||
| use std::{fmt::Debug, hash::Hash, mem::ManuallyDrop, ptr}; | ||||
| 
 | ||||
| mod sealed { | ||||
|  | @ -9,7 +13,17 @@ mod sealed { | |||
| /// # Safety
 | ||||
| /// the only implementation is `ConstBool<Self::VALUE>`
 | ||||
| pub unsafe trait GenericConstBool: | ||||
|     sealed::Sealed + Copy + Ord + Hash + Default + Debug + 'static + Send + Sync | ||||
|     sealed::Sealed | ||||
|     + Copy | ||||
|     + Ord | ||||
|     + Hash | ||||
|     + Default | ||||
|     + Debug | ||||
|     + 'static | ||||
|     + Send | ||||
|     + Sync | ||||
|     + Serialize | ||||
|     + DeserializeOwned | ||||
| { | ||||
|     const VALUE: bool; | ||||
| } | ||||
|  | @ -30,6 +44,32 @@ unsafe impl<const VALUE: bool> GenericConstBool for ConstBool<VALUE> { | |||
|     const VALUE: bool = VALUE; | ||||
| } | ||||
| 
 | ||||
| impl<const VALUE: bool> Serialize for ConstBool<VALUE> { | ||||
|     fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> | ||||
|     where | ||||
|         S: Serializer, | ||||
|     { | ||||
|         VALUE.serialize(serializer) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<'de, const VALUE: bool> Deserialize<'de> for ConstBool<VALUE> { | ||||
|     fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> | ||||
|     where | ||||
|         D: Deserializer<'de>, | ||||
|     { | ||||
|         let value = bool::deserialize(deserializer)?; | ||||
|         if value == VALUE { | ||||
|             Ok(ConstBool) | ||||
|         } else { | ||||
|             Err(D::Error::invalid_value( | ||||
|                 Unexpected::Bool(value), | ||||
|                 &if VALUE { "true" } else { "false" }, | ||||
|             )) | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| pub trait ConstBoolDispatchTag { | ||||
|     type Type<Select: GenericConstBool>; | ||||
| } | ||||
|  |  | |||
|  | @ -1,5 +1,9 @@ | |||
| // SPDX-License-Identifier: LGPL-3.0-or-later
 | ||||
| // See Notices.txt for copyright information
 | ||||
| use serde::{ | ||||
|     de::{DeserializeOwned, Error, Unexpected}, | ||||
|     Deserialize, Deserializer, Serialize, Serializer, | ||||
| }; | ||||
| use std::{fmt::Debug, hash::Hash}; | ||||
| 
 | ||||
| mod sealed { | ||||
|  | @ -8,7 +12,17 @@ mod sealed { | |||
| 
 | ||||
| /// the only implementation is `ConstUsize<Self::VALUE>`
 | ||||
| pub trait GenericConstUsize: | ||||
|     sealed::Sealed + Copy + Ord + Hash + Default + Debug + 'static + Send + Sync | ||||
|     sealed::Sealed | ||||
|     + Copy | ||||
|     + Ord | ||||
|     + Hash | ||||
|     + Default | ||||
|     + Debug | ||||
|     + 'static | ||||
|     + Send | ||||
|     + Sync | ||||
|     + Serialize | ||||
|     + DeserializeOwned | ||||
| { | ||||
|     const VALUE: usize; | ||||
| } | ||||
|  | @ -27,3 +41,29 @@ impl<const VALUE: usize> sealed::Sealed for ConstUsize<VALUE> {} | |||
| impl<const VALUE: usize> GenericConstUsize for ConstUsize<VALUE> { | ||||
|     const VALUE: usize = VALUE; | ||||
| } | ||||
| 
 | ||||
| impl<const VALUE: usize> Serialize for ConstUsize<VALUE> { | ||||
|     fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> | ||||
|     where | ||||
|         S: Serializer, | ||||
|     { | ||||
|         VALUE.serialize(serializer) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<'de, const VALUE: usize> Deserialize<'de> for ConstUsize<VALUE> { | ||||
|     fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> | ||||
|     where | ||||
|         D: Deserializer<'de>, | ||||
|     { | ||||
|         let value = usize::deserialize(deserializer)?; | ||||
|         if value == VALUE { | ||||
|             Ok(ConstUsize) | ||||
|         } else { | ||||
|             Err(D::Error::invalid_value( | ||||
|                 Unexpected::Unsigned(value as u64), | ||||
|                 &&*VALUE.to_string(), | ||||
|             )) | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  |  | |||
							
								
								
									
										839
									
								
								crates/fayalite/src/util/prefix_sum.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										839
									
								
								crates/fayalite/src/util/prefix_sum.rs
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,839 @@ | |||
| // SPDX-License-Identifier: LGPL-3.0-or-later
 | ||||
| // See Notices.txt for copyright information
 | ||||
| 
 | ||||
| // code derived from:
 | ||||
| // https://web.archive.org/web/20250303054010/https://git.libre-soc.org/?p=nmutil.git;a=blob;f=src/nmutil/prefix_sum.py;hb=effeb28e5848392adddcdad1f6e7a098f2a44c9c
 | ||||
| 
 | ||||
| use crate::intern::{Intern, Interned, Memoize}; | ||||
| use std::{borrow::Cow, num::NonZeroUsize}; | ||||
| 
 | ||||
| #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] | ||||
| pub struct PrefixSumOp { | ||||
|     pub lhs_index: usize, | ||||
|     pub rhs_and_dest_index: NonZeroUsize, | ||||
|     pub row: u32, | ||||
| } | ||||
| 
 | ||||
| #[derive(Clone, PartialEq, Eq, Hash, Debug)] | ||||
| #[non_exhaustive] | ||||
| pub struct DiagramConfig { | ||||
|     pub space: Cow<'static, str>, | ||||
|     pub vertical_bar: Cow<'static, str>, | ||||
|     pub plus: Cow<'static, str>, | ||||
|     pub slant: Cow<'static, str>, | ||||
|     pub connect: Cow<'static, str>, | ||||
|     pub no_connect: Cow<'static, str>, | ||||
|     pub padding: usize, | ||||
| } | ||||
| 
 | ||||
| impl DiagramConfig { | ||||
|     pub const fn new() -> Self { | ||||
|         Self { | ||||
|             space: Cow::Borrowed(" "), | ||||
|             vertical_bar: Cow::Borrowed("|"), | ||||
|             plus: Cow::Borrowed("\u{2295}"), // ⊕
 | ||||
|             slant: Cow::Borrowed(r"\"), | ||||
|             connect: Cow::Borrowed("\u{25CF}"), // ●
 | ||||
|             no_connect: Cow::Borrowed("X"), | ||||
|             padding: 1, | ||||
|         } | ||||
|     } | ||||
|     pub fn draw(self, ops: impl IntoIterator<Item = PrefixSumOp>, item_count: usize) -> String { | ||||
|         #[derive(Copy, Clone, Debug)] | ||||
|         struct DiagramCell { | ||||
|             slant: bool, | ||||
|             plus: bool, | ||||
|             tee: bool, | ||||
|         } | ||||
|         let mut ops_by_row: Vec<Vec<PrefixSumOp>> = Vec::new(); | ||||
|         let mut last_row = 0; | ||||
|         ops.into_iter().for_each(|op| { | ||||
|             assert!( | ||||
|                 op.lhs_index < op.rhs_and_dest_index.get(), | ||||
|                 "invalid PrefixSumOp! lhs_index must be less \ | ||||
|                 than rhs_and_dest_index: {op:?}",
 | ||||
|             ); | ||||
|             assert!( | ||||
|                 op.row >= last_row, | ||||
|                 "invalid PrefixSumOp! row must \ | ||||
|                 not decrease (row last was: {last_row}): {op:?}",
 | ||||
|             ); | ||||
|             let ops = if op.row > last_row || ops_by_row.is_empty() { | ||||
|                 ops_by_row.push(vec![]); | ||||
|                 ops_by_row.last_mut().expect("just pushed") | ||||
|             } else { | ||||
|                 ops_by_row | ||||
|                     .last_mut() | ||||
|                     .expect("just checked if ops_by_row is empty") | ||||
|             }; | ||||
|             if let Some(last) = ops.last() { | ||||
|                 assert!( | ||||
|                     op.rhs_and_dest_index < last.rhs_and_dest_index, | ||||
|                     "invalid PrefixSumOp! rhs_and_dest_index must strictly \ | ||||
|                     decrease in a row:\nthis op: {op:?}\nlast op: {last:?}",
 | ||||
|                 ); | ||||
|             } | ||||
|             ops.push(op); | ||||
|             last_row = op.row; | ||||
|         }); | ||||
|         let blank_row = || { | ||||
|             vec![ | ||||
|                 DiagramCell { | ||||
|                     slant: false, | ||||
|                     plus: false, | ||||
|                     tee: false | ||||
|                 }; | ||||
|                 item_count | ||||
|             ] | ||||
|         }; | ||||
|         let mut cells = vec![blank_row()]; | ||||
|         for ops in ops_by_row { | ||||
|             let max_distance = ops | ||||
|                 .iter() | ||||
|                 .map( | ||||
|                     |&PrefixSumOp { | ||||
|                          lhs_index, | ||||
|                          rhs_and_dest_index, | ||||
|                          .. | ||||
|                      }| { rhs_and_dest_index.get() - lhs_index }, | ||||
|                 ) | ||||
|                 .max() | ||||
|                 .expect("ops is known to be non-empty"); | ||||
|             cells.extend((0..max_distance).map(|_| blank_row())); | ||||
|             for op in ops { | ||||
|                 let mut y = cells.len() - 1; | ||||
|                 assert!( | ||||
|                     op.rhs_and_dest_index.get() < item_count, | ||||
|                     "invalid PrefixSumOp! rhs_and_dest_index must be \ | ||||
|                     less than item_count ({item_count}): {op:?}",
 | ||||
|                 ); | ||||
|                 let mut x = op.rhs_and_dest_index.get(); | ||||
|                 cells[y][x].plus = true; | ||||
|                 x -= 1; | ||||
|                 y -= 1; | ||||
|                 while op.lhs_index < x { | ||||
|                     cells[y][x].slant = true; | ||||
|                     x -= 1; | ||||
|                     y -= 1; | ||||
|                 } | ||||
|                 cells[y][x].tee = true; | ||||
|             } | ||||
|         } | ||||
|         let mut retval = String::new(); | ||||
|         let mut row_text = vec![String::new(); 2 * self.padding + 1]; | ||||
|         for cells_row in cells { | ||||
|             for cell in cells_row { | ||||
|                 // top padding
 | ||||
|                 for y in 0..self.padding { | ||||
|                     // top left padding
 | ||||
|                     for x in 0..self.padding { | ||||
|                         row_text[y] += if x == y && (cell.plus || cell.slant) { | ||||
|                             &self.slant | ||||
|                         } else { | ||||
|                             &self.space | ||||
|                         }; | ||||
|                     } | ||||
|                     // top vertical bar
 | ||||
|                     row_text[y] += &self.vertical_bar; | ||||
|                     // top right padding
 | ||||
|                     for _ in 0..self.padding { | ||||
|                         row_text[y] += &self.space; | ||||
|                     } | ||||
|                 } | ||||
|                 // center left padding
 | ||||
|                 for _ in 0..self.padding { | ||||
|                     row_text[self.padding] += &self.space; | ||||
|                 } | ||||
|                 // center
 | ||||
|                 row_text[self.padding] += if cell.plus { | ||||
|                     &self.plus | ||||
|                 } else if cell.tee { | ||||
|                     &self.connect | ||||
|                 } else if cell.slant { | ||||
|                     &self.no_connect | ||||
|                 } else { | ||||
|                     &self.vertical_bar | ||||
|                 }; | ||||
|                 // center right padding
 | ||||
|                 for _ in 0..self.padding { | ||||
|                     row_text[self.padding] += &self.space; | ||||
|                 } | ||||
|                 let bottom_padding_start = self.padding + 1; | ||||
|                 let bottom_padding_last = self.padding * 2; | ||||
|                 // bottom padding
 | ||||
|                 for y in bottom_padding_start..=bottom_padding_last { | ||||
|                     // bottom left padding
 | ||||
|                     for _ in 0..self.padding { | ||||
|                         row_text[y] += &self.space; | ||||
|                     } | ||||
|                     // bottom vertical bar
 | ||||
|                     row_text[y] += &self.vertical_bar; | ||||
|                     // bottom right padding
 | ||||
|                     for x in bottom_padding_start..=bottom_padding_last { | ||||
|                         row_text[y] += if x == y && (cell.tee || cell.slant) { | ||||
|                             &self.slant | ||||
|                         } else { | ||||
|                             &self.space | ||||
|                         }; | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|             for line in &mut row_text { | ||||
|                 retval += line.trim_end(); | ||||
|                 retval += "\n"; | ||||
|                 line.clear(); | ||||
|             } | ||||
|         } | ||||
|         retval | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl Default for DiagramConfig { | ||||
|     fn default() -> Self { | ||||
|         Self::new() | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl PrefixSumOp { | ||||
|     pub fn diagram(ops: impl IntoIterator<Item = Self>, item_count: usize) -> String { | ||||
|         Self::diagram_with_config(ops, item_count, DiagramConfig::new()) | ||||
|     } | ||||
|     pub fn diagram_with_config( | ||||
|         ops: impl IntoIterator<Item = Self>, | ||||
|         item_count: usize, | ||||
|         config: DiagramConfig, | ||||
|     ) -> String { | ||||
|         config.draw(ops, item_count) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] | ||||
| pub enum PrefixSumAlgorithm { | ||||
|     /// Uses the algorithm from:
 | ||||
|     /// <https://en.wikipedia.org/wiki/Prefix_sum#Algorithm_1:_Shorter_span,_more_parallel>
 | ||||
|     LowLatency, | ||||
|     /// Uses the algorithm from:
 | ||||
|     /// <https://en.wikipedia.org/wiki/Prefix_sum#Algorithm_2:_Work-efficient>
 | ||||
|     WorkEfficient, | ||||
| } | ||||
| 
 | ||||
| impl PrefixSumAlgorithm { | ||||
|     fn ops_impl(self, item_count: usize) -> Vec<PrefixSumOp> { | ||||
|         let mut retval = Vec::new(); | ||||
|         let mut distance = 1; | ||||
|         let mut row = 0; | ||||
|         while distance < item_count { | ||||
|             let double_distance = distance | ||||
|                 .checked_mul(2) | ||||
|                 .expect("prefix-sum item_count is too big"); | ||||
|             let (start, step) = match self { | ||||
|                 Self::LowLatency => (distance, 1), | ||||
|                 Self::WorkEfficient => (double_distance - 1, double_distance), | ||||
|             }; | ||||
|             for rhs_and_dest_index in (start..item_count).step_by(step).rev() { | ||||
|                 let Some(rhs_and_dest_index) = NonZeroUsize::new(rhs_and_dest_index) else { | ||||
|                     unreachable!(); | ||||
|                 }; | ||||
|                 let lhs_index = rhs_and_dest_index.get() - distance; | ||||
|                 retval.push(PrefixSumOp { | ||||
|                     lhs_index, | ||||
|                     rhs_and_dest_index, | ||||
|                     row, | ||||
|                 }); | ||||
|             } | ||||
|             distance = double_distance; | ||||
|             row += 1; | ||||
|         } | ||||
|         match self { | ||||
|             Self::LowLatency => {} | ||||
|             Self::WorkEfficient => { | ||||
|                 distance /= 2; | ||||
|                 while distance >= 1 { | ||||
|                     let start = distance | ||||
|                         .checked_mul(3) | ||||
|                         .expect("prefix-sum item_count is too big") | ||||
|                         - 1; | ||||
|                     for rhs_and_dest_index in (start..item_count).step_by(distance * 2).rev() { | ||||
|                         let Some(rhs_and_dest_index) = NonZeroUsize::new(rhs_and_dest_index) else { | ||||
|                             unreachable!(); | ||||
|                         }; | ||||
|                         let lhs_index = rhs_and_dest_index.get() - distance; | ||||
|                         retval.push(PrefixSumOp { | ||||
|                             lhs_index, | ||||
|                             rhs_and_dest_index, | ||||
|                             row, | ||||
|                         }); | ||||
|                     } | ||||
|                     row += 1; | ||||
|                     distance /= 2; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         retval | ||||
|     } | ||||
|     pub fn ops(self, item_count: usize) -> Interned<[PrefixSumOp]> { | ||||
|         #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] | ||||
|         struct MyMemoize(PrefixSumAlgorithm); | ||||
|         impl Memoize for MyMemoize { | ||||
|             type Input = usize; | ||||
|             type InputOwned = usize; | ||||
|             type Output = Interned<[PrefixSumOp]>; | ||||
| 
 | ||||
|             fn inner(self, item_count: &Self::Input) -> Self::Output { | ||||
|                 Intern::intern_owned(self.0.ops_impl(*item_count)) | ||||
|             } | ||||
|         } | ||||
|         MyMemoize(self).get_owned(item_count) | ||||
|     } | ||||
|     pub fn run<T>(self, items: impl IntoIterator<Item = T>, f: impl FnMut(&T, &T) -> T) -> Vec<T> { | ||||
|         let mut items = Vec::from_iter(items); | ||||
|         self.run_on_slice(&mut items, f); | ||||
|         items | ||||
|     } | ||||
|     pub fn run_on_slice<T>(self, items: &mut [T], mut f: impl FnMut(&T, &T) -> T) -> &mut [T] { | ||||
|         self.ops(items.len()).into_iter().for_each( | ||||
|             |PrefixSumOp { | ||||
|                  lhs_index, | ||||
|                  rhs_and_dest_index, | ||||
|                  row: _, | ||||
|              }| { | ||||
|                 items[rhs_and_dest_index.get()] = | ||||
|                     f(&items[lhs_index], &items[rhs_and_dest_index.get()]); | ||||
|             }, | ||||
|         ); | ||||
|         items | ||||
|     } | ||||
|     pub fn filtered_ops( | ||||
|         self, | ||||
|         item_live_out_flags: impl IntoIterator<Item = bool>, | ||||
|     ) -> Vec<PrefixSumOp> { | ||||
|         let mut item_live_out_flags = Vec::from_iter(item_live_out_flags); | ||||
|         let prefix_sum_ops = self.ops(item_live_out_flags.len()); | ||||
|         let mut ops_live_flags = vec![false; prefix_sum_ops.len()]; | ||||
|         for ( | ||||
|             op_index, | ||||
|             &PrefixSumOp { | ||||
|                 lhs_index, | ||||
|                 rhs_and_dest_index, | ||||
|                 row: _, | ||||
|             }, | ||||
|         ) in prefix_sum_ops.iter().enumerate().rev() | ||||
|         { | ||||
|             let live = item_live_out_flags[rhs_and_dest_index.get()]; | ||||
|             item_live_out_flags[lhs_index] |= live; | ||||
|             ops_live_flags[op_index] = live; | ||||
|         } | ||||
|         prefix_sum_ops | ||||
|             .into_iter() | ||||
|             .zip(ops_live_flags) | ||||
|             .filter_map(|(op, live)| live.then_some(op)) | ||||
|             .collect() | ||||
|     } | ||||
|     pub fn reduce_ops(self, item_count: usize) -> Interned<[PrefixSumOp]> { | ||||
|         #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] | ||||
|         struct MyMemoize(PrefixSumAlgorithm); | ||||
|         impl Memoize for MyMemoize { | ||||
|             type Input = usize; | ||||
|             type InputOwned = usize; | ||||
|             type Output = Interned<[PrefixSumOp]>; | ||||
| 
 | ||||
|             fn inner(self, item_count: &Self::Input) -> Self::Output { | ||||
|                 let mut item_live_out_flags = vec![false; *item_count]; | ||||
|                 let Some(last_item_live_out_flag) = item_live_out_flags.last_mut() else { | ||||
|                     return Interned::default(); | ||||
|                 }; | ||||
|                 *last_item_live_out_flag = true; | ||||
|                 Intern::intern_owned(self.0.filtered_ops(item_live_out_flags)) | ||||
|             } | ||||
|         } | ||||
|         MyMemoize(self).get_owned(item_count) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| pub fn reduce_ops(item_count: usize) -> Interned<[PrefixSumOp]> { | ||||
|     PrefixSumAlgorithm::LowLatency.reduce_ops(item_count) | ||||
| } | ||||
| 
 | ||||
| pub fn reduce<T>(items: impl IntoIterator<Item = T>, mut f: impl FnMut(T, T) -> T) -> Option<T> { | ||||
|     let mut items: Vec<_> = items.into_iter().map(Some).collect(); | ||||
|     for op in reduce_ops(items.len()) { | ||||
|         let (Some(lhs), Some(rhs)) = ( | ||||
|             items[op.lhs_index].take(), | ||||
|             items[op.rhs_and_dest_index.get()].take(), | ||||
|         ) else { | ||||
|             unreachable!(); | ||||
|         }; | ||||
|         items[op.rhs_and_dest_index.get()] = Some(f(lhs, rhs)); | ||||
|     } | ||||
|     items.last_mut().and_then(Option::take) | ||||
| } | ||||
| 
 | ||||
| #[cfg(test)] | ||||
| mod tests { | ||||
|     use super::*; | ||||
| 
 | ||||
|     fn input_strings() -> [String; 9] { | ||||
|         std::array::from_fn(|i| String::from_utf8(vec![b'a' + i as u8]).unwrap()) | ||||
|     } | ||||
| 
 | ||||
|     #[test] | ||||
|     fn test_prefix_sum_strings() { | ||||
|         let input = input_strings(); | ||||
|         let expected: Vec<String> = input | ||||
|             .iter() | ||||
|             .scan(String::new(), |l, r| { | ||||
|                 *l += r; | ||||
|                 Some(l.clone()) | ||||
|             }) | ||||
|             .collect(); | ||||
|         println!("expected: {expected:?}"); | ||||
|         assert_eq!( | ||||
|             *PrefixSumAlgorithm::WorkEfficient | ||||
|                 .run_on_slice(&mut input.clone(), |l, r| l.to_string() + r), | ||||
|             *expected | ||||
|         ); | ||||
|         assert_eq!( | ||||
|             *PrefixSumAlgorithm::LowLatency | ||||
|                 .run_on_slice(&mut input.clone(), |l, r| l.to_string() + r), | ||||
|             *expected | ||||
|         ); | ||||
|     } | ||||
| 
 | ||||
|     #[test] | ||||
|     fn test_reduce_string() { | ||||
|         let input = input_strings(); | ||||
|         let expected = input.clone().into_iter().reduce(|l, r| l + &r); | ||||
|         assert_eq!(reduce(input, |l, r| l + &r), expected); | ||||
|     } | ||||
| 
 | ||||
|     fn op(lhs_index: usize, rhs_and_dest_index: usize, row: u32) -> PrefixSumOp { | ||||
|         PrefixSumOp { | ||||
|             lhs_index, | ||||
|             rhs_and_dest_index: NonZeroUsize::new(rhs_and_dest_index).expect("should be non-zero"), | ||||
|             row, | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     #[test] | ||||
|     fn test_reduce_ops_9() { | ||||
|         let expected = vec![ | ||||
|             op(7, 8, 0), | ||||
|             op(5, 6, 0), | ||||
|             op(3, 4, 0), | ||||
|             op(1, 2, 0), | ||||
|             op(6, 8, 1), | ||||
|             op(2, 4, 1), | ||||
|             op(4, 8, 2), | ||||
|             op(0, 8, 3), | ||||
|         ]; | ||||
|         println!("expected: {expected:#?}"); | ||||
|         let ops = reduce_ops(9); | ||||
|         println!("ops: {ops:#?}"); | ||||
|         assert_eq!(*ops, *expected); | ||||
|     } | ||||
| 
 | ||||
|     #[test] | ||||
|     fn test_reduce_ops_8() { | ||||
|         let expected = vec![ | ||||
|             op(6, 7, 0), | ||||
|             op(4, 5, 0), | ||||
|             op(2, 3, 0), | ||||
|             op(0, 1, 0), | ||||
|             op(5, 7, 1), | ||||
|             op(1, 3, 1), | ||||
|             op(3, 7, 2), | ||||
|         ]; | ||||
|         println!("expected: {expected:#?}"); | ||||
|         let ops = reduce_ops(8); | ||||
|         println!("ops: {ops:#?}"); | ||||
|         assert_eq!(*ops, *expected); | ||||
|     } | ||||
| 
 | ||||
|     #[test] | ||||
|     fn test_count_ones() { | ||||
|         for width in 0..=10u32 { | ||||
|             for v in 0..1u32 << width { | ||||
|                 let expected = v.count_ones(); | ||||
|                 assert_eq!( | ||||
|                     reduce((0..width).map(|i| (v >> i) & 1), |l, r| l + r).unwrap_or(0), | ||||
|                     expected, | ||||
|                     "v={v:#X}" | ||||
|                 ); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     #[track_caller] | ||||
|     fn test_diagram(ops: impl IntoIterator<Item = PrefixSumOp>, item_count: usize, expected: &str) { | ||||
|         let text = PrefixSumOp::diagram_with_config( | ||||
|             ops, | ||||
|             item_count, | ||||
|             DiagramConfig { | ||||
|                 plus: Cow::Borrowed("@"), | ||||
|                 ..Default::default() | ||||
|             }, | ||||
|         ); | ||||
|         println!("text:\n{text}\n"); | ||||
|         assert_eq!(text, expected); | ||||
|     } | ||||
| 
 | ||||
|     #[test] | ||||
|     fn test_work_efficient_diagram_16() { | ||||
|         let item_count = 16; | ||||
|         test_diagram( | ||||
|             PrefixSumAlgorithm::WorkEfficient.ops(item_count), | ||||
|             item_count, | ||||
|             &r" | ||||
|  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  | | ||||
|  ●  |  ●  |  ●  |  ●  |  ●  |  ●  |  ●  |  ●  | | ||||
|  |\ |  |\ |  |\ |  |\ |  |\ |  |\ |  |\ |  |\ | | ||||
|  | \|  | \|  | \|  | \|  | \|  | \|  | \|  | \| | ||||
|  |  @  |  @  |  @  |  @  |  @  |  @  |  @  |  @ | ||||
|  |  |\ |  |  |  |\ |  |  |  |\ |  |  |  |\ |  | | ||||
|  |  | \|  |  |  | \|  |  |  | \|  |  |  | \|  | | ||||
|  |  |  X  |  |  |  X  |  |  |  X  |  |  |  X  | | ||||
|  |  |  |\ |  |  |  |\ |  |  |  |\ |  |  |  |\ | | ||||
|  |  |  | \|  |  |  | \|  |  |  | \|  |  |  | \| | ||||
|  |  |  |  @  |  |  |  @  |  |  |  @  |  |  |  @ | ||||
|  |  |  |  |\ |  |  |  |  |  |  |  |\ |  |  |  | | ||||
|  |  |  |  | \|  |  |  |  |  |  |  | \|  |  |  | | ||||
|  |  |  |  |  X  |  |  |  |  |  |  |  X  |  |  | | ||||
|  |  |  |  |  |\ |  |  |  |  |  |  |  |\ |  |  | | ||||
|  |  |  |  |  | \|  |  |  |  |  |  |  | \|  |  | | ||||
|  |  |  |  |  |  X  |  |  |  |  |  |  |  X  |  | | ||||
|  |  |  |  |  |  |\ |  |  |  |  |  |  |  |\ |  | | ||||
|  |  |  |  |  |  | \|  |  |  |  |  |  |  | \|  | | ||||
|  |  |  |  |  |  |  X  |  |  |  |  |  |  |  X  | | ||||
|  |  |  |  |  |  |  |\ |  |  |  |  |  |  |  |\ | | ||||
|  |  |  |  |  |  |  | \|  |  |  |  |  |  |  | \| | ||||
|  |  |  |  |  |  |  |  @  |  |  |  |  |  |  |  @ | ||||
|  |  |  |  |  |  |  |  |\ |  |  |  |  |  |  |  | | ||||
|  |  |  |  |  |  |  |  | \|  |  |  |  |  |  |  | | ||||
|  |  |  |  |  |  |  |  |  X  |  |  |  |  |  |  | | ||||
|  |  |  |  |  |  |  |  |  |\ |  |  |  |  |  |  | | ||||
|  |  |  |  |  |  |  |  |  | \|  |  |  |  |  |  | | ||||
|  |  |  |  |  |  |  |  |  |  X  |  |  |  |  |  | | ||||
|  |  |  |  |  |  |  |  |  |  |\ |  |  |  |  |  | | ||||
|  |  |  |  |  |  |  |  |  |  | \|  |  |  |  |  | | ||||
|  |  |  |  |  |  |  |  |  |  |  X  |  |  |  |  | | ||||
|  |  |  |  |  |  |  |  |  |  |  |\ |  |  |  |  | | ||||
|  |  |  |  |  |  |  |  |  |  |  | \|  |  |  |  | | ||||
|  |  |  |  |  |  |  |  |  |  |  |  X  |  |  |  | | ||||
|  |  |  |  |  |  |  |  |  |  |  |  |\ |  |  |  | | ||||
|  |  |  |  |  |  |  |  |  |  |  |  | \|  |  |  | | ||||
|  |  |  |  |  |  |  |  |  |  |  |  |  X  |  |  | | ||||
|  |  |  |  |  |  |  |  |  |  |  |  |  |\ |  |  | | ||||
|  |  |  |  |  |  |  |  |  |  |  |  |  | \|  |  | | ||||
|  |  |  |  |  |  |  |  |  |  |  |  |  |  X  |  | | ||||
|  |  |  |  |  |  |  |  |  |  |  |  |  |  |\ |  | | ||||
|  |  |  |  |  |  |  |  |  |  |  |  |  |  | \|  | | ||||
|  |  |  |  |  |  |  |  |  |  |  |  |  |  |  X  | | ||||
|  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |\ | | ||||
|  |  |  |  |  |  |  |  |  |  |  |  |  |  |  | \| | ||||
|  |  |  |  |  |  |  |  ●  |  |  |  |  |  |  |  @ | ||||
|  |  |  |  |  |  |  |  |\ |  |  |  |  |  |  |  | | ||||
|  |  |  |  |  |  |  |  | \|  |  |  |  |  |  |  | | ||||
|  |  |  |  |  |  |  |  |  X  |  |  |  |  |  |  | | ||||
|  |  |  |  |  |  |  |  |  |\ |  |  |  |  |  |  | | ||||
|  |  |  |  |  |  |  |  |  | \|  |  |  |  |  |  | | ||||
|  |  |  |  |  |  |  |  |  |  X  |  |  |  |  |  | | ||||
|  |  |  |  |  |  |  |  |  |  |\ |  |  |  |  |  | | ||||
|  |  |  |  |  |  |  |  |  |  | \|  |  |  |  |  | | ||||
|  |  |  |  |  |  |  |  |  |  |  X  |  |  |  |  | | ||||
|  |  |  |  |  |  |  |  |  |  |  |\ |  |  |  |  | | ||||
|  |  |  |  |  |  |  |  |  |  |  | \|  |  |  |  | | ||||
|  |  |  |  ●  |  |  |  ●  |  |  |  @  |  |  |  | | ||||
|  |  |  |  |\ |  |  |  |\ |  |  |  |\ |  |  |  | | ||||
|  |  |  |  | \|  |  |  | \|  |  |  | \|  |  |  | | ||||
|  |  |  |  |  X  |  |  |  X  |  |  |  X  |  |  | | ||||
|  |  |  |  |  |\ |  |  |  |\ |  |  |  |\ |  |  | | ||||
|  |  |  |  |  | \|  |  |  | \|  |  |  | \|  |  | | ||||
|  |  ●  |  ●  |  @  |  ●  |  @  |  ●  |  @  |  | | ||||
|  |  |\ |  |\ |  |\ |  |\ |  |\ |  |\ |  |\ |  | | ||||
|  |  | \|  | \|  | \|  | \|  | \|  | \|  | \|  | | ||||
|  |  |  @  |  @  |  @  |  @  |  @  |  @  |  @  | | ||||
|  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  | | ||||
| "[1..], // trim newline at start
 | ||||
|         ); | ||||
|     } | ||||
| 
 | ||||
|     #[test] | ||||
|     fn test_low_latency_diagram_16() { | ||||
|         let item_count = 16; | ||||
|         test_diagram( | ||||
|             PrefixSumAlgorithm::LowLatency.ops(item_count), | ||||
|             item_count, | ||||
|             &r" | ||||
|  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  | | ||||
|  ●  ●  ●  ●  ●  ●  ●  ●  ●  ●  ●  ●  ●  ●  ●  | | ||||
|  |\ |\ |\ |\ |\ |\ |\ |\ |\ |\ |\ |\ |\ |\ |\ | | ||||
|  | \| \| \| \| \| \| \| \| \| \| \| \| \| \| \| | ||||
|  ●  @  @  @  @  @  @  @  @  @  @  @  @  @  @  @ | ||||
|  |\ |\ |\ |\ |\ |\ |\ |\ |\ |\ |\ |\ |\ |\ |  | | ||||
|  | \| \| \| \| \| \| \| \| \| \| \| \| \| \|  | | ||||
|  |  X  X  X  X  X  X  X  X  X  X  X  X  X  X  | | ||||
|  |  |\ |\ |\ |\ |\ |\ |\ |\ |\ |\ |\ |\ |\ |\ | | ||||
|  |  | \| \| \| \| \| \| \| \| \| \| \| \| \| \| | ||||
|  ●  ●  @  @  @  @  @  @  @  @  @  @  @  @  @  @ | ||||
|  |\ |\ |\ |\ |\ |\ |\ |\ |\ |\ |\ |\ |  |  |  | | ||||
|  | \| \| \| \| \| \| \| \| \| \| \| \|  |  |  | | ||||
|  |  X  X  X  X  X  X  X  X  X  X  X  X  |  |  | | ||||
|  |  |\ |\ |\ |\ |\ |\ |\ |\ |\ |\ |\ |\ |  |  | | ||||
|  |  | \| \| \| \| \| \| \| \| \| \| \| \|  |  | | ||||
|  |  |  X  X  X  X  X  X  X  X  X  X  X  X  |  | | ||||
|  |  |  |\ |\ |\ |\ |\ |\ |\ |\ |\ |\ |\ |\ |  | | ||||
|  |  |  | \| \| \| \| \| \| \| \| \| \| \| \|  | | ||||
|  |  |  |  X  X  X  X  X  X  X  X  X  X  X  X  | | ||||
|  |  |  |  |\ |\ |\ |\ |\ |\ |\ |\ |\ |\ |\ |\ | | ||||
|  |  |  |  | \| \| \| \| \| \| \| \| \| \| \| \| | ||||
|  ●  ●  ●  ●  @  @  @  @  @  @  @  @  @  @  @  @ | ||||
|  |\ |\ |\ |\ |\ |\ |\ |\ |  |  |  |  |  |  |  | | ||||
|  | \| \| \| \| \| \| \| \|  |  |  |  |  |  |  | | ||||
|  |  X  X  X  X  X  X  X  X  |  |  |  |  |  |  | | ||||
|  |  |\ |\ |\ |\ |\ |\ |\ |\ |  |  |  |  |  |  | | ||||
|  |  | \| \| \| \| \| \| \| \|  |  |  |  |  |  | | ||||
|  |  |  X  X  X  X  X  X  X  X  |  |  |  |  |  | | ||||
|  |  |  |\ |\ |\ |\ |\ |\ |\ |\ |  |  |  |  |  | | ||||
|  |  |  | \| \| \| \| \| \| \| \|  |  |  |  |  | | ||||
|  |  |  |  X  X  X  X  X  X  X  X  |  |  |  |  | | ||||
|  |  |  |  |\ |\ |\ |\ |\ |\ |\ |\ |  |  |  |  | | ||||
|  |  |  |  | \| \| \| \| \| \| \| \|  |  |  |  | | ||||
|  |  |  |  |  X  X  X  X  X  X  X  X  |  |  |  | | ||||
|  |  |  |  |  |\ |\ |\ |\ |\ |\ |\ |\ |  |  |  | | ||||
|  |  |  |  |  | \| \| \| \| \| \| \| \|  |  |  | | ||||
|  |  |  |  |  |  X  X  X  X  X  X  X  X  |  |  | | ||||
|  |  |  |  |  |  |\ |\ |\ |\ |\ |\ |\ |\ |  |  | | ||||
|  |  |  |  |  |  | \| \| \| \| \| \| \| \|  |  | | ||||
|  |  |  |  |  |  |  X  X  X  X  X  X  X  X  |  | | ||||
|  |  |  |  |  |  |  |\ |\ |\ |\ |\ |\ |\ |\ |  | | ||||
|  |  |  |  |  |  |  | \| \| \| \| \| \| \| \|  | | ||||
|  |  |  |  |  |  |  |  X  X  X  X  X  X  X  X  | | ||||
|  |  |  |  |  |  |  |  |\ |\ |\ |\ |\ |\ |\ |\ | | ||||
|  |  |  |  |  |  |  |  | \| \| \| \| \| \| \| \| | ||||
|  |  |  |  |  |  |  |  |  @  @  @  @  @  @  @  @ | ||||
|  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  | | ||||
| "[1..], // trim newline at start
 | ||||
|         ); | ||||
|     } | ||||
| 
 | ||||
|     #[test] | ||||
|     fn test_work_efficient_diagram_9() { | ||||
|         let item_count = 9; | ||||
|         test_diagram( | ||||
|             PrefixSumAlgorithm::WorkEfficient.ops(item_count), | ||||
|             item_count, | ||||
|             &r" | ||||
|  |  |  |  |  |  |  |  |  | | ||||
|  ●  |  ●  |  ●  |  ●  |  | | ||||
|  |\ |  |\ |  |\ |  |\ |  | | ||||
|  | \|  | \|  | \|  | \|  | | ||||
|  |  @  |  @  |  @  |  @  | | ||||
|  |  |\ |  |  |  |\ |  |  | | ||||
|  |  | \|  |  |  | \|  |  | | ||||
|  |  |  X  |  |  |  X  |  | | ||||
|  |  |  |\ |  |  |  |\ |  | | ||||
|  |  |  | \|  |  |  | \|  | | ||||
|  |  |  |  @  |  |  |  @  | | ||||
|  |  |  |  |\ |  |  |  |  | | ||||
|  |  |  |  | \|  |  |  |  | | ||||
|  |  |  |  |  X  |  |  |  | | ||||
|  |  |  |  |  |\ |  |  |  | | ||||
|  |  |  |  |  | \|  |  |  | | ||||
|  |  |  |  |  |  X  |  |  | | ||||
|  |  |  |  |  |  |\ |  |  | | ||||
|  |  |  |  |  |  | \|  |  | | ||||
|  |  |  |  |  |  |  X  |  | | ||||
|  |  |  |  |  |  |  |\ |  | | ||||
|  |  |  |  |  |  |  | \|  | | ||||
|  |  |  |  ●  |  |  |  @  | | ||||
|  |  |  |  |\ |  |  |  |  | | ||||
|  |  |  |  | \|  |  |  |  | | ||||
|  |  |  |  |  X  |  |  |  | | ||||
|  |  |  |  |  |\ |  |  |  | | ||||
|  |  |  |  |  | \|  |  |  | | ||||
|  |  ●  |  ●  |  @  |  ●  | | ||||
|  |  |\ |  |\ |  |\ |  |\ | | ||||
|  |  | \|  | \|  | \|  | \| | ||||
|  |  |  @  |  @  |  @  |  @ | ||||
|  |  |  |  |  |  |  |  |  | | ||||
| "[1..], // trim newline at start
 | ||||
|         ); | ||||
|     } | ||||
| 
 | ||||
|     #[test] | ||||
|     fn test_low_latency_diagram_9() { | ||||
|         let item_count = 9; | ||||
|         test_diagram( | ||||
|             PrefixSumAlgorithm::LowLatency.ops(item_count), | ||||
|             item_count, | ||||
|             &r" | ||||
|  |  |  |  |  |  |  |  |  | | ||||
|  ●  ●  ●  ●  ●  ●  ●  ●  | | ||||
|  |\ |\ |\ |\ |\ |\ |\ |\ | | ||||
|  | \| \| \| \| \| \| \| \| | ||||
|  ●  @  @  @  @  @  @  @  @ | ||||
|  |\ |\ |\ |\ |\ |\ |\ |  | | ||||
|  | \| \| \| \| \| \| \|  | | ||||
|  |  X  X  X  X  X  X  X  | | ||||
|  |  |\ |\ |\ |\ |\ |\ |\ | | ||||
|  |  | \| \| \| \| \| \| \| | ||||
|  ●  ●  @  @  @  @  @  @  @ | ||||
|  |\ |\ |\ |\ |\ |  |  |  | | ||||
|  | \| \| \| \| \|  |  |  | | ||||
|  |  X  X  X  X  X  |  |  | | ||||
|  |  |\ |\ |\ |\ |\ |  |  | | ||||
|  |  | \| \| \| \| \|  |  | | ||||
|  |  |  X  X  X  X  X  |  | | ||||
|  |  |  |\ |\ |\ |\ |\ |  | | ||||
|  |  |  | \| \| \| \| \|  | | ||||
|  |  |  |  X  X  X  X  X  | | ||||
|  |  |  |  |\ |\ |\ |\ |\ | | ||||
|  |  |  |  | \| \| \| \| \| | ||||
|  ●  |  |  |  @  @  @  @  @ | ||||
|  |\ |  |  |  |  |  |  |  | | ||||
|  | \|  |  |  |  |  |  |  | | ||||
|  |  X  |  |  |  |  |  |  | | ||||
|  |  |\ |  |  |  |  |  |  | | ||||
|  |  | \|  |  |  |  |  |  | | ||||
|  |  |  X  |  |  |  |  |  | | ||||
|  |  |  |\ |  |  |  |  |  | | ||||
|  |  |  | \|  |  |  |  |  | | ||||
|  |  |  |  X  |  |  |  |  | | ||||
|  |  |  |  |\ |  |  |  |  | | ||||
|  |  |  |  | \|  |  |  |  | | ||||
|  |  |  |  |  X  |  |  |  | | ||||
|  |  |  |  |  |\ |  |  |  | | ||||
|  |  |  |  |  | \|  |  |  | | ||||
|  |  |  |  |  |  X  |  |  | | ||||
|  |  |  |  |  |  |\ |  |  | | ||||
|  |  |  |  |  |  | \|  |  | | ||||
|  |  |  |  |  |  |  X  |  | | ||||
|  |  |  |  |  |  |  |\ |  | | ||||
|  |  |  |  |  |  |  | \|  | | ||||
|  |  |  |  |  |  |  |  X  | | ||||
|  |  |  |  |  |  |  |  |\ | | ||||
|  |  |  |  |  |  |  |  | \| | ||||
|  |  |  |  |  |  |  |  |  @ | ||||
|  |  |  |  |  |  |  |  |  | | ||||
| "[1..], // trim newline at start
 | ||||
|         ); | ||||
|     } | ||||
| 
 | ||||
|     #[test] | ||||
|     fn test_reduce_diagram_16() { | ||||
|         let item_count = 16; | ||||
|         test_diagram( | ||||
|             reduce_ops(item_count), | ||||
|             item_count, | ||||
|             &r" | ||||
|  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  | | ||||
|  ●  |  ●  |  ●  |  ●  |  ●  |  ●  |  ●  |  ●  | | ||||
|  |\ |  |\ |  |\ |  |\ |  |\ |  |\ |  |\ |  |\ | | ||||
|  | \|  | \|  | \|  | \|  | \|  | \|  | \|  | \| | ||||
|  |  @  |  @  |  @  |  @  |  @  |  @  |  @  |  @ | ||||
|  |  |\ |  |  |  |\ |  |  |  |\ |  |  |  |\ |  | | ||||
|  |  | \|  |  |  | \|  |  |  | \|  |  |  | \|  | | ||||
|  |  |  X  |  |  |  X  |  |  |  X  |  |  |  X  | | ||||
|  |  |  |\ |  |  |  |\ |  |  |  |\ |  |  |  |\ | | ||||
|  |  |  | \|  |  |  | \|  |  |  | \|  |  |  | \| | ||||
|  |  |  |  @  |  |  |  @  |  |  |  @  |  |  |  @ | ||||
|  |  |  |  |\ |  |  |  |  |  |  |  |\ |  |  |  | | ||||
|  |  |  |  | \|  |  |  |  |  |  |  | \|  |  |  | | ||||
|  |  |  |  |  X  |  |  |  |  |  |  |  X  |  |  | | ||||
|  |  |  |  |  |\ |  |  |  |  |  |  |  |\ |  |  | | ||||
|  |  |  |  |  | \|  |  |  |  |  |  |  | \|  |  | | ||||
|  |  |  |  |  |  X  |  |  |  |  |  |  |  X  |  | | ||||
|  |  |  |  |  |  |\ |  |  |  |  |  |  |  |\ |  | | ||||
|  |  |  |  |  |  | \|  |  |  |  |  |  |  | \|  | | ||||
|  |  |  |  |  |  |  X  |  |  |  |  |  |  |  X  | | ||||
|  |  |  |  |  |  |  |\ |  |  |  |  |  |  |  |\ | | ||||
|  |  |  |  |  |  |  | \|  |  |  |  |  |  |  | \| | ||||
|  |  |  |  |  |  |  |  @  |  |  |  |  |  |  |  @ | ||||
|  |  |  |  |  |  |  |  |\ |  |  |  |  |  |  |  | | ||||
|  |  |  |  |  |  |  |  | \|  |  |  |  |  |  |  | | ||||
|  |  |  |  |  |  |  |  |  X  |  |  |  |  |  |  | | ||||
|  |  |  |  |  |  |  |  |  |\ |  |  |  |  |  |  | | ||||
|  |  |  |  |  |  |  |  |  | \|  |  |  |  |  |  | | ||||
|  |  |  |  |  |  |  |  |  |  X  |  |  |  |  |  | | ||||
|  |  |  |  |  |  |  |  |  |  |\ |  |  |  |  |  | | ||||
|  |  |  |  |  |  |  |  |  |  | \|  |  |  |  |  | | ||||
|  |  |  |  |  |  |  |  |  |  |  X  |  |  |  |  | | ||||
|  |  |  |  |  |  |  |  |  |  |  |\ |  |  |  |  | | ||||
|  |  |  |  |  |  |  |  |  |  |  | \|  |  |  |  | | ||||
|  |  |  |  |  |  |  |  |  |  |  |  X  |  |  |  | | ||||
|  |  |  |  |  |  |  |  |  |  |  |  |\ |  |  |  | | ||||
|  |  |  |  |  |  |  |  |  |  |  |  | \|  |  |  | | ||||
|  |  |  |  |  |  |  |  |  |  |  |  |  X  |  |  | | ||||
|  |  |  |  |  |  |  |  |  |  |  |  |  |\ |  |  | | ||||
|  |  |  |  |  |  |  |  |  |  |  |  |  | \|  |  | | ||||
|  |  |  |  |  |  |  |  |  |  |  |  |  |  X  |  | | ||||
|  |  |  |  |  |  |  |  |  |  |  |  |  |  |\ |  | | ||||
|  |  |  |  |  |  |  |  |  |  |  |  |  |  | \|  | | ||||
|  |  |  |  |  |  |  |  |  |  |  |  |  |  |  X  | | ||||
|  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |\ | | ||||
|  |  |  |  |  |  |  |  |  |  |  |  |  |  |  | \| | ||||
|  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  @ | ||||
|  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  | | ||||
| "[1..], // trim newline at start
 | ||||
|         ); | ||||
|     } | ||||
| 
 | ||||
|     #[test] | ||||
|     fn test_reduce_diagram_9() { | ||||
|         let item_count = 9; | ||||
|         test_diagram( | ||||
|             reduce_ops(item_count), | ||||
|             item_count, | ||||
|             &r" | ||||
|  |  |  |  |  |  |  |  |  | | ||||
|  |  ●  |  ●  |  ●  |  ●  | | ||||
|  |  |\ |  |\ |  |\ |  |\ | | ||||
|  |  | \|  | \|  | \|  | \| | ||||
|  |  |  @  |  @  |  @  |  @ | ||||
|  |  |  |\ |  |  |  |\ |  | | ||||
|  |  |  | \|  |  |  | \|  | | ||||
|  |  |  |  X  |  |  |  X  | | ||||
|  |  |  |  |\ |  |  |  |\ | | ||||
|  |  |  |  | \|  |  |  | \| | ||||
|  |  |  |  |  @  |  |  |  @ | ||||
|  |  |  |  |  |\ |  |  |  | | ||||
|  |  |  |  |  | \|  |  |  | | ||||
|  |  |  |  |  |  X  |  |  | | ||||
|  |  |  |  |  |  |\ |  |  | | ||||
|  |  |  |  |  |  | \|  |  | | ||||
|  |  |  |  |  |  |  X  |  | | ||||
|  |  |  |  |  |  |  |\ |  | | ||||
|  |  |  |  |  |  |  | \|  | | ||||
|  |  |  |  |  |  |  |  X  | | ||||
|  |  |  |  |  |  |  |  |\ | | ||||
|  |  |  |  |  |  |  |  | \| | ||||
|  ●  |  |  |  |  |  |  |  @ | ||||
|  |\ |  |  |  |  |  |  |  | | ||||
|  | \|  |  |  |  |  |  |  | | ||||
|  |  X  |  |  |  |  |  |  | | ||||
|  |  |\ |  |  |  |  |  |  | | ||||
|  |  | \|  |  |  |  |  |  | | ||||
|  |  |  X  |  |  |  |  |  | | ||||
|  |  |  |\ |  |  |  |  |  | | ||||
|  |  |  | \|  |  |  |  |  | | ||||
|  |  |  |  X  |  |  |  |  | | ||||
|  |  |  |  |\ |  |  |  |  | | ||||
|  |  |  |  | \|  |  |  |  | | ||||
|  |  |  |  |  X  |  |  |  | | ||||
|  |  |  |  |  |\ |  |  |  | | ||||
|  |  |  |  |  | \|  |  |  | | ||||
|  |  |  |  |  |  X  |  |  | | ||||
|  |  |  |  |  |  |\ |  |  | | ||||
|  |  |  |  |  |  | \|  |  | | ||||
|  |  |  |  |  |  |  X  |  | | ||||
|  |  |  |  |  |  |  |\ |  | | ||||
|  |  |  |  |  |  |  | \|  | | ||||
|  |  |  |  |  |  |  |  X  | | ||||
|  |  |  |  |  |  |  |  |\ | | ||||
|  |  |  |  |  |  |  |  | \| | ||||
|  |  |  |  |  |  |  |  |  @ | ||||
|  |  |  |  |  |  |  |  |  | | ||||
| "[1..], // trim newline at start
 | ||||
|         ); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										240
									
								
								crates/fayalite/src/util/test_hasher.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										240
									
								
								crates/fayalite/src/util/test_hasher.rs
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,240 @@ | |||
| // SPDX-License-Identifier: LGPL-3.0-or-later
 | ||||
| // See Notices.txt for copyright information
 | ||||
| #![cfg(feature = "unstable-test-hasher")] | ||||
| 
 | ||||
| use std::{ | ||||
|     fmt::Write as _, | ||||
|     hash::{BuildHasher, Hash, Hasher}, | ||||
|     io::Write as _, | ||||
|     marker::PhantomData, | ||||
|     sync::LazyLock, | ||||
| }; | ||||
| 
 | ||||
| type BoxDynHasher = Box<dyn Hasher + Send + Sync>; | ||||
| type BoxDynBuildHasher = Box<dyn DynBuildHasherTrait + Send + Sync>; | ||||
| type BoxDynMakeBuildHasher = Box<dyn Fn() -> BoxDynBuildHasher + Send + Sync>; | ||||
| 
 | ||||
| trait TryGetDynBuildHasher: Copy { | ||||
|     type Type; | ||||
|     fn try_get_make_build_hasher(self) -> Option<BoxDynMakeBuildHasher>; | ||||
| } | ||||
| 
 | ||||
| impl<T> TryGetDynBuildHasher for PhantomData<T> { | ||||
|     type Type = T; | ||||
|     fn try_get_make_build_hasher(self) -> Option<BoxDynMakeBuildHasher> { | ||||
|         None | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<T: Default + BuildHasher<Hasher: Send + Sync + 'static> + Send + Sync + 'static + Clone> | ||||
|     TryGetDynBuildHasher for &'_ PhantomData<T> | ||||
| { | ||||
|     type Type = T; | ||||
|     fn try_get_make_build_hasher(self) -> Option<BoxDynMakeBuildHasher> { | ||||
|         Some(Box::new(|| Box::<DynBuildHasher<T>>::default())) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[derive(Default, Clone)] | ||||
| struct DynBuildHasher<T>(T); | ||||
| 
 | ||||
| trait DynBuildHasherTrait: BuildHasher<Hasher = BoxDynHasher> { | ||||
|     fn clone_dyn_build_hasher(&self) -> BoxDynBuildHasher; | ||||
| } | ||||
| 
 | ||||
| impl<BH: BuildHasher<Hasher: Send + Sync + 'static>> BuildHasher for DynBuildHasher<BH> { | ||||
|     type Hasher = BoxDynHasher; | ||||
| 
 | ||||
|     fn build_hasher(&self) -> Self::Hasher { | ||||
|         Box::new(self.0.build_hasher()) | ||||
|     } | ||||
| 
 | ||||
|     fn hash_one<T: Hash>(&self, x: T) -> u64 { | ||||
|         self.0.hash_one(x) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<BH> DynBuildHasherTrait for DynBuildHasher<BH> | ||||
| where | ||||
|     Self: Clone + BuildHasher<Hasher = BoxDynHasher> + Send + Sync + 'static, | ||||
| { | ||||
|     fn clone_dyn_build_hasher(&self) -> BoxDynBuildHasher { | ||||
|         Box::new(self.clone()) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| pub struct DefaultBuildHasher(BoxDynBuildHasher); | ||||
| 
 | ||||
| impl Clone for DefaultBuildHasher { | ||||
|     fn clone(&self) -> Self { | ||||
|         DefaultBuildHasher(self.0.clone_dyn_build_hasher()) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| const ENV_VAR_NAME: &'static str = "FAYALITE_TEST_HASHER"; | ||||
| 
 | ||||
| struct EnvVarValue { | ||||
|     key: &'static str, | ||||
|     try_get_make_build_hasher: fn() -> Option<BoxDynMakeBuildHasher>, | ||||
|     description: &'static str, | ||||
| } | ||||
| 
 | ||||
| macro_rules! env_var_value { | ||||
|     ( | ||||
|         key: $key:literal, | ||||
|         build_hasher: $build_hasher:ty, | ||||
|         description: $description:literal, | ||||
|     ) => { | ||||
|         EnvVarValue { | ||||
|             key: $key, | ||||
|             try_get_make_build_hasher: || { | ||||
|                 // use rust method resolution to detect if $build_hasher is usable
 | ||||
|                 // (e.g. hashbrown's hasher won't be usable without the right feature enabled)
 | ||||
|                 (&PhantomData::<DynBuildHasher<$build_hasher>>).try_get_make_build_hasher() | ||||
|             }, | ||||
|             description: $description, | ||||
|         } | ||||
|     }; | ||||
| } | ||||
| 
 | ||||
| #[derive(Default)] | ||||
| struct AlwaysZeroHasher; | ||||
| 
 | ||||
| impl Hasher for AlwaysZeroHasher { | ||||
|     fn write(&mut self, _bytes: &[u8]) {} | ||||
|     fn finish(&self) -> u64 { | ||||
|         0 | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| const ENV_VAR_VALUES: &'static [EnvVarValue] = &[ | ||||
|     env_var_value! { | ||||
|         key: "std", | ||||
|         build_hasher: std::hash::RandomState, | ||||
|         description: "use std::hash::RandomState", | ||||
|     }, | ||||
|     env_var_value! { | ||||
|         key: "hashbrown", | ||||
|         build_hasher: hashbrown::DefaultHashBuilder, | ||||
|         description: "use hashbrown's DefaultHashBuilder", | ||||
|     }, | ||||
|     env_var_value! { | ||||
|         key: "always_zero", | ||||
|         build_hasher: std::hash::BuildHasherDefault<AlwaysZeroHasher>, | ||||
|         description: "use a hasher that always returns 0 for all hashes,\n    \ | ||||
|         this is useful for checking that PartialEq impls are correct",
 | ||||
|     }, | ||||
| ]; | ||||
| 
 | ||||
| fn report_bad_env_var(msg: impl std::fmt::Display) -> ! { | ||||
|     let mut msg = format!("{ENV_VAR_NAME}: {msg}\n"); | ||||
|     for &EnvVarValue { | ||||
|         key, | ||||
|         try_get_make_build_hasher, | ||||
|         description, | ||||
|     } in ENV_VAR_VALUES | ||||
|     { | ||||
|         let availability = match try_get_make_build_hasher() { | ||||
|             Some(_) => "available", | ||||
|             None => "unavailable", | ||||
|         }; | ||||
|         writeln!(msg, "{key}: ({availability})\n    {description}").expect("can't fail"); | ||||
|     } | ||||
|     std::io::stderr() | ||||
|         .write_all(msg.as_bytes()) | ||||
|         .expect("should be able to write to stderr"); | ||||
|     std::process::abort(); | ||||
| } | ||||
| 
 | ||||
| impl Default for DefaultBuildHasher { | ||||
|     fn default() -> Self { | ||||
|         static DEFAULT_FN: LazyLock<BoxDynMakeBuildHasher> = LazyLock::new(|| { | ||||
|             let var = std::env::var_os(ENV_VAR_NAME); | ||||
|             let var = var.as_deref().unwrap_or("std".as_ref()); | ||||
|             for &EnvVarValue { | ||||
|                 key, | ||||
|                 try_get_make_build_hasher, | ||||
|                 description: _, | ||||
|             } in ENV_VAR_VALUES | ||||
|             { | ||||
|                 if var.as_encoded_bytes().eq_ignore_ascii_case(key.as_bytes()) { | ||||
|                     return try_get_make_build_hasher().unwrap_or_else(|| { | ||||
|                         report_bad_env_var(format_args!( | ||||
|                             "unavailable hasher: {key} (is the appropriate feature enabled?)" | ||||
|                         )); | ||||
|                     }); | ||||
|                 } | ||||
|             } | ||||
|             report_bad_env_var(format_args!("unrecognized hasher: {var:?}")); | ||||
|         }); | ||||
|         Self(DEFAULT_FN()) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| pub struct DefaultHasher(BoxDynHasher); | ||||
| 
 | ||||
| impl BuildHasher for DefaultBuildHasher { | ||||
|     type Hasher = DefaultHasher; | ||||
| 
 | ||||
|     fn build_hasher(&self) -> Self::Hasher { | ||||
|         DefaultHasher(self.0.build_hasher()) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl Hasher for DefaultHasher { | ||||
|     fn finish(&self) -> u64 { | ||||
|         self.0.finish() | ||||
|     } | ||||
| 
 | ||||
|     fn write(&mut self, bytes: &[u8]) { | ||||
|         self.0.write(bytes) | ||||
|     } | ||||
| 
 | ||||
|     fn write_u8(&mut self, i: u8) { | ||||
|         self.0.write_u8(i) | ||||
|     } | ||||
| 
 | ||||
|     fn write_u16(&mut self, i: u16) { | ||||
|         self.0.write_u16(i) | ||||
|     } | ||||
| 
 | ||||
|     fn write_u32(&mut self, i: u32) { | ||||
|         self.0.write_u32(i) | ||||
|     } | ||||
| 
 | ||||
|     fn write_u64(&mut self, i: u64) { | ||||
|         self.0.write_u64(i) | ||||
|     } | ||||
| 
 | ||||
|     fn write_u128(&mut self, i: u128) { | ||||
|         self.0.write_u128(i) | ||||
|     } | ||||
| 
 | ||||
|     fn write_usize(&mut self, i: usize) { | ||||
|         self.0.write_usize(i) | ||||
|     } | ||||
| 
 | ||||
|     fn write_i8(&mut self, i: i8) { | ||||
|         self.0.write_i8(i) | ||||
|     } | ||||
| 
 | ||||
|     fn write_i16(&mut self, i: i16) { | ||||
|         self.0.write_i16(i) | ||||
|     } | ||||
| 
 | ||||
|     fn write_i32(&mut self, i: i32) { | ||||
|         self.0.write_i32(i) | ||||
|     } | ||||
| 
 | ||||
|     fn write_i64(&mut self, i: i64) { | ||||
|         self.0.write_i64(i) | ||||
|     } | ||||
| 
 | ||||
|     fn write_i128(&mut self, i: i128) { | ||||
|         self.0.write_i128(i) | ||||
|     } | ||||
| 
 | ||||
|     fn write_isize(&mut self, i: isize) { | ||||
|         self.0.write_isize(i) | ||||
|     } | ||||
| } | ||||
|  | @ -4,11 +4,17 @@ use fayalite::{ | |||
|     bundle::BundleType, | ||||
|     enum_::EnumType, | ||||
|     int::{BoolOrIntType, IntType}, | ||||
|     phantom_const::PhantomConst, | ||||
|     prelude::*, | ||||
|     ty::StaticType, | ||||
| }; | ||||
| use std::marker::PhantomData; | ||||
| 
 | ||||
| #[hdl(outline_generated)] | ||||
| pub struct MyConstSize<V: Size> { | ||||
|     pub size: PhantomConst<UIntType<V>>, | ||||
| } | ||||
| 
 | ||||
| #[hdl(outline_generated)] | ||||
| pub struct S<T, Len: Size, T2> { | ||||
|     pub a: T, | ||||
|  |  | |||
|  | @ -1,8 +1,13 @@ | |||
| // SPDX-License-Identifier: LGPL-3.0-or-later
 | ||||
| // See Notices.txt for copyright information
 | ||||
| use fayalite::{ | ||||
|     assert_export_firrtl, firrtl::ExportOptions, intern::Intern, | ||||
|     module::transform::simplify_enums::SimplifyEnumsKind, prelude::*, reset::ResetType, | ||||
|     assert_export_firrtl, | ||||
|     firrtl::ExportOptions, | ||||
|     int::{UIntInRange, UIntInRangeInclusive}, | ||||
|     intern::Intern, | ||||
|     module::transform::simplify_enums::SimplifyEnumsKind, | ||||
|     prelude::*, | ||||
|     reset::ResetType, | ||||
|     ty::StaticType, | ||||
| }; | ||||
| use serde_json::json; | ||||
|  | @ -191,10 +196,14 @@ circuit check_array_repeat: | |||
|     }; | ||||
| } | ||||
| 
 | ||||
| pub trait UnknownTrait {} | ||||
| 
 | ||||
| impl<T: ?Sized> UnknownTrait for T {} | ||||
| 
 | ||||
| #[hdl_module(outline_generated)] | ||||
| pub fn check_skipped_generics<T, #[hdl(skip)] U, const N: usize, #[hdl(skip)] const M: usize>(v: U) | ||||
| where | ||||
|     T: StaticType, | ||||
|     T: StaticType + UnknownTrait, | ||||
|     ConstUsize<N>: KnownSize, | ||||
|     U: std::fmt::Display, | ||||
| { | ||||
|  | @ -376,18 +385,18 @@ circuit check_written_inside_both_if_else: | |||
|     }; | ||||
| } | ||||
| 
 | ||||
| #[hdl(outline_generated)] | ||||
| #[hdl(outline_generated, cmp_eq)] | ||||
| pub struct TestStruct<T> { | ||||
|     pub a: T, | ||||
|     pub b: UInt<8>, | ||||
| } | ||||
| 
 | ||||
| #[hdl(outline_generated)] | ||||
| #[hdl(outline_generated, cmp_eq)] | ||||
| pub struct TestStruct2 { | ||||
|     pub v: UInt<8>, | ||||
| } | ||||
| 
 | ||||
| #[hdl(outline_generated)] | ||||
| #[hdl(outline_generated, cmp_eq)] | ||||
| pub struct TestStruct3 {} | ||||
| 
 | ||||
| #[hdl_module(outline_generated)] | ||||
|  | @ -4421,3 +4430,204 @@ circuit check_let_patterns: | |||
| ",
 | ||||
|     }; | ||||
| } | ||||
| 
 | ||||
| #[hdl_module(outline_generated)] | ||||
| pub fn check_struct_cmp_eq() { | ||||
|     #[hdl] | ||||
|     let tuple_lhs: (UInt<1>, SInt<1>, Bool) = m.input(); | ||||
|     #[hdl] | ||||
|     let tuple_rhs: (UInt<1>, SInt<1>, Bool) = m.input(); | ||||
|     #[hdl] | ||||
|     let tuple_cmp_eq: Bool = m.output(); | ||||
|     connect(tuple_cmp_eq, tuple_lhs.cmp_eq(tuple_rhs)); | ||||
|     #[hdl] | ||||
|     let tuple_cmp_ne: Bool = m.output(); | ||||
|     connect(tuple_cmp_ne, tuple_lhs.cmp_ne(tuple_rhs)); | ||||
| 
 | ||||
|     #[hdl] | ||||
|     let test_struct_lhs: TestStruct<SInt<8>> = m.input(); | ||||
|     #[hdl] | ||||
|     let test_struct_rhs: TestStruct<SInt<8>> = m.input(); | ||||
|     #[hdl] | ||||
|     let test_struct_cmp_eq: Bool = m.output(); | ||||
|     connect(test_struct_cmp_eq, test_struct_lhs.cmp_eq(test_struct_rhs)); | ||||
|     #[hdl] | ||||
|     let test_struct_cmp_ne: Bool = m.output(); | ||||
|     connect(test_struct_cmp_ne, test_struct_lhs.cmp_ne(test_struct_rhs)); | ||||
| 
 | ||||
|     #[hdl] | ||||
|     let test_struct_2_lhs: TestStruct2 = m.input(); | ||||
|     #[hdl] | ||||
|     let test_struct_2_rhs: TestStruct2 = m.input(); | ||||
|     #[hdl] | ||||
|     let test_struct_2_cmp_eq: Bool = m.output(); | ||||
|     connect( | ||||
|         test_struct_2_cmp_eq, | ||||
|         test_struct_2_lhs.cmp_eq(test_struct_2_rhs), | ||||
|     ); | ||||
|     #[hdl] | ||||
|     let test_struct_2_cmp_ne: Bool = m.output(); | ||||
|     connect( | ||||
|         test_struct_2_cmp_ne, | ||||
|         test_struct_2_lhs.cmp_ne(test_struct_2_rhs), | ||||
|     ); | ||||
| 
 | ||||
|     #[hdl] | ||||
|     let test_struct_3_lhs: TestStruct3 = m.input(); | ||||
|     #[hdl] | ||||
|     let test_struct_3_rhs: TestStruct3 = m.input(); | ||||
|     #[hdl] | ||||
|     let test_struct_3_cmp_eq: Bool = m.output(); | ||||
|     connect( | ||||
|         test_struct_3_cmp_eq, | ||||
|         test_struct_3_lhs.cmp_eq(test_struct_3_rhs), | ||||
|     ); | ||||
|     #[hdl] | ||||
|     let test_struct_3_cmp_ne: Bool = m.output(); | ||||
|     connect( | ||||
|         test_struct_3_cmp_ne, | ||||
|         test_struct_3_lhs.cmp_ne(test_struct_3_rhs), | ||||
|     ); | ||||
| } | ||||
| 
 | ||||
| #[test] | ||||
| fn test_struct_cmp_eq() { | ||||
|     let _n = SourceLocation::normalize_files_for_tests(); | ||||
|     let m = check_struct_cmp_eq(); | ||||
|     dbg!(m); | ||||
|     #[rustfmt::skip] // work around https://github.com/rust-lang/rustfmt/issues/6161
 | ||||
|     assert_export_firrtl! { | ||||
|         m => | ||||
|         "/test/check_struct_cmp_eq.fir": r"FIRRTL version 3.2.0
 | ||||
| circuit check_struct_cmp_eq: | ||||
|     type Ty0 = {`0`: UInt<1>, `1`: SInt<1>, `2`: UInt<1>} | ||||
|     type Ty1 = {a: SInt<8>, b: UInt<8>} | ||||
|     type Ty2 = {v: UInt<8>} | ||||
|     type Ty3 = {} | ||||
|     module check_struct_cmp_eq: @[module-XXXXXXXXXX.rs 1:1] | ||||
|         input tuple_lhs: Ty0 @[module-XXXXXXXXXX.rs 2:1] | ||||
|         input tuple_rhs: Ty0 @[module-XXXXXXXXXX.rs 3:1] | ||||
|         output tuple_cmp_eq: UInt<1> @[module-XXXXXXXXXX.rs 4:1] | ||||
|         output tuple_cmp_ne: UInt<1> @[module-XXXXXXXXXX.rs 6:1] | ||||
|         input test_struct_lhs: Ty1 @[module-XXXXXXXXXX.rs 8:1] | ||||
|         input test_struct_rhs: Ty1 @[module-XXXXXXXXXX.rs 9:1] | ||||
|         output test_struct_cmp_eq: UInt<1> @[module-XXXXXXXXXX.rs 10:1] | ||||
|         output test_struct_cmp_ne: UInt<1> @[module-XXXXXXXXXX.rs 12:1] | ||||
|         input test_struct_2_lhs: Ty2 @[module-XXXXXXXXXX.rs 14:1] | ||||
|         input test_struct_2_rhs: Ty2 @[module-XXXXXXXXXX.rs 15:1] | ||||
|         output test_struct_2_cmp_eq: UInt<1> @[module-XXXXXXXXXX.rs 16:1] | ||||
|         output test_struct_2_cmp_ne: UInt<1> @[module-XXXXXXXXXX.rs 18:1] | ||||
|         input test_struct_3_lhs: Ty3 @[module-XXXXXXXXXX.rs 20:1] | ||||
|         input test_struct_3_rhs: Ty3 @[module-XXXXXXXXXX.rs 21:1] | ||||
|         output test_struct_3_cmp_eq: UInt<1> @[module-XXXXXXXXXX.rs 22:1] | ||||
|         output test_struct_3_cmp_ne: UInt<1> @[module-XXXXXXXXXX.rs 24:1] | ||||
|         wire _array_literal_expr: UInt<1>[3] | ||||
|         connect _array_literal_expr[0], eq(tuple_lhs.`0`, tuple_rhs.`0`) | ||||
|         connect _array_literal_expr[1], eq(tuple_lhs.`1`, tuple_rhs.`1`) | ||||
|         connect _array_literal_expr[2], eq(tuple_lhs.`2`, tuple_rhs.`2`) | ||||
|         wire _cast_array_to_bits_expr: UInt<1>[3] | ||||
|         connect _cast_array_to_bits_expr[0], _array_literal_expr[0] | ||||
|         connect _cast_array_to_bits_expr[1], _array_literal_expr[1] | ||||
|         connect _cast_array_to_bits_expr[2], _array_literal_expr[2] | ||||
|         wire _cast_to_bits_expr: UInt<3> | ||||
|         connect _cast_to_bits_expr, cat(_cast_array_to_bits_expr[2], cat(_cast_array_to_bits_expr[1], _cast_array_to_bits_expr[0])) | ||||
|         connect tuple_cmp_eq, andr(_cast_to_bits_expr) @[module-XXXXXXXXXX.rs 5:1] | ||||
|         wire _array_literal_expr_1: UInt<1>[3] | ||||
|         connect _array_literal_expr_1[0], neq(tuple_lhs.`0`, tuple_rhs.`0`) | ||||
|         connect _array_literal_expr_1[1], neq(tuple_lhs.`1`, tuple_rhs.`1`) | ||||
|         connect _array_literal_expr_1[2], neq(tuple_lhs.`2`, tuple_rhs.`2`) | ||||
|         wire _cast_array_to_bits_expr_1: UInt<1>[3] | ||||
|         connect _cast_array_to_bits_expr_1[0], _array_literal_expr_1[0] | ||||
|         connect _cast_array_to_bits_expr_1[1], _array_literal_expr_1[1] | ||||
|         connect _cast_array_to_bits_expr_1[2], _array_literal_expr_1[2] | ||||
|         wire _cast_to_bits_expr_1: UInt<3> | ||||
|         connect _cast_to_bits_expr_1, cat(_cast_array_to_bits_expr_1[2], cat(_cast_array_to_bits_expr_1[1], _cast_array_to_bits_expr_1[0])) | ||||
|         connect tuple_cmp_ne, orr(_cast_to_bits_expr_1) @[module-XXXXXXXXXX.rs 7:1] | ||||
|         connect test_struct_cmp_eq, and(eq(test_struct_lhs.a, test_struct_rhs.a), eq(test_struct_lhs.b, test_struct_rhs.b)) @[module-XXXXXXXXXX.rs 11:1] | ||||
|         connect test_struct_cmp_ne, or(neq(test_struct_lhs.a, test_struct_rhs.a), neq(test_struct_lhs.b, test_struct_rhs.b)) @[module-XXXXXXXXXX.rs 13:1] | ||||
|         connect test_struct_2_cmp_eq, eq(test_struct_2_lhs.v, test_struct_2_rhs.v) @[module-XXXXXXXXXX.rs 17:1] | ||||
|         connect test_struct_2_cmp_ne, neq(test_struct_2_lhs.v, test_struct_2_rhs.v) @[module-XXXXXXXXXX.rs 19:1] | ||||
|         connect test_struct_3_cmp_eq, UInt<1>(0h1) @[module-XXXXXXXXXX.rs 23:1] | ||||
|         connect test_struct_3_cmp_ne, UInt<1>(0h0) @[module-XXXXXXXXXX.rs 25:1] | ||||
| ",
 | ||||
|     }; | ||||
| } | ||||
| 
 | ||||
| #[hdl_module(outline_generated)] | ||||
| pub fn check_uint_in_range() { | ||||
|     #[hdl] | ||||
|     let i_0_to_1: UIntInRange<0, 1> = m.input(); | ||||
|     #[hdl] | ||||
|     let i_0_to_2: UIntInRange<0, 2> = m.input(); | ||||
|     #[hdl] | ||||
|     let i_0_to_3: UIntInRange<0, 3> = m.input(); | ||||
|     #[hdl] | ||||
|     let i_0_to_4: UIntInRange<0, 4> = m.input(); | ||||
|     #[hdl] | ||||
|     let i_0_to_7: UIntInRange<0, 7> = m.input(); | ||||
|     #[hdl] | ||||
|     let i_0_to_8: UIntInRange<0, 8> = m.input(); | ||||
|     #[hdl] | ||||
|     let i_0_to_9: UIntInRange<0, 9> = m.input(); | ||||
|     #[hdl] | ||||
|     let i_0_through_0: UIntInRangeInclusive<0, 0> = m.input(); | ||||
|     #[hdl] | ||||
|     let i_0_through_1: UIntInRangeInclusive<0, 1> = m.input(); | ||||
|     #[hdl] | ||||
|     let i_0_through_2: UIntInRangeInclusive<0, 2> = m.input(); | ||||
|     #[hdl] | ||||
|     let i_0_through_3: UIntInRangeInclusive<0, 3> = m.input(); | ||||
|     #[hdl] | ||||
|     let i_0_through_4: UIntInRangeInclusive<0, 4> = m.input(); | ||||
|     #[hdl] | ||||
|     let i_0_through_7: UIntInRangeInclusive<0, 7> = m.input(); | ||||
|     #[hdl] | ||||
|     let i_0_through_8: UIntInRangeInclusive<0, 8> = m.input(); | ||||
|     #[hdl] | ||||
|     let i_0_through_9: UIntInRangeInclusive<0, 9> = m.input(); | ||||
| } | ||||
| 
 | ||||
| #[test] | ||||
| fn test_uint_in_range() { | ||||
|     let _n = SourceLocation::normalize_files_for_tests(); | ||||
|     let m = check_uint_in_range(); | ||||
|     dbg!(m); | ||||
|     #[rustfmt::skip] // work around https://github.com/rust-lang/rustfmt/issues/6161
 | ||||
|     assert_export_firrtl! { | ||||
|         m => | ||||
|         "/test/check_uint_in_range.fir": r"FIRRTL version 3.2.0
 | ||||
| circuit check_uint_in_range: | ||||
|     type Ty0 = {value: UInt<0>, range: {}} | ||||
|     type Ty1 = {value: UInt<1>, range: {}} | ||||
|     type Ty2 = {value: UInt<2>, range: {}} | ||||
|     type Ty3 = {value: UInt<2>, range: {}} | ||||
|     type Ty4 = {value: UInt<3>, range: {}} | ||||
|     type Ty5 = {value: UInt<3>, range: {}} | ||||
|     type Ty6 = {value: UInt<4>, range: {}} | ||||
|     type Ty7 = {value: UInt<0>, range: {}} | ||||
|     type Ty8 = {value: UInt<1>, range: {}} | ||||
|     type Ty9 = {value: UInt<2>, range: {}} | ||||
|     type Ty10 = {value: UInt<2>, range: {}} | ||||
|     type Ty11 = {value: UInt<3>, range: {}} | ||||
|     type Ty12 = {value: UInt<3>, range: {}} | ||||
|     type Ty13 = {value: UInt<4>, range: {}} | ||||
|     type Ty14 = {value: UInt<4>, range: {}} | ||||
|     module check_uint_in_range: @[module-XXXXXXXXXX.rs 1:1] | ||||
|         input i_0_to_1: Ty0 @[module-XXXXXXXXXX.rs 2:1] | ||||
|         input i_0_to_2: Ty1 @[module-XXXXXXXXXX.rs 3:1] | ||||
|         input i_0_to_3: Ty2 @[module-XXXXXXXXXX.rs 4:1] | ||||
|         input i_0_to_4: Ty3 @[module-XXXXXXXXXX.rs 5:1] | ||||
|         input i_0_to_7: Ty4 @[module-XXXXXXXXXX.rs 6:1] | ||||
|         input i_0_to_8: Ty5 @[module-XXXXXXXXXX.rs 7:1] | ||||
|         input i_0_to_9: Ty6 @[module-XXXXXXXXXX.rs 8:1] | ||||
|         input i_0_through_0: Ty7 @[module-XXXXXXXXXX.rs 9:1] | ||||
|         input i_0_through_1: Ty8 @[module-XXXXXXXXXX.rs 10:1] | ||||
|         input i_0_through_2: Ty9 @[module-XXXXXXXXXX.rs 11:1] | ||||
|         input i_0_through_3: Ty10 @[module-XXXXXXXXXX.rs 12:1] | ||||
|         input i_0_through_4: Ty11 @[module-XXXXXXXXXX.rs 13:1] | ||||
|         input i_0_through_7: Ty12 @[module-XXXXXXXXXX.rs 14:1] | ||||
|         input i_0_through_8: Ty13 @[module-XXXXXXXXXX.rs 15:1] | ||||
|         input i_0_through_9: Ty14 @[module-XXXXXXXXXX.rs 16:1] | ||||
| ",
 | ||||
|     }; | ||||
| } | ||||
|  |  | |||
|  | @ -2,11 +2,11 @@ | |||
| // See Notices.txt for copyright information
 | ||||
| 
 | ||||
| use fayalite::{ | ||||
|     int::UIntValue, | ||||
|     memory::{ReadStruct, ReadWriteStruct, WriteStruct}, | ||||
|     module::{instance_with_loc, reg_builder_with_loc}, | ||||
|     prelude::*, | ||||
|     reset::ResetType, | ||||
|     sim::{time::SimDuration, vcd::VcdWriterDecls, Simulation, ToSimValue}, | ||||
|     ty::StaticType, | ||||
|     sim::vcd::VcdWriterDecls, | ||||
|     util::RcWriter, | ||||
| }; | ||||
| use std::num::NonZeroUsize; | ||||
|  | @ -316,8 +316,13 @@ pub fn enums() { | |||
|     let which_out: UInt<2> = m.output(); | ||||
|     #[hdl] | ||||
|     let data_out: UInt<4> = m.output(); | ||||
|     let b_out_ty = HdlOption[(UInt[1], Bool)]; | ||||
|     #[hdl] | ||||
|     let b_out: HdlOption<(UInt<1>, Bool)> = m.output(); | ||||
|     let b_out: HdlOption<(UInt, Bool)> = m.output(HdlOption[(UInt[1], Bool)]); | ||||
|     #[hdl] | ||||
|     let b2_out: HdlOption<(UInt<1>, Bool)> = m.output(); | ||||
| 
 | ||||
|     connect_any(b2_out, b_out); | ||||
| 
 | ||||
|     #[hdl] | ||||
|     struct MyStruct<T> { | ||||
|  | @ -357,7 +362,7 @@ pub fn enums() { | |||
|         } | ||||
|     } | ||||
| 
 | ||||
|     connect(b_out, HdlNone()); | ||||
|     connect(b_out, b_out_ty.HdlNone()); | ||||
| 
 | ||||
|     #[hdl] | ||||
|     match the_reg { | ||||
|  | @ -368,7 +373,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)); | ||||
|             connect_any(b_out, HdlSome(v)); | ||||
|         } | ||||
|         MyEnum::C(v) => { | ||||
|             connect(which_out, 2_hdl_u2); | ||||
|  | @ -384,132 +389,136 @@ fn test_enums() { | |||
|     let mut sim = Simulation::new(enums()); | ||||
|     let mut writer = RcWriter::default(); | ||||
|     sim.add_trace_writer(VcdWriterDecls::new(writer.clone())); | ||||
|     sim.write_clock(sim.io().cd.clk, false); | ||||
|     sim.write_reset(sim.io().cd.rst, true); | ||||
|     sim.write_bool(sim.io().en, false); | ||||
|     sim.write_bool_or_int(sim.io().which_in, 0_hdl_u2); | ||||
|     sim.write_bool_or_int(sim.io().data_in, 0_hdl_u4); | ||||
|     sim.write(sim.io().cd.clk, false); | ||||
|     sim.write(sim.io().cd.rst, true); | ||||
|     sim.write(sim.io().en, false); | ||||
|     sim.write(sim.io().which_in, 0_hdl_u2); | ||||
|     sim.write(sim.io().data_in, 0_hdl_u4); | ||||
|     sim.advance_time(SimDuration::from_micros(1)); | ||||
|     sim.write_clock(sim.io().cd.clk, true); | ||||
|     sim.write(sim.io().cd.clk, true); | ||||
|     sim.advance_time(SimDuration::from_nanos(100)); | ||||
|     sim.write_reset(sim.io().cd.rst, false); | ||||
|     sim.write(sim.io().cd.rst, false); | ||||
|     sim.advance_time(SimDuration::from_nanos(900)); | ||||
|     type BOutTy = HdlOption<(UInt<1>, Bool)>; | ||||
|     #[derive(Debug)] | ||||
|     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) | ||||
|         } | ||||
|     #[hdl(cmp_eq)] | ||||
|     struct IO<W: Size> { | ||||
|         en: Bool, | ||||
|         which_in: UInt<2>, | ||||
|         data_in: UInt<4>, | ||||
|         which_out: UInt<2>, | ||||
|         data_out: UInt<4>, | ||||
|         b_out: HdlOption<(UIntType<W>, Bool)>, | ||||
|         b2_out: HdlOption<(UInt<1>, Bool)>, | ||||
|     } | ||||
|     let io_ty = IO[1]; | ||||
|     let io_cycles = [ | ||||
|         IO { | ||||
|         #[hdl(sim)] | ||||
|         IO::<_> { | ||||
|             en: false, | ||||
|             which_in: 0, | ||||
|             data_in: 0, | ||||
|             which_out: 0, | ||||
|             data_out: 0, | ||||
|             b_out: HdlNone(), | ||||
|             which_in: 0_hdl_u2, | ||||
|             data_in: 0_hdl_u4, | ||||
|             which_out: 0_hdl_u2, | ||||
|             data_out: 0_hdl_u4, | ||||
|             b_out: #[hdl(sim)] | ||||
|             (io_ty.b_out).HdlNone(), | ||||
|             b2_out: #[hdl(sim)] | ||||
|             HdlNone(), | ||||
|         }, | ||||
|         IO { | ||||
|         #[hdl(sim)] | ||||
|         IO::<_> { | ||||
|             en: true, | ||||
|             which_in: 1, | ||||
|             data_in: 0, | ||||
|             which_out: 0, | ||||
|             data_out: 0, | ||||
|             b_out: HdlNone(), | ||||
|             which_in: 1_hdl_u2, | ||||
|             data_in: 0_hdl_u4, | ||||
|             which_out: 0_hdl_u2, | ||||
|             data_out: 0_hdl_u4, | ||||
|             b_out: #[hdl(sim)] | ||||
|             (io_ty.b_out).HdlNone(), | ||||
|             b2_out: #[hdl(sim)] | ||||
|             HdlNone(), | ||||
|         }, | ||||
|         IO { | ||||
|         #[hdl(sim)] | ||||
|         IO::<_> { | ||||
|             en: false, | ||||
|             which_in: 0, | ||||
|             data_in: 0, | ||||
|             which_out: 1, | ||||
|             data_out: 0, | ||||
|             b_out: HdlSome((0_hdl_u1, false)), | ||||
|             which_in: 0_hdl_u2, | ||||
|             data_in: 0_hdl_u4, | ||||
|             which_out: 1_hdl_u2, | ||||
|             data_out: 0_hdl_u4, | ||||
|             b_out: #[hdl(sim)] | ||||
|             (io_ty.b_out).HdlSome((0u8.cast_to(UInt[1]), false)), | ||||
|             b2_out: #[hdl(sim)] | ||||
|             HdlSome((0_hdl_u1, false)), | ||||
|         }, | ||||
|         IO { | ||||
|         #[hdl(sim)] | ||||
|         IO::<_> { | ||||
|             en: true, | ||||
|             which_in: 1, | ||||
|             data_in: 0xF, | ||||
|             which_out: 1, | ||||
|             data_out: 0, | ||||
|             b_out: HdlSome((0_hdl_u1, false)), | ||||
|             which_in: 1_hdl_u2, | ||||
|             data_in: 0xF_hdl_u4, | ||||
|             which_out: 1_hdl_u2, | ||||
|             data_out: 0_hdl_u4, | ||||
|             b_out: #[hdl(sim)] | ||||
|             (io_ty.b_out).HdlSome((0u8.cast_to(UInt[1]), false)), | ||||
|             b2_out: #[hdl(sim)] | ||||
|             HdlSome((0_hdl_u1, false)), | ||||
|         }, | ||||
|         IO { | ||||
|         #[hdl(sim)] | ||||
|         IO::<_> { | ||||
|             en: true, | ||||
|             which_in: 1, | ||||
|             data_in: 0xF, | ||||
|             which_out: 1, | ||||
|             data_out: 0x3, | ||||
|             b_out: HdlSome((1_hdl_u1, true)), | ||||
|             which_in: 1_hdl_u2, | ||||
|             data_in: 0xF_hdl_u4, | ||||
|             which_out: 1_hdl_u2, | ||||
|             data_out: 0x3_hdl_u4, | ||||
|             b_out: #[hdl(sim)] | ||||
|             (io_ty.b_out).HdlSome((1u8.cast_to(UInt[1]), true)), | ||||
|             b2_out: #[hdl(sim)] | ||||
|             HdlSome((1_hdl_u1, true)), | ||||
|         }, | ||||
|         IO { | ||||
|         #[hdl(sim)] | ||||
|         IO::<_> { | ||||
|             en: true, | ||||
|             which_in: 2, | ||||
|             data_in: 0xF, | ||||
|             which_out: 1, | ||||
|             data_out: 0x3, | ||||
|             b_out: HdlSome((1_hdl_u1, true)), | ||||
|             which_in: 2_hdl_u2, | ||||
|             data_in: 0xF_hdl_u4, | ||||
|             which_out: 1_hdl_u2, | ||||
|             data_out: 0x3_hdl_u4, | ||||
|             b_out: #[hdl(sim)] | ||||
|             (io_ty.b_out).HdlSome((1u8.cast_to(UInt[1]), true)), | ||||
|             b2_out: #[hdl(sim)] | ||||
|             HdlSome((1_hdl_u1, true)), | ||||
|         }, | ||||
|         IO { | ||||
|         #[hdl(sim)] | ||||
|         IO::<_> { | ||||
|             en: true, | ||||
|             which_in: 2, | ||||
|             data_in: 0xF, | ||||
|             which_out: 2, | ||||
|             data_out: 0xF, | ||||
|             b_out: HdlNone(), | ||||
|             which_in: 2_hdl_u2, | ||||
|             data_in: 0xF_hdl_u4, | ||||
|             which_out: 2_hdl_u2, | ||||
|             data_out: 0xF_hdl_u4, | ||||
|             b_out: #[hdl(sim)] | ||||
|             (io_ty.b_out).HdlNone(), | ||||
|             b2_out: #[hdl(sim)] | ||||
|             HdlNone(), | ||||
|         }, | ||||
|     ]; | ||||
|     for ( | ||||
|         cycle, | ||||
|         expected @ IO { | ||||
|     for (cycle, expected) in io_cycles.into_iter().enumerate() { | ||||
|         #[hdl(sim)] | ||||
|         let IO::<_> { | ||||
|             en, | ||||
|             which_in, | ||||
|             data_in, | ||||
|             which_out: _, | ||||
|             data_out: _, | ||||
|             b_out: _, | ||||
|         }, | ||||
|     ) in io_cycles.into_iter().enumerate() | ||||
|     { | ||||
|         sim.write_bool(sim.io().en, en); | ||||
|         sim.write_bool_or_int(sim.io().which_in, which_in.cast_to_static()); | ||||
|         sim.write_bool_or_int(sim.io().data_in, data_in.cast_to_static()); | ||||
|         let io = IO { | ||||
|             b2_out: _, | ||||
|         } = expected; | ||||
|         sim.write(sim.io().en, &en); | ||||
|         sim.write(sim.io().which_in, &which_in); | ||||
|         sim.write(sim.io().data_in, &data_in); | ||||
|         let io = #[hdl(sim)] | ||||
|         IO::<_> { | ||||
|             en, | ||||
|             which_in, | ||||
|             data_in, | ||||
|             which_out: sim | ||||
|                 .read_bool_or_int(sim.io().which_out) | ||||
|                 .to_bigint() | ||||
|                 .try_into() | ||||
|                 .expect("known to be in range"), | ||||
|             data_out: sim | ||||
|                 .read_bool_or_int(sim.io().data_out) | ||||
|                 .to_bigint() | ||||
|                 .try_into() | ||||
|                 .expect("known to be in range"), | ||||
|             b_out: sim.read(sim.io().b_out).to_expr(), | ||||
|             which_out: sim.read(sim.io().which_out), | ||||
|             data_out: sim.read(sim.io().data_out), | ||||
|             b_out: sim.read(sim.io().b_out), | ||||
|             b2_out: sim.read(sim.io().b2_out), | ||||
|         }; | ||||
|         assert_eq!( | ||||
|             expected, | ||||
|  | @ -517,6 +526,12 @@ fn test_enums() { | |||
|             "vcd:\n{}\ncycle: {cycle}", | ||||
|             String::from_utf8(writer.take()).unwrap(), | ||||
|         ); | ||||
|         // make sure matching on SimValue<SomeEnum> works
 | ||||
|         #[hdl(sim)] | ||||
|         match io.b_out { | ||||
|             HdlNone => println!("io.b_out is HdlNone"), | ||||
|             HdlSome(v) => println!("io.b_out is HdlSome(({:?}, {:?}))", *v.0, *v.1), | ||||
|         } | ||||
|         sim.write_clock(sim.io().cd.clk, false); | ||||
|         sim.advance_time(SimDuration::from_micros(1)); | ||||
|         sim.write_clock(sim.io().cd.clk, true); | ||||
|  | @ -538,9 +553,9 @@ fn test_enums() { | |||
| #[hdl_module(outline_generated)] | ||||
| pub fn memories() { | ||||
|     #[hdl] | ||||
|     let r: fayalite::memory::ReadStruct<(UInt<8>, SInt<8>), ConstUsize<4>> = m.input(); | ||||
|     let r: ReadStruct<(UInt<8>, SInt<8>), ConstUsize<4>> = m.input(); | ||||
|     #[hdl] | ||||
|     let w: fayalite::memory::WriteStruct<(UInt<8>, SInt<8>), ConstUsize<4>> = m.input(); | ||||
|     let w: WriteStruct<(UInt<8>, SInt<8>), ConstUsize<4>> = m.input(); | ||||
|     #[hdl] | ||||
|     let mut mem = memory_with_init([(0x01u8, 0x23i8); 16]); | ||||
|     mem.read_latency(0); | ||||
|  | @ -559,120 +574,131 @@ fn test_memories() { | |||
|     sim.add_trace_writer(VcdWriterDecls::new(writer.clone())); | ||||
|     sim.write_clock(sim.io().r.clk, false); | ||||
|     sim.write_clock(sim.io().w.clk, false); | ||||
|     #[derive(Debug, PartialEq, Eq)] | ||||
|     #[hdl(cmp_eq)] | ||||
|     struct IO { | ||||
|         r_addr: u8, | ||||
|         r_en: bool, | ||||
|         r_data: (u8, i8), | ||||
|         w_addr: u8, | ||||
|         w_en: bool, | ||||
|         w_data: (u8, i8), | ||||
|         w_mask: (bool, bool), | ||||
|         r_addr: UInt<4>, | ||||
|         r_en: Bool, | ||||
|         r_data: (UInt<8>, SInt<8>), | ||||
|         w_addr: UInt<4>, | ||||
|         w_en: Bool, | ||||
|         w_data: (UInt<8>, SInt<8>), | ||||
|         w_mask: (Bool, Bool), | ||||
|     } | ||||
|     let io_cycles = [ | ||||
|         #[hdl(sim)] | ||||
|         IO { | ||||
|             r_addr: 0, | ||||
|             r_addr: 0_hdl_u4, | ||||
|             r_en: false, | ||||
|             r_data: (0, 0), | ||||
|             w_addr: 0, | ||||
|             r_data: (0u8, 0i8), | ||||
|             w_addr: 0_hdl_u4, | ||||
|             w_en: false, | ||||
|             w_data: (0, 0), | ||||
|             w_data: (0u8, 0i8), | ||||
|             w_mask: (false, false), | ||||
|         }, | ||||
|         #[hdl(sim)] | ||||
|         IO { | ||||
|             r_addr: 0, | ||||
|             r_addr: 0_hdl_u4, | ||||
|             r_en: true, | ||||
|             r_data: (0x1, 0x23), | ||||
|             w_addr: 0, | ||||
|             r_data: (0x1u8, 0x23i8), | ||||
|             w_addr: 0_hdl_u4, | ||||
|             w_en: true, | ||||
|             w_data: (0x10, 0x20), | ||||
|             w_data: (0x10u8, 0x20i8), | ||||
|             w_mask: (true, true), | ||||
|         }, | ||||
|         #[hdl(sim)] | ||||
|         IO { | ||||
|             r_addr: 0, | ||||
|             r_addr: 0_hdl_u4, | ||||
|             r_en: true, | ||||
|             r_data: (0x10, 0x20), | ||||
|             w_addr: 0, | ||||
|             r_data: (0x10u8, 0x20i8), | ||||
|             w_addr: 0_hdl_u4, | ||||
|             w_en: true, | ||||
|             w_data: (0x30, 0x40), | ||||
|             w_data: (0x30u8, 0x40i8), | ||||
|             w_mask: (false, true), | ||||
|         }, | ||||
|         #[hdl(sim)] | ||||
|         IO { | ||||
|             r_addr: 0, | ||||
|             r_addr: 0_hdl_u4, | ||||
|             r_en: true, | ||||
|             r_data: (0x10, 0x40), | ||||
|             w_addr: 0, | ||||
|             r_data: (0x10u8, 0x40i8), | ||||
|             w_addr: 0_hdl_u4, | ||||
|             w_en: true, | ||||
|             w_data: (0x50, 0x60), | ||||
|             w_data: (0x50u8, 0x60i8), | ||||
|             w_mask: (true, false), | ||||
|         }, | ||||
|         #[hdl(sim)] | ||||
|         IO { | ||||
|             r_addr: 0, | ||||
|             r_addr: 0_hdl_u4, | ||||
|             r_en: true, | ||||
|             r_data: (0x50, 0x40), | ||||
|             w_addr: 0, | ||||
|             r_data: (0x50u8, 0x40i8), | ||||
|             w_addr: 0_hdl_u4, | ||||
|             w_en: true, | ||||
|             w_data: (0x70, -0x80), | ||||
|             w_data: (0x70u8, -0x80i8), | ||||
|             w_mask: (false, false), | ||||
|         }, | ||||
|         #[hdl(sim)] | ||||
|         IO { | ||||
|             r_addr: 0, | ||||
|             r_addr: 0_hdl_u4, | ||||
|             r_en: true, | ||||
|             r_data: (0x50, 0x40), | ||||
|             w_addr: 0, | ||||
|             r_data: (0x50u8, 0x40i8), | ||||
|             w_addr: 0_hdl_u4, | ||||
|             w_en: false, | ||||
|             w_data: (0x90, 0xA0u8 as i8), | ||||
|             w_data: (0x90u8, 0xA0u8 as i8), | ||||
|             w_mask: (false, false), | ||||
|         }, | ||||
|         #[hdl(sim)] | ||||
|         IO { | ||||
|             r_addr: 0, | ||||
|             r_addr: 0_hdl_u4, | ||||
|             r_en: true, | ||||
|             r_data: (0x50, 0x40), | ||||
|             w_addr: 1, | ||||
|             r_data: (0x50u8, 0x40i8), | ||||
|             w_addr: 1_hdl_u4, | ||||
|             w_en: true, | ||||
|             w_data: (0x90, 0xA0u8 as i8), | ||||
|             w_data: (0x90u8, 0xA0u8 as i8), | ||||
|             w_mask: (true, true), | ||||
|         }, | ||||
|         #[hdl(sim)] | ||||
|         IO { | ||||
|             r_addr: 0, | ||||
|             r_addr: 0_hdl_u4, | ||||
|             r_en: true, | ||||
|             r_data: (0x50, 0x40), | ||||
|             w_addr: 2, | ||||
|             r_data: (0x50u8, 0x40i8), | ||||
|             w_addr: 2_hdl_u4, | ||||
|             w_en: true, | ||||
|             w_data: (0xB0, 0xC0u8 as i8), | ||||
|             w_data: (0xB0u8, 0xC0u8 as i8), | ||||
|             w_mask: (true, true), | ||||
|         }, | ||||
|         #[hdl(sim)] | ||||
|         IO { | ||||
|             r_addr: 0, | ||||
|             r_addr: 0_hdl_u4, | ||||
|             r_en: true, | ||||
|             r_data: (0x50, 0x40), | ||||
|             w_addr: 2, | ||||
|             r_data: (0x50u8, 0x40i8), | ||||
|             w_addr: 2_hdl_u4, | ||||
|             w_en: false, | ||||
|             w_data: (0xD0, 0xE0u8 as i8), | ||||
|             w_data: (0xD0u8, 0xE0u8 as i8), | ||||
|             w_mask: (true, true), | ||||
|         }, | ||||
|         #[hdl(sim)] | ||||
|         IO { | ||||
|             r_addr: 1, | ||||
|             r_addr: 1_hdl_u4, | ||||
|             r_en: true, | ||||
|             r_data: (0x90, 0xA0u8 as i8), | ||||
|             w_addr: 2, | ||||
|             r_data: (0x90u8, 0xA0u8 as i8), | ||||
|             w_addr: 2_hdl_u4, | ||||
|             w_en: false, | ||||
|             w_data: (0xD0, 0xE0u8 as i8), | ||||
|             w_data: (0xD0u8, 0xE0u8 as i8), | ||||
|             w_mask: (true, true), | ||||
|         }, | ||||
|         #[hdl(sim)] | ||||
|         IO { | ||||
|             r_addr: 2, | ||||
|             r_addr: 2_hdl_u4, | ||||
|             r_en: true, | ||||
|             r_data: (0xB0, 0xC0u8 as i8), | ||||
|             w_addr: 2, | ||||
|             r_data: (0xB0u8, 0xC0u8 as i8), | ||||
|             w_addr: 2_hdl_u4, | ||||
|             w_en: false, | ||||
|             w_data: (0xD0, 0xE0u8 as i8), | ||||
|             w_data: (0xD0u8, 0xE0u8 as i8), | ||||
|             w_mask: (true, true), | ||||
|         }, | ||||
|     ]; | ||||
|     for ( | ||||
|         cycle, | ||||
|         expected @ IO { | ||||
|     for (cycle, expected) in io_cycles.into_iter().enumerate() { | ||||
|         #[hdl(sim)] | ||||
|         let IO { | ||||
|             r_addr, | ||||
|             r_en, | ||||
|             r_data: _, | ||||
|  | @ -680,30 +706,18 @@ fn test_memories() { | |||
|             w_en, | ||||
|             w_data, | ||||
|             w_mask, | ||||
|         }, | ||||
|     ) in io_cycles.into_iter().enumerate() | ||||
|     { | ||||
|         sim.write_bool_or_int(sim.io().r.addr, r_addr.cast_to_static()); | ||||
|         sim.write_bool(sim.io().r.en, r_en); | ||||
|         sim.write_bool_or_int(sim.io().w.addr, w_addr.cast_to_static()); | ||||
|         sim.write_bool(sim.io().w.en, w_en); | ||||
|         sim.write_bool_or_int(sim.io().w.data.0, w_data.0); | ||||
|         sim.write_bool_or_int(sim.io().w.data.1, w_data.1); | ||||
|         sim.write_bool(sim.io().w.mask.0, w_mask.0); | ||||
|         sim.write_bool(sim.io().w.mask.1, w_mask.1); | ||||
|         let io = IO { | ||||
|         } = expected; | ||||
|         sim.write(sim.io().r.addr, &r_addr); | ||||
|         sim.write(sim.io().r.en, &r_en); | ||||
|         sim.write(sim.io().w.addr, &w_addr); | ||||
|         sim.write(sim.io().w.en, &w_en); | ||||
|         sim.write(sim.io().w.data, &w_data); | ||||
|         sim.write(sim.io().w.mask, &w_mask); | ||||
|         let io = #[hdl(sim)] | ||||
|         IO { | ||||
|             r_addr, | ||||
|             r_en, | ||||
|             r_data: ( | ||||
|                 sim.read_bool_or_int(sim.io().r.data.0) | ||||
|                     .to_bigint() | ||||
|                     .try_into() | ||||
|                     .expect("known to be in range"), | ||||
|                 sim.read_bool_or_int(sim.io().r.data.1) | ||||
|                     .to_bigint() | ||||
|                     .try_into() | ||||
|                     .expect("known to be in range"), | ||||
|             ), | ||||
|             r_data: sim.read(sim.io().r.data), | ||||
|             w_addr, | ||||
|             w_en, | ||||
|             w_data, | ||||
|  | @ -716,11 +730,11 @@ fn test_memories() { | |||
|             String::from_utf8(writer.take()).unwrap(), | ||||
|         ); | ||||
|         sim.advance_time(SimDuration::from_micros(1)); | ||||
|         sim.write_clock(sim.io().r.clk, true); | ||||
|         sim.write_clock(sim.io().w.clk, true); | ||||
|         sim.write(sim.io().r.clk, true); | ||||
|         sim.write(sim.io().w.clk, true); | ||||
|         sim.advance_time(SimDuration::from_micros(1)); | ||||
|         sim.write_clock(sim.io().r.clk, false); | ||||
|         sim.write_clock(sim.io().w.clk, false); | ||||
|         sim.write(sim.io().r.clk, false); | ||||
|         sim.write(sim.io().w.clk, false); | ||||
|     } | ||||
|     sim.flush_traces().unwrap(); | ||||
|     let vcd = String::from_utf8(writer.take()).unwrap(); | ||||
|  | @ -738,7 +752,7 @@ fn test_memories() { | |||
| #[hdl_module(outline_generated)] | ||||
| pub fn memories2() { | ||||
|     #[hdl] | ||||
|     let rw: fayalite::memory::ReadWriteStruct<UInt<2>, ConstUsize<3>> = m.input(); | ||||
|     let rw: ReadWriteStruct<UInt<2>, ConstUsize<3>> = m.input(); | ||||
|     #[hdl] | ||||
|     let mut mem = memory_with_init([HdlSome(true); 5]); | ||||
|     mem.read_latency(1); | ||||
|  | @ -1011,9 +1025,9 @@ fn test_memories2() { | |||
| #[hdl_module(outline_generated)] | ||||
| pub fn memories3() { | ||||
|     #[hdl] | ||||
|     let r: fayalite::memory::ReadStruct<Array<UInt<8>, 8>, ConstUsize<3>> = m.input(); | ||||
|     let r: ReadStruct<Array<UInt<8>, 8>, ConstUsize<3>> = m.input(); | ||||
|     #[hdl] | ||||
|     let w: fayalite::memory::WriteStruct<Array<UInt<8>, 8>, ConstUsize<3>> = m.input(); | ||||
|     let w: WriteStruct<Array<UInt<8>, 8>, ConstUsize<3>> = m.input(); | ||||
|     #[hdl] | ||||
|     let mut mem: MemBuilder<Array<UInt<8>, 8>> = memory(); | ||||
|     mem.depth(8); | ||||
|  | @ -1443,3 +1457,172 @@ fn test_conditional_assignment_last() { | |||
|         panic!(); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[hdl_module(outline_generated, extern)] | ||||
| pub fn extern_module() { | ||||
|     #[hdl] | ||||
|     let i: Bool = m.input(); | ||||
|     #[hdl] | ||||
|     let o: Bool = m.output(); | ||||
|     m.extern_module_simulation_fn((i, o), |(i, o), mut sim| async move { | ||||
|         sim.write(o, true).await; | ||||
|         sim.advance_time(SimDuration::from_nanos(500)).await; | ||||
|         let mut invert = false; | ||||
|         loop { | ||||
|             sim.advance_time(SimDuration::from_micros(1)).await; | ||||
|             let v = sim.read_bool(i).await; | ||||
|             sim.write(o, v ^ invert).await; | ||||
|             invert = !invert; | ||||
|         } | ||||
|     }); | ||||
| } | ||||
| 
 | ||||
| #[test] | ||||
| fn test_extern_module() { | ||||
|     let _n = SourceLocation::normalize_files_for_tests(); | ||||
|     let mut sim = Simulation::new(extern_module()); | ||||
|     let mut writer = RcWriter::default(); | ||||
|     sim.add_trace_writer(VcdWriterDecls::new(writer.clone())); | ||||
|     sim.write(sim.io().i, false); | ||||
|     sim.advance_time(SimDuration::from_micros(10)); | ||||
|     sim.write(sim.io().i, true); | ||||
|     sim.advance_time(SimDuration::from_micros(10)); | ||||
|     sim.flush_traces().unwrap(); | ||||
|     let vcd = String::from_utf8(writer.take()).unwrap(); | ||||
|     println!("####### VCD:\n{vcd}\n#######"); | ||||
|     if vcd != include_str!("sim/expected/extern_module.vcd") { | ||||
|         panic!(); | ||||
|     } | ||||
|     let sim_debug = format!("{sim:#?}"); | ||||
|     println!("#######\n{sim_debug}\n#######"); | ||||
|     if sim_debug != include_str!("sim/expected/extern_module.txt") { | ||||
|         panic!(); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[hdl_module(outline_generated, extern)] | ||||
| pub fn extern_module2() { | ||||
|     #[hdl] | ||||
|     let en: Bool = m.input(); | ||||
|     #[hdl] | ||||
|     let clk: Clock = m.input(); | ||||
|     #[hdl] | ||||
|     let o: UInt<8> = m.output(); | ||||
|     m.extern_module_simulation_fn((en, clk, o), |(en, clk, o), mut sim| async move { | ||||
|         for b in "Hello, World!\n".bytes().cycle() { | ||||
|             sim.write(o, b).await; | ||||
|             loop { | ||||
|                 sim.wait_for_clock_edge(clk).await; | ||||
|                 if sim.read_bool(en).await { | ||||
|                     break; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     }); | ||||
| } | ||||
| 
 | ||||
| #[test] | ||||
| fn test_extern_module2() { | ||||
|     let _n = SourceLocation::normalize_files_for_tests(); | ||||
|     let mut sim = Simulation::new(extern_module2()); | ||||
|     let mut writer = RcWriter::default(); | ||||
|     sim.add_trace_writer(VcdWriterDecls::new(writer.clone())); | ||||
|     for i in 0..30 { | ||||
|         sim.write(sim.io().en, i % 10 < 5); | ||||
|         sim.write(sim.io().clk, false); | ||||
|         sim.advance_time(SimDuration::from_micros(1)); | ||||
|         sim.write(sim.io().clk, true); | ||||
|         sim.advance_time(SimDuration::from_micros(1)); | ||||
|     } | ||||
|     sim.flush_traces().unwrap(); | ||||
|     let vcd = String::from_utf8(writer.take()).unwrap(); | ||||
|     println!("####### VCD:\n{vcd}\n#######"); | ||||
|     if vcd != include_str!("sim/expected/extern_module2.vcd") { | ||||
|         panic!(); | ||||
|     } | ||||
|     let sim_debug = format!("{sim:#?}"); | ||||
|     println!("#######\n{sim_debug}\n#######"); | ||||
|     if sim_debug != include_str!("sim/expected/extern_module2.txt") { | ||||
|         panic!(); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| // use an extern module to simulate a register to test that the
 | ||||
| // simulator can handle chains of alternating circuits and extern modules.
 | ||||
| #[hdl_module(outline_generated, extern)] | ||||
| pub fn sw_reg() { | ||||
|     #[hdl] | ||||
|     let clk: Clock = m.input(); | ||||
|     #[hdl] | ||||
|     let o: Bool = m.output(); | ||||
|     m.extern_module_simulation_fn((clk, o), |(clk, o), mut sim| async move { | ||||
|         let mut state = false; | ||||
|         loop { | ||||
|             sim.write(o, state).await; | ||||
|             sim.wait_for_clock_edge(clk).await; | ||||
|             state = !state; | ||||
|         } | ||||
|     }); | ||||
| } | ||||
| 
 | ||||
| #[hdl_module(outline_generated)] | ||||
| pub fn ripple_counter() { | ||||
|     #[hdl] | ||||
|     let clk: Clock = m.input(); | ||||
|     #[hdl] | ||||
|     let o: UInt<6> = m.output(); | ||||
| 
 | ||||
|     #[hdl] | ||||
|     let bits: Array<Bool, 6> = wire(); | ||||
| 
 | ||||
|     connect_any(o, bits.cast_to_bits()); | ||||
| 
 | ||||
|     let mut clk_in = clk; | ||||
|     for (i, bit) in bits.into_iter().enumerate() { | ||||
|         if i % 2 == 0 { | ||||
|             let bit_reg = reg_builder_with_loc(&format!("bit_reg_{i}"), SourceLocation::caller()) | ||||
|                 .clock_domain( | ||||
|                     #[hdl] | ||||
|                     ClockDomain { | ||||
|                         clk: clk_in, | ||||
|                         rst: false.to_sync_reset(), | ||||
|                     }, | ||||
|                 ) | ||||
|                 .no_reset(Bool) | ||||
|                 .build(); | ||||
|             connect(bit, bit_reg); | ||||
|             connect(bit_reg, !bit_reg); | ||||
|         } else { | ||||
|             let bit_reg = | ||||
|                 instance_with_loc(&format!("bit_reg_{i}"), sw_reg(), SourceLocation::caller()); | ||||
|             connect(bit_reg.clk, clk_in); | ||||
|             connect(bit, bit_reg.o); | ||||
|         } | ||||
|         clk_in = bit.to_clock(); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[test] | ||||
| fn test_ripple_counter() { | ||||
|     let _n = SourceLocation::normalize_files_for_tests(); | ||||
|     let mut sim = Simulation::new(ripple_counter()); | ||||
|     let mut writer = RcWriter::default(); | ||||
|     sim.add_trace_writer(VcdWriterDecls::new(writer.clone())); | ||||
|     for _ in 0..0x80 { | ||||
|         sim.write(sim.io().clk, false); | ||||
|         sim.advance_time(SimDuration::from_micros(1)); | ||||
|         sim.write(sim.io().clk, true); | ||||
|         sim.advance_time(SimDuration::from_micros(1)); | ||||
|     } | ||||
|     sim.flush_traces().unwrap(); | ||||
|     let vcd = String::from_utf8(writer.take()).unwrap(); | ||||
|     println!("####### VCD:\n{vcd}\n#######"); | ||||
|     if vcd != include_str!("sim/expected/ripple_counter.vcd") { | ||||
|         panic!(); | ||||
|     } | ||||
|     let sim_debug = format!("{sim:#?}"); | ||||
|     println!("#######\n{sim_debug}\n#######"); | ||||
|     if sim_debug != include_str!("sim/expected/ripple_counter.txt") { | ||||
|         panic!(); | ||||
|     } | ||||
| } | ||||
|  |  | |||
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							|  | @ -92,7 +92,17 @@ Simulation { | |||
|             .. | ||||
|         }, | ||||
|     }, | ||||
|     uninitialized_inputs: {}, | ||||
|     main_module: SimulationModuleState { | ||||
|         base_targets: [ | ||||
|             Instance { | ||||
|                 name: <simulator>::conditional_assignment_last, | ||||
|                 instantiated: Module { | ||||
|                     name: conditional_assignment_last, | ||||
|                     .. | ||||
|                 }, | ||||
|             }.i, | ||||
|         ], | ||||
|         uninitialized_ios: {}, | ||||
|         io_targets: { | ||||
|             Instance { | ||||
|                 name: <simulator>::conditional_assignment_last, | ||||
|  | @ -100,37 +110,12 @@ Simulation { | |||
|                     name: conditional_assignment_last, | ||||
|                     .. | ||||
|                 }, | ||||
|         }.i: CompiledValue { | ||||
|             layout: CompiledTypeLayout { | ||||
|                 ty: Bool, | ||||
|                 layout: TypeLayout { | ||||
|                     small_slots: StatePartLayout<SmallSlots> { | ||||
|                         len: 0, | ||||
|                         debug_data: [], | ||||
|                         .. | ||||
|             }.i, | ||||
|         }, | ||||
|                     big_slots: StatePartLayout<BigSlots> { | ||||
|                         len: 1, | ||||
|                         debug_data: [ | ||||
|                             SlotDebugData { | ||||
|                                 name: "InstantiatedModule(conditional_assignment_last: conditional_assignment_last).conditional_assignment_last::i", | ||||
|                                 ty: Bool, | ||||
|         did_initial_settle: true, | ||||
|     }, | ||||
|                         ], | ||||
|                         .. | ||||
|                     }, | ||||
|                 }, | ||||
|                 body: Scalar, | ||||
|             }, | ||||
|             range: TypeIndexRange { | ||||
|                 small_slots: StatePartIndexRange<SmallSlots> { start: 0, len: 0 }, | ||||
|                 big_slots: StatePartIndexRange<BigSlots> { start: 0, len: 1 }, | ||||
|             }, | ||||
|             write: None, | ||||
|         }, | ||||
|     }, | ||||
|     made_initial_step: true, | ||||
|     needs_settle: false, | ||||
|     extern_modules: [], | ||||
|     state_ready_to_run: false, | ||||
|     trace_decls: TraceModule { | ||||
|         name: "conditional_assignment_last", | ||||
|         children: [ | ||||
|  |  | |||
|  | @ -68,7 +68,17 @@ Simulation { | |||
|             .. | ||||
|         }, | ||||
|     }, | ||||
|     uninitialized_inputs: {}, | ||||
|     main_module: SimulationModuleState { | ||||
|         base_targets: [ | ||||
|             Instance { | ||||
|                 name: <simulator>::connect_const, | ||||
|                 instantiated: Module { | ||||
|                     name: connect_const, | ||||
|                     .. | ||||
|                 }, | ||||
|             }.o, | ||||
|         ], | ||||
|         uninitialized_ios: {}, | ||||
|         io_targets: { | ||||
|             Instance { | ||||
|                 name: <simulator>::connect_const, | ||||
|  | @ -76,37 +86,12 @@ Simulation { | |||
|                     name: connect_const, | ||||
|                     .. | ||||
|                 }, | ||||
|         }.o: CompiledValue { | ||||
|             layout: CompiledTypeLayout { | ||||
|                 ty: UInt<8>, | ||||
|                 layout: TypeLayout { | ||||
|                     small_slots: StatePartLayout<SmallSlots> { | ||||
|                         len: 0, | ||||
|                         debug_data: [], | ||||
|                         .. | ||||
|             }.o, | ||||
|         }, | ||||
|                     big_slots: StatePartLayout<BigSlots> { | ||||
|                         len: 1, | ||||
|                         debug_data: [ | ||||
|                             SlotDebugData { | ||||
|                                 name: "InstantiatedModule(connect_const: connect_const).connect_const::o", | ||||
|                                 ty: UInt<8>, | ||||
|         did_initial_settle: true, | ||||
|     }, | ||||
|                         ], | ||||
|                         .. | ||||
|                     }, | ||||
|                 }, | ||||
|                 body: Scalar, | ||||
|             }, | ||||
|             range: TypeIndexRange { | ||||
|                 small_slots: StatePartIndexRange<SmallSlots> { start: 0, len: 0 }, | ||||
|                 big_slots: StatePartIndexRange<BigSlots> { start: 0, len: 1 }, | ||||
|             }, | ||||
|             write: None, | ||||
|         }, | ||||
|     }, | ||||
|     made_initial_step: true, | ||||
|     needs_settle: false, | ||||
|     extern_modules: [], | ||||
|     state_ready_to_run: false, | ||||
|     trace_decls: TraceModule { | ||||
|         name: "connect_const", | ||||
|         children: [ | ||||
|  |  | |||
|  | @ -97,7 +97,24 @@ Simulation { | |||
|             .. | ||||
|         }, | ||||
|     }, | ||||
|     uninitialized_inputs: {}, | ||||
|     main_module: SimulationModuleState { | ||||
|         base_targets: [ | ||||
|             Instance { | ||||
|                 name: <simulator>::connect_const_reset, | ||||
|                 instantiated: Module { | ||||
|                     name: connect_const_reset, | ||||
|                     .. | ||||
|                 }, | ||||
|             }.reset_out, | ||||
|             Instance { | ||||
|                 name: <simulator>::connect_const_reset, | ||||
|                 instantiated: Module { | ||||
|                     name: connect_const_reset, | ||||
|                     .. | ||||
|                 }, | ||||
|             }.bit_out, | ||||
|         ], | ||||
|         uninitialized_ios: {}, | ||||
|         io_targets: { | ||||
|             Instance { | ||||
|                 name: <simulator>::connect_const_reset, | ||||
|  | @ -105,71 +122,19 @@ Simulation { | |||
|                     name: connect_const_reset, | ||||
|                     .. | ||||
|                 }, | ||||
|         }.bit_out: CompiledValue { | ||||
|             layout: CompiledTypeLayout { | ||||
|                 ty: Bool, | ||||
|                 layout: TypeLayout { | ||||
|                     small_slots: StatePartLayout<SmallSlots> { | ||||
|                         len: 0, | ||||
|                         debug_data: [], | ||||
|                         .. | ||||
|                     }, | ||||
|                     big_slots: StatePartLayout<BigSlots> { | ||||
|                         len: 1, | ||||
|                         debug_data: [ | ||||
|                             SlotDebugData { | ||||
|                                 name: "InstantiatedModule(connect_const_reset: connect_const_reset).connect_const_reset::bit_out", | ||||
|                                 ty: Bool, | ||||
|                             }, | ||||
|                         ], | ||||
|                         .. | ||||
|                     }, | ||||
|                 }, | ||||
|                 body: Scalar, | ||||
|             }, | ||||
|             range: TypeIndexRange { | ||||
|                 small_slots: StatePartIndexRange<SmallSlots> { start: 0, len: 0 }, | ||||
|                 big_slots: StatePartIndexRange<BigSlots> { start: 1, len: 1 }, | ||||
|             }, | ||||
|             write: None, | ||||
|         }, | ||||
|             }.bit_out, | ||||
|             Instance { | ||||
|                 name: <simulator>::connect_const_reset, | ||||
|                 instantiated: Module { | ||||
|                     name: connect_const_reset, | ||||
|                     .. | ||||
|                 }, | ||||
|         }.reset_out: CompiledValue { | ||||
|             layout: CompiledTypeLayout { | ||||
|                 ty: AsyncReset, | ||||
|                 layout: TypeLayout { | ||||
|                     small_slots: StatePartLayout<SmallSlots> { | ||||
|                         len: 0, | ||||
|                         debug_data: [], | ||||
|                         .. | ||||
|             }.reset_out, | ||||
|         }, | ||||
|                     big_slots: StatePartLayout<BigSlots> { | ||||
|                         len: 1, | ||||
|                         debug_data: [ | ||||
|                             SlotDebugData { | ||||
|                                 name: "InstantiatedModule(connect_const_reset: connect_const_reset).connect_const_reset::reset_out", | ||||
|                                 ty: AsyncReset, | ||||
|         did_initial_settle: true, | ||||
|     }, | ||||
|                         ], | ||||
|                         .. | ||||
|                     }, | ||||
|                 }, | ||||
|                 body: Scalar, | ||||
|             }, | ||||
|             range: TypeIndexRange { | ||||
|                 small_slots: StatePartIndexRange<SmallSlots> { start: 0, len: 0 }, | ||||
|                 big_slots: StatePartIndexRange<BigSlots> { start: 0, len: 1 }, | ||||
|             }, | ||||
|             write: None, | ||||
|         }, | ||||
|     }, | ||||
|     made_initial_step: true, | ||||
|     needs_settle: false, | ||||
|     extern_modules: [], | ||||
|     state_ready_to_run: false, | ||||
|     trace_decls: TraceModule { | ||||
|         name: "connect_const_reset", | ||||
|         children: [ | ||||
|  |  | |||
|  | @ -203,7 +203,24 @@ Simulation { | |||
|             .. | ||||
|         }, | ||||
|     }, | ||||
|     uninitialized_inputs: {}, | ||||
|     main_module: SimulationModuleState { | ||||
|         base_targets: [ | ||||
|             Instance { | ||||
|                 name: <simulator>::counter, | ||||
|                 instantiated: Module { | ||||
|                     name: counter, | ||||
|                     .. | ||||
|                 }, | ||||
|             }.cd, | ||||
|             Instance { | ||||
|                 name: <simulator>::counter, | ||||
|                 instantiated: Module { | ||||
|                     name: counter, | ||||
|                     .. | ||||
|                 }, | ||||
|             }.count, | ||||
|         ], | ||||
|         uninitialized_ios: {}, | ||||
|         io_targets: { | ||||
|             Instance { | ||||
|                 name: <simulator>::counter, | ||||
|  | @ -211,205 +228,33 @@ Simulation { | |||
|                     name: counter, | ||||
|                     .. | ||||
|                 }, | ||||
|         }.cd: CompiledValue { | ||||
|             layout: CompiledTypeLayout { | ||||
|                 ty: Bundle { | ||||
|                     /* offset = 0 */ | ||||
|                     clk: Clock, | ||||
|                     /* offset = 1 */ | ||||
|                     rst: AsyncReset, | ||||
|                 }, | ||||
|                 layout: TypeLayout { | ||||
|                     small_slots: StatePartLayout<SmallSlots> { | ||||
|                         len: 0, | ||||
|                         debug_data: [], | ||||
|                         .. | ||||
|                     }, | ||||
|                     big_slots: StatePartLayout<BigSlots> { | ||||
|                         len: 2, | ||||
|                         debug_data: [ | ||||
|                             SlotDebugData { | ||||
|                                 name: "InstantiatedModule(counter: counter).counter::cd.clk", | ||||
|                                 ty: Clock, | ||||
|                             }, | ||||
|                             SlotDebugData { | ||||
|                                 name: "InstantiatedModule(counter: counter).counter::cd.rst", | ||||
|                                 ty: AsyncReset, | ||||
|                             }, | ||||
|                         ], | ||||
|                         .. | ||||
|                     }, | ||||
|                 }, | ||||
|                 body: Bundle { | ||||
|                     fields: [ | ||||
|                         CompiledBundleField { | ||||
|                             offset: TypeIndex { | ||||
|                                 small_slots: StatePartIndex<SmallSlots>(0), | ||||
|                                 big_slots: StatePartIndex<BigSlots>(0), | ||||
|                             }, | ||||
|                             ty: CompiledTypeLayout { | ||||
|                                 ty: Clock, | ||||
|                                 layout: TypeLayout { | ||||
|                                     small_slots: StatePartLayout<SmallSlots> { | ||||
|                                         len: 0, | ||||
|                                         debug_data: [], | ||||
|                                         .. | ||||
|                                     }, | ||||
|                                     big_slots: StatePartLayout<BigSlots> { | ||||
|                                         len: 1, | ||||
|                                         debug_data: [ | ||||
|                                             SlotDebugData { | ||||
|                                                 name: "", | ||||
|                                                 ty: Clock, | ||||
|                                             }, | ||||
|                                         ], | ||||
|                                         .. | ||||
|                                     }, | ||||
|                                 }, | ||||
|                                 body: Scalar, | ||||
|                             }, | ||||
|                         }, | ||||
|                         CompiledBundleField { | ||||
|                             offset: TypeIndex { | ||||
|                                 small_slots: StatePartIndex<SmallSlots>(0), | ||||
|                                 big_slots: StatePartIndex<BigSlots>(1), | ||||
|                             }, | ||||
|                             ty: CompiledTypeLayout { | ||||
|                                 ty: AsyncReset, | ||||
|                                 layout: TypeLayout { | ||||
|                                     small_slots: StatePartLayout<SmallSlots> { | ||||
|                                         len: 0, | ||||
|                                         debug_data: [], | ||||
|                                         .. | ||||
|                                     }, | ||||
|                                     big_slots: StatePartLayout<BigSlots> { | ||||
|                                         len: 1, | ||||
|                                         debug_data: [ | ||||
|                                             SlotDebugData { | ||||
|                                                 name: "", | ||||
|                                                 ty: AsyncReset, | ||||
|                                             }, | ||||
|                                         ], | ||||
|                                         .. | ||||
|                                     }, | ||||
|                                 }, | ||||
|                                 body: Scalar, | ||||
|                             }, | ||||
|                         }, | ||||
|                     ], | ||||
|                 }, | ||||
|             }, | ||||
|             range: TypeIndexRange { | ||||
|                 small_slots: StatePartIndexRange<SmallSlots> { start: 0, len: 0 }, | ||||
|                 big_slots: StatePartIndexRange<BigSlots> { start: 0, len: 2 }, | ||||
|             }, | ||||
|             write: None, | ||||
|         }, | ||||
|             }.cd, | ||||
|             Instance { | ||||
|                 name: <simulator>::counter, | ||||
|                 instantiated: Module { | ||||
|                     name: counter, | ||||
|                     .. | ||||
|                 }, | ||||
|         }.cd.clk: CompiledValue { | ||||
|             layout: CompiledTypeLayout { | ||||
|                 ty: Clock, | ||||
|                 layout: TypeLayout { | ||||
|                     small_slots: StatePartLayout<SmallSlots> { | ||||
|                         len: 0, | ||||
|                         debug_data: [], | ||||
|                         .. | ||||
|                     }, | ||||
|                     big_slots: StatePartLayout<BigSlots> { | ||||
|                         len: 1, | ||||
|                         debug_data: [ | ||||
|                             SlotDebugData { | ||||
|                                 name: "", | ||||
|                                 ty: Clock, | ||||
|                             }, | ||||
|                         ], | ||||
|                         .. | ||||
|                     }, | ||||
|                 }, | ||||
|                 body: Scalar, | ||||
|             }, | ||||
|             range: TypeIndexRange { | ||||
|                 small_slots: StatePartIndexRange<SmallSlots> { start: 0, len: 0 }, | ||||
|                 big_slots: StatePartIndexRange<BigSlots> { start: 0, len: 1 }, | ||||
|             }, | ||||
|             write: None, | ||||
|         }, | ||||
|             }.cd.clk, | ||||
|             Instance { | ||||
|                 name: <simulator>::counter, | ||||
|                 instantiated: Module { | ||||
|                     name: counter, | ||||
|                     .. | ||||
|                 }, | ||||
|         }.cd.rst: CompiledValue { | ||||
|             layout: CompiledTypeLayout { | ||||
|                 ty: AsyncReset, | ||||
|                 layout: TypeLayout { | ||||
|                     small_slots: StatePartLayout<SmallSlots> { | ||||
|                         len: 0, | ||||
|                         debug_data: [], | ||||
|                         .. | ||||
|                     }, | ||||
|                     big_slots: StatePartLayout<BigSlots> { | ||||
|                         len: 1, | ||||
|                         debug_data: [ | ||||
|                             SlotDebugData { | ||||
|                                 name: "", | ||||
|                                 ty: AsyncReset, | ||||
|                             }, | ||||
|                         ], | ||||
|                         .. | ||||
|                     }, | ||||
|                 }, | ||||
|                 body: Scalar, | ||||
|             }, | ||||
|             range: TypeIndexRange { | ||||
|                 small_slots: StatePartIndexRange<SmallSlots> { start: 0, len: 0 }, | ||||
|                 big_slots: StatePartIndexRange<BigSlots> { start: 1, len: 1 }, | ||||
|             }, | ||||
|             write: None, | ||||
|         }, | ||||
|             }.cd.rst, | ||||
|             Instance { | ||||
|                 name: <simulator>::counter, | ||||
|                 instantiated: Module { | ||||
|                     name: counter, | ||||
|                     .. | ||||
|                 }, | ||||
|         }.count: CompiledValue { | ||||
|             layout: CompiledTypeLayout { | ||||
|                 ty: UInt<4>, | ||||
|                 layout: TypeLayout { | ||||
|                     small_slots: StatePartLayout<SmallSlots> { | ||||
|                         len: 0, | ||||
|                         debug_data: [], | ||||
|                         .. | ||||
|             }.count, | ||||
|         }, | ||||
|                     big_slots: StatePartLayout<BigSlots> { | ||||
|                         len: 1, | ||||
|                         debug_data: [ | ||||
|                             SlotDebugData { | ||||
|                                 name: "InstantiatedModule(counter: counter).counter::count", | ||||
|                                 ty: UInt<4>, | ||||
|         did_initial_settle: true, | ||||
|     }, | ||||
|                         ], | ||||
|                         .. | ||||
|                     }, | ||||
|                 }, | ||||
|                 body: Scalar, | ||||
|             }, | ||||
|             range: TypeIndexRange { | ||||
|                 small_slots: StatePartIndexRange<SmallSlots> { start: 0, len: 0 }, | ||||
|                 big_slots: StatePartIndexRange<BigSlots> { start: 2, len: 1 }, | ||||
|             }, | ||||
|             write: None, | ||||
|         }, | ||||
|     }, | ||||
|     made_initial_step: true, | ||||
|     needs_settle: false, | ||||
|     extern_modules: [], | ||||
|     state_ready_to_run: false, | ||||
|     trace_decls: TraceModule { | ||||
|         name: "counter", | ||||
|         children: [ | ||||
|  |  | |||
|  | @ -184,7 +184,24 @@ Simulation { | |||
|             .. | ||||
|         }, | ||||
|     }, | ||||
|     uninitialized_inputs: {}, | ||||
|     main_module: SimulationModuleState { | ||||
|         base_targets: [ | ||||
|             Instance { | ||||
|                 name: <simulator>::counter, | ||||
|                 instantiated: Module { | ||||
|                     name: counter, | ||||
|                     .. | ||||
|                 }, | ||||
|             }.cd, | ||||
|             Instance { | ||||
|                 name: <simulator>::counter, | ||||
|                 instantiated: Module { | ||||
|                     name: counter, | ||||
|                     .. | ||||
|                 }, | ||||
|             }.count, | ||||
|         ], | ||||
|         uninitialized_ios: {}, | ||||
|         io_targets: { | ||||
|             Instance { | ||||
|                 name: <simulator>::counter, | ||||
|  | @ -192,205 +209,33 @@ Simulation { | |||
|                     name: counter, | ||||
|                     .. | ||||
|                 }, | ||||
|         }.cd: CompiledValue { | ||||
|             layout: CompiledTypeLayout { | ||||
|                 ty: Bundle { | ||||
|                     /* offset = 0 */ | ||||
|                     clk: Clock, | ||||
|                     /* offset = 1 */ | ||||
|                     rst: SyncReset, | ||||
|                 }, | ||||
|                 layout: TypeLayout { | ||||
|                     small_slots: StatePartLayout<SmallSlots> { | ||||
|                         len: 0, | ||||
|                         debug_data: [], | ||||
|                         .. | ||||
|                     }, | ||||
|                     big_slots: StatePartLayout<BigSlots> { | ||||
|                         len: 2, | ||||
|                         debug_data: [ | ||||
|                             SlotDebugData { | ||||
|                                 name: "InstantiatedModule(counter: counter).counter::cd.clk", | ||||
|                                 ty: Clock, | ||||
|                             }, | ||||
|                             SlotDebugData { | ||||
|                                 name: "InstantiatedModule(counter: counter).counter::cd.rst", | ||||
|                                 ty: SyncReset, | ||||
|                             }, | ||||
|                         ], | ||||
|                         .. | ||||
|                     }, | ||||
|                 }, | ||||
|                 body: Bundle { | ||||
|                     fields: [ | ||||
|                         CompiledBundleField { | ||||
|                             offset: TypeIndex { | ||||
|                                 small_slots: StatePartIndex<SmallSlots>(0), | ||||
|                                 big_slots: StatePartIndex<BigSlots>(0), | ||||
|                             }, | ||||
|                             ty: CompiledTypeLayout { | ||||
|                                 ty: Clock, | ||||
|                                 layout: TypeLayout { | ||||
|                                     small_slots: StatePartLayout<SmallSlots> { | ||||
|                                         len: 0, | ||||
|                                         debug_data: [], | ||||
|                                         .. | ||||
|                                     }, | ||||
|                                     big_slots: StatePartLayout<BigSlots> { | ||||
|                                         len: 1, | ||||
|                                         debug_data: [ | ||||
|                                             SlotDebugData { | ||||
|                                                 name: "", | ||||
|                                                 ty: Clock, | ||||
|                                             }, | ||||
|                                         ], | ||||
|                                         .. | ||||
|                                     }, | ||||
|                                 }, | ||||
|                                 body: Scalar, | ||||
|                             }, | ||||
|                         }, | ||||
|                         CompiledBundleField { | ||||
|                             offset: TypeIndex { | ||||
|                                 small_slots: StatePartIndex<SmallSlots>(0), | ||||
|                                 big_slots: StatePartIndex<BigSlots>(1), | ||||
|                             }, | ||||
|                             ty: CompiledTypeLayout { | ||||
|                                 ty: SyncReset, | ||||
|                                 layout: TypeLayout { | ||||
|                                     small_slots: StatePartLayout<SmallSlots> { | ||||
|                                         len: 0, | ||||
|                                         debug_data: [], | ||||
|                                         .. | ||||
|                                     }, | ||||
|                                     big_slots: StatePartLayout<BigSlots> { | ||||
|                                         len: 1, | ||||
|                                         debug_data: [ | ||||
|                                             SlotDebugData { | ||||
|                                                 name: "", | ||||
|                                                 ty: SyncReset, | ||||
|                                             }, | ||||
|                                         ], | ||||
|                                         .. | ||||
|                                     }, | ||||
|                                 }, | ||||
|                                 body: Scalar, | ||||
|                             }, | ||||
|                         }, | ||||
|                     ], | ||||
|                 }, | ||||
|             }, | ||||
|             range: TypeIndexRange { | ||||
|                 small_slots: StatePartIndexRange<SmallSlots> { start: 0, len: 0 }, | ||||
|                 big_slots: StatePartIndexRange<BigSlots> { start: 0, len: 2 }, | ||||
|             }, | ||||
|             write: None, | ||||
|         }, | ||||
|             }.cd, | ||||
|             Instance { | ||||
|                 name: <simulator>::counter, | ||||
|                 instantiated: Module { | ||||
|                     name: counter, | ||||
|                     .. | ||||
|                 }, | ||||
|         }.cd.clk: CompiledValue { | ||||
|             layout: CompiledTypeLayout { | ||||
|                 ty: Clock, | ||||
|                 layout: TypeLayout { | ||||
|                     small_slots: StatePartLayout<SmallSlots> { | ||||
|                         len: 0, | ||||
|                         debug_data: [], | ||||
|                         .. | ||||
|                     }, | ||||
|                     big_slots: StatePartLayout<BigSlots> { | ||||
|                         len: 1, | ||||
|                         debug_data: [ | ||||
|                             SlotDebugData { | ||||
|                                 name: "", | ||||
|                                 ty: Clock, | ||||
|                             }, | ||||
|                         ], | ||||
|                         .. | ||||
|                     }, | ||||
|                 }, | ||||
|                 body: Scalar, | ||||
|             }, | ||||
|             range: TypeIndexRange { | ||||
|                 small_slots: StatePartIndexRange<SmallSlots> { start: 0, len: 0 }, | ||||
|                 big_slots: StatePartIndexRange<BigSlots> { start: 0, len: 1 }, | ||||
|             }, | ||||
|             write: None, | ||||
|         }, | ||||
|             }.cd.clk, | ||||
|             Instance { | ||||
|                 name: <simulator>::counter, | ||||
|                 instantiated: Module { | ||||
|                     name: counter, | ||||
|                     .. | ||||
|                 }, | ||||
|         }.cd.rst: CompiledValue { | ||||
|             layout: CompiledTypeLayout { | ||||
|                 ty: SyncReset, | ||||
|                 layout: TypeLayout { | ||||
|                     small_slots: StatePartLayout<SmallSlots> { | ||||
|                         len: 0, | ||||
|                         debug_data: [], | ||||
|                         .. | ||||
|                     }, | ||||
|                     big_slots: StatePartLayout<BigSlots> { | ||||
|                         len: 1, | ||||
|                         debug_data: [ | ||||
|                             SlotDebugData { | ||||
|                                 name: "", | ||||
|                                 ty: SyncReset, | ||||
|                             }, | ||||
|                         ], | ||||
|                         .. | ||||
|                     }, | ||||
|                 }, | ||||
|                 body: Scalar, | ||||
|             }, | ||||
|             range: TypeIndexRange { | ||||
|                 small_slots: StatePartIndexRange<SmallSlots> { start: 0, len: 0 }, | ||||
|                 big_slots: StatePartIndexRange<BigSlots> { start: 1, len: 1 }, | ||||
|             }, | ||||
|             write: None, | ||||
|         }, | ||||
|             }.cd.rst, | ||||
|             Instance { | ||||
|                 name: <simulator>::counter, | ||||
|                 instantiated: Module { | ||||
|                     name: counter, | ||||
|                     .. | ||||
|                 }, | ||||
|         }.count: CompiledValue { | ||||
|             layout: CompiledTypeLayout { | ||||
|                 ty: UInt<4>, | ||||
|                 layout: TypeLayout { | ||||
|                     small_slots: StatePartLayout<SmallSlots> { | ||||
|                         len: 0, | ||||
|                         debug_data: [], | ||||
|                         .. | ||||
|             }.count, | ||||
|         }, | ||||
|                     big_slots: StatePartLayout<BigSlots> { | ||||
|                         len: 1, | ||||
|                         debug_data: [ | ||||
|                             SlotDebugData { | ||||
|                                 name: "InstantiatedModule(counter: counter).counter::count", | ||||
|                                 ty: UInt<4>, | ||||
|         did_initial_settle: true, | ||||
|     }, | ||||
|                         ], | ||||
|                         .. | ||||
|                     }, | ||||
|                 }, | ||||
|                 body: Scalar, | ||||
|             }, | ||||
|             range: TypeIndexRange { | ||||
|                 small_slots: StatePartIndexRange<SmallSlots> { start: 0, len: 0 }, | ||||
|                 big_slots: StatePartIndexRange<BigSlots> { start: 2, len: 1 }, | ||||
|             }, | ||||
|             write: None, | ||||
|         }, | ||||
|     }, | ||||
|     made_initial_step: true, | ||||
|     needs_settle: false, | ||||
|     extern_modules: [], | ||||
|     state_ready_to_run: false, | ||||
|     trace_decls: TraceModule { | ||||
|         name: "counter", | ||||
|         children: [ | ||||
|  |  | |||
|  | @ -88,10 +88,14 @@ Simulation { | |||
|             .. | ||||
|         }, | ||||
|     }, | ||||
|     uninitialized_inputs: {}, | ||||
|     main_module: SimulationModuleState { | ||||
|         base_targets: [], | ||||
|         uninitialized_ios: {}, | ||||
|         io_targets: {}, | ||||
|     made_initial_step: true, | ||||
|     needs_settle: false, | ||||
|         did_initial_settle: true, | ||||
|     }, | ||||
|     extern_modules: [], | ||||
|     state_ready_to_run: false, | ||||
|     trace_decls: TraceModule { | ||||
|         name: "duplicate_names", | ||||
|         children: [ | ||||
|  |  | |||
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							|  | @ -16,18 +16,25 @@ $var wire 1 ) \0 $end | |||
| $var wire 1 * \1 $end | ||||
| $upscope $end | ||||
| $upscope $end | ||||
| $scope struct the_reg $end | ||||
| $scope struct b2_out $end | ||||
| $var string 1 + \$tag $end | ||||
| $scope struct HdlSome $end | ||||
| $var wire 1 , \0 $end | ||||
| $var wire 1 - \1 $end | ||||
| $upscope $end | ||||
| $upscope $end | ||||
| $scope struct the_reg $end | ||||
| $var string 1 . \$tag $end | ||||
| $scope struct B $end | ||||
| $var reg 1 , \0 $end | ||||
| $var reg 1 - \1 $end | ||||
| $var reg 1 / \0 $end | ||||
| $var reg 1 0 \1 $end | ||||
| $upscope $end | ||||
| $scope struct C $end | ||||
| $scope struct a $end | ||||
| $var reg 1 . \[0] $end | ||||
| $var reg 1 / \[1] $end | ||||
| $var reg 1 1 \[0] $end | ||||
| $var reg 1 2 \[1] $end | ||||
| $upscope $end | ||||
| $var reg 2 0 b $end | ||||
| $var reg 2 3 b $end | ||||
| $upscope $end | ||||
| $upscope $end | ||||
| $upscope $end | ||||
|  | @ -43,12 +50,15 @@ b0 ' | |||
| sHdlNone\x20(0) ( | ||||
| 0) | ||||
| 0* | ||||
| sA\x20(0) + | ||||
| sHdlNone\x20(0) + | ||||
| 0, | ||||
| 0- | ||||
| 0. | ||||
| sA\x20(0) . | ||||
| 0/ | ||||
| b0 0 | ||||
| 00 | ||||
| 01 | ||||
| 02 | ||||
| b0 3 | ||||
| $end | ||||
| #1000000 | ||||
| 1! | ||||
|  | @ -66,7 +76,8 @@ b1 $ | |||
| 1! | ||||
| b1 & | ||||
| sHdlSome\x20(1) ( | ||||
| sB\x20(1) + | ||||
| sHdlSome\x20(1) + | ||||
| sB\x20(1) . | ||||
| #6000000 | ||||
| 0# | ||||
| b0 $ | ||||
|  | @ -85,8 +96,10 @@ b11 ' | |||
| 1* | ||||
| 1, | ||||
| 1- | ||||
| 1. | ||||
| 1/ | ||||
| 10 | ||||
| 11 | ||||
| 12 | ||||
| #10000000 | ||||
| 0! | ||||
| #11000000 | ||||
|  | @ -101,8 +114,11 @@ b1111 ' | |||
| sHdlNone\x20(0) ( | ||||
| 0) | ||||
| 0* | ||||
| sC\x20(2) + | ||||
| b11 0 | ||||
| sHdlNone\x20(0) + | ||||
| 0, | ||||
| 0- | ||||
| sC\x20(2) . | ||||
| b11 3 | ||||
| #14000000 | ||||
| 0! | ||||
| #15000000 | ||||
|  |  | |||
							
								
								
									
										220
									
								
								crates/fayalite/tests/sim/expected/extern_module.txt
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										220
									
								
								crates/fayalite/tests/sim/expected/extern_module.txt
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,220 @@ | |||
| Simulation { | ||||
|     state: State { | ||||
|         insns: Insns { | ||||
|             state_layout: StateLayout { | ||||
|                 ty: TypeLayout { | ||||
|                     small_slots: StatePartLayout<SmallSlots> { | ||||
|                         len: 0, | ||||
|                         debug_data: [], | ||||
|                         .. | ||||
|                     }, | ||||
|                     big_slots: StatePartLayout<BigSlots> { | ||||
|                         len: 2, | ||||
|                         debug_data: [ | ||||
|                             SlotDebugData { | ||||
|                                 name: "InstantiatedModule(extern_module: extern_module).extern_module::i", | ||||
|                                 ty: Bool, | ||||
|                             }, | ||||
|                             SlotDebugData { | ||||
|                                 name: "InstantiatedModule(extern_module: extern_module).extern_module::o", | ||||
|                                 ty: Bool, | ||||
|                             }, | ||||
|                         ], | ||||
|                         .. | ||||
|                     }, | ||||
|                 }, | ||||
|                 memories: StatePartLayout<Memories> { | ||||
|                     len: 0, | ||||
|                     debug_data: [], | ||||
|                     layout_data: [], | ||||
|                     .. | ||||
|                 }, | ||||
|             }, | ||||
|             insns: [ | ||||
|                 // at: module-XXXXXXXXXX.rs:1:1 | ||||
|                 0: Return, | ||||
|             ], | ||||
|             .. | ||||
|         }, | ||||
|         pc: 0, | ||||
|         memory_write_log: [], | ||||
|         memories: StatePart { | ||||
|             value: [], | ||||
|         }, | ||||
|         small_slots: StatePart { | ||||
|             value: [], | ||||
|         }, | ||||
|         big_slots: StatePart { | ||||
|             value: [ | ||||
|                 1, | ||||
|                 1, | ||||
|             ], | ||||
|         }, | ||||
|     }, | ||||
|     io: Instance { | ||||
|         name: <simulator>::extern_module, | ||||
|         instantiated: Module { | ||||
|             name: extern_module, | ||||
|             .. | ||||
|         }, | ||||
|     }, | ||||
|     main_module: SimulationModuleState { | ||||
|         base_targets: [ | ||||
|             Instance { | ||||
|                 name: <simulator>::extern_module, | ||||
|                 instantiated: Module { | ||||
|                     name: extern_module, | ||||
|                     .. | ||||
|                 }, | ||||
|             }.i, | ||||
|             Instance { | ||||
|                 name: <simulator>::extern_module, | ||||
|                 instantiated: Module { | ||||
|                     name: extern_module, | ||||
|                     .. | ||||
|                 }, | ||||
|             }.o, | ||||
|         ], | ||||
|         uninitialized_ios: {}, | ||||
|         io_targets: { | ||||
|             Instance { | ||||
|                 name: <simulator>::extern_module, | ||||
|                 instantiated: Module { | ||||
|                     name: extern_module, | ||||
|                     .. | ||||
|                 }, | ||||
|             }.i, | ||||
|             Instance { | ||||
|                 name: <simulator>::extern_module, | ||||
|                 instantiated: Module { | ||||
|                     name: extern_module, | ||||
|                     .. | ||||
|                 }, | ||||
|             }.o, | ||||
|         }, | ||||
|         did_initial_settle: true, | ||||
|     }, | ||||
|     extern_modules: [ | ||||
|         SimulationExternModuleState { | ||||
|             module_state: SimulationModuleState { | ||||
|                 base_targets: [ | ||||
|                     ModuleIO { | ||||
|                         name: extern_module::i, | ||||
|                         is_input: true, | ||||
|                         ty: Bool, | ||||
|                         .. | ||||
|                     }, | ||||
|                     ModuleIO { | ||||
|                         name: extern_module::o, | ||||
|                         is_input: false, | ||||
|                         ty: Bool, | ||||
|                         .. | ||||
|                     }, | ||||
|                 ], | ||||
|                 uninitialized_ios: {}, | ||||
|                 io_targets: { | ||||
|                     ModuleIO { | ||||
|                         name: extern_module::i, | ||||
|                         is_input: true, | ||||
|                         ty: Bool, | ||||
|                         .. | ||||
|                     }, | ||||
|                     ModuleIO { | ||||
|                         name: extern_module::o, | ||||
|                         is_input: false, | ||||
|                         ty: Bool, | ||||
|                         .. | ||||
|                     }, | ||||
|                 }, | ||||
|                 did_initial_settle: true, | ||||
|             }, | ||||
|             sim: ExternModuleSimulation { | ||||
|                 generator: SimGeneratorFn { | ||||
|                     args: ( | ||||
|                         ModuleIO { | ||||
|                             name: extern_module::i, | ||||
|                             is_input: true, | ||||
|                             ty: Bool, | ||||
|                             .. | ||||
|                         }, | ||||
|                         ModuleIO { | ||||
|                             name: extern_module::o, | ||||
|                             is_input: false, | ||||
|                             ty: Bool, | ||||
|                             .. | ||||
|                         }, | ||||
|                     ), | ||||
|                     f: ..., | ||||
|                 }, | ||||
|                 source_location: SourceLocation( | ||||
|                     module-XXXXXXXXXX.rs:4:1, | ||||
|                 ), | ||||
|             }, | ||||
|             running_generator: Some( | ||||
|                 ..., | ||||
|             ), | ||||
|             wait_targets: { | ||||
|                 Instant( | ||||
|                     20.500000000000 μs, | ||||
|                 ), | ||||
|             }, | ||||
|         }, | ||||
|     ], | ||||
|     state_ready_to_run: false, | ||||
|     trace_decls: TraceModule { | ||||
|         name: "extern_module", | ||||
|         children: [ | ||||
|             TraceModuleIO { | ||||
|                 name: "i", | ||||
|                 child: TraceBool { | ||||
|                     location: TraceScalarId(0), | ||||
|                     name: "i", | ||||
|                     flow: Source, | ||||
|                 }, | ||||
|                 ty: Bool, | ||||
|                 flow: Source, | ||||
|             }, | ||||
|             TraceModuleIO { | ||||
|                 name: "o", | ||||
|                 child: TraceBool { | ||||
|                     location: TraceScalarId(1), | ||||
|                     name: "o", | ||||
|                     flow: Sink, | ||||
|                 }, | ||||
|                 ty: Bool, | ||||
|                 flow: Sink, | ||||
|             }, | ||||
|         ], | ||||
|     }, | ||||
|     traces: [ | ||||
|         SimTrace { | ||||
|             id: TraceScalarId(0), | ||||
|             kind: BigBool { | ||||
|                 index: StatePartIndex<BigSlots>(0), | ||||
|             }, | ||||
|             state: 0x1, | ||||
|             last_state: 0x1, | ||||
|         }, | ||||
|         SimTrace { | ||||
|             id: TraceScalarId(1), | ||||
|             kind: BigBool { | ||||
|                 index: StatePartIndex<BigSlots>(1), | ||||
|             }, | ||||
|             state: 0x1, | ||||
|             last_state: 0x1, | ||||
|         }, | ||||
|     ], | ||||
|     trace_memories: {}, | ||||
|     trace_writers: [ | ||||
|         Running( | ||||
|             VcdWriter { | ||||
|                 finished_init: true, | ||||
|                 timescale: 1 ps, | ||||
|                 .. | ||||
|             }, | ||||
|         ), | ||||
|     ], | ||||
|     instant: 20 μs, | ||||
|     clocks_triggered: [], | ||||
|     .. | ||||
| } | ||||
							
								
								
									
										51
									
								
								crates/fayalite/tests/sim/expected/extern_module.vcd
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								crates/fayalite/tests/sim/expected/extern_module.vcd
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,51 @@ | |||
| $timescale 1 ps $end | ||||
| $scope module extern_module $end | ||||
| $var wire 1 ! i $end | ||||
| $var wire 1 " o $end | ||||
| $upscope $end | ||||
| $enddefinitions $end | ||||
| $dumpvars | ||||
| 0! | ||||
| 1" | ||||
| $end | ||||
| #500000 | ||||
| #1500000 | ||||
| 0" | ||||
| #2500000 | ||||
| 1" | ||||
| #3500000 | ||||
| 0" | ||||
| #4500000 | ||||
| 1" | ||||
| #5500000 | ||||
| 0" | ||||
| #6500000 | ||||
| 1" | ||||
| #7500000 | ||||
| 0" | ||||
| #8500000 | ||||
| 1" | ||||
| #9500000 | ||||
| 0" | ||||
| #10000000 | ||||
| 1! | ||||
| #10500000 | ||||
| #11500000 | ||||
| 1" | ||||
| #12500000 | ||||
| 0" | ||||
| #13500000 | ||||
| 1" | ||||
| #14500000 | ||||
| 0" | ||||
| #15500000 | ||||
| 1" | ||||
| #16500000 | ||||
| 0" | ||||
| #17500000 | ||||
| 1" | ||||
| #18500000 | ||||
| 0" | ||||
| #19500000 | ||||
| 1" | ||||
| #20000000 | ||||
							
								
								
									
										310
									
								
								crates/fayalite/tests/sim/expected/extern_module2.txt
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										310
									
								
								crates/fayalite/tests/sim/expected/extern_module2.txt
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,310 @@ | |||
| Simulation { | ||||
|     state: State { | ||||
|         insns: Insns { | ||||
|             state_layout: StateLayout { | ||||
|                 ty: TypeLayout { | ||||
|                     small_slots: StatePartLayout<SmallSlots> { | ||||
|                         len: 0, | ||||
|                         debug_data: [], | ||||
|                         .. | ||||
|                     }, | ||||
|                     big_slots: StatePartLayout<BigSlots> { | ||||
|                         len: 3, | ||||
|                         debug_data: [ | ||||
|                             SlotDebugData { | ||||
|                                 name: "InstantiatedModule(extern_module2: extern_module2).extern_module2::en", | ||||
|                                 ty: Bool, | ||||
|                             }, | ||||
|                             SlotDebugData { | ||||
|                                 name: "InstantiatedModule(extern_module2: extern_module2).extern_module2::clk", | ||||
|                                 ty: Clock, | ||||
|                             }, | ||||
|                             SlotDebugData { | ||||
|                                 name: "InstantiatedModule(extern_module2: extern_module2).extern_module2::o", | ||||
|                                 ty: UInt<8>, | ||||
|                             }, | ||||
|                         ], | ||||
|                         .. | ||||
|                     }, | ||||
|                 }, | ||||
|                 memories: StatePartLayout<Memories> { | ||||
|                     len: 0, | ||||
|                     debug_data: [], | ||||
|                     layout_data: [], | ||||
|                     .. | ||||
|                 }, | ||||
|             }, | ||||
|             insns: [ | ||||
|                 // at: module-XXXXXXXXXX.rs:1:1 | ||||
|                 0: Return, | ||||
|             ], | ||||
|             .. | ||||
|         }, | ||||
|         pc: 0, | ||||
|         memory_write_log: [], | ||||
|         memories: StatePart { | ||||
|             value: [], | ||||
|         }, | ||||
|         small_slots: StatePart { | ||||
|             value: [], | ||||
|         }, | ||||
|         big_slots: StatePart { | ||||
|             value: [ | ||||
|                 0, | ||||
|                 1, | ||||
|                 101, | ||||
|             ], | ||||
|         }, | ||||
|     }, | ||||
|     io: Instance { | ||||
|         name: <simulator>::extern_module2, | ||||
|         instantiated: Module { | ||||
|             name: extern_module2, | ||||
|             .. | ||||
|         }, | ||||
|     }, | ||||
|     main_module: SimulationModuleState { | ||||
|         base_targets: [ | ||||
|             Instance { | ||||
|                 name: <simulator>::extern_module2, | ||||
|                 instantiated: Module { | ||||
|                     name: extern_module2, | ||||
|                     .. | ||||
|                 }, | ||||
|             }.en, | ||||
|             Instance { | ||||
|                 name: <simulator>::extern_module2, | ||||
|                 instantiated: Module { | ||||
|                     name: extern_module2, | ||||
|                     .. | ||||
|                 }, | ||||
|             }.clk, | ||||
|             Instance { | ||||
|                 name: <simulator>::extern_module2, | ||||
|                 instantiated: Module { | ||||
|                     name: extern_module2, | ||||
|                     .. | ||||
|                 }, | ||||
|             }.o, | ||||
|         ], | ||||
|         uninitialized_ios: {}, | ||||
|         io_targets: { | ||||
|             Instance { | ||||
|                 name: <simulator>::extern_module2, | ||||
|                 instantiated: Module { | ||||
|                     name: extern_module2, | ||||
|                     .. | ||||
|                 }, | ||||
|             }.clk, | ||||
|             Instance { | ||||
|                 name: <simulator>::extern_module2, | ||||
|                 instantiated: Module { | ||||
|                     name: extern_module2, | ||||
|                     .. | ||||
|                 }, | ||||
|             }.en, | ||||
|             Instance { | ||||
|                 name: <simulator>::extern_module2, | ||||
|                 instantiated: Module { | ||||
|                     name: extern_module2, | ||||
|                     .. | ||||
|                 }, | ||||
|             }.o, | ||||
|         }, | ||||
|         did_initial_settle: true, | ||||
|     }, | ||||
|     extern_modules: [ | ||||
|         SimulationExternModuleState { | ||||
|             module_state: SimulationModuleState { | ||||
|                 base_targets: [ | ||||
|                     ModuleIO { | ||||
|                         name: extern_module2::en, | ||||
|                         is_input: true, | ||||
|                         ty: Bool, | ||||
|                         .. | ||||
|                     }, | ||||
|                     ModuleIO { | ||||
|                         name: extern_module2::clk, | ||||
|                         is_input: true, | ||||
|                         ty: Clock, | ||||
|                         .. | ||||
|                     }, | ||||
|                     ModuleIO { | ||||
|                         name: extern_module2::o, | ||||
|                         is_input: false, | ||||
|                         ty: UInt<8>, | ||||
|                         .. | ||||
|                     }, | ||||
|                 ], | ||||
|                 uninitialized_ios: {}, | ||||
|                 io_targets: { | ||||
|                     ModuleIO { | ||||
|                         name: extern_module2::clk, | ||||
|                         is_input: true, | ||||
|                         ty: Clock, | ||||
|                         .. | ||||
|                     }, | ||||
|                     ModuleIO { | ||||
|                         name: extern_module2::en, | ||||
|                         is_input: true, | ||||
|                         ty: Bool, | ||||
|                         .. | ||||
|                     }, | ||||
|                     ModuleIO { | ||||
|                         name: extern_module2::o, | ||||
|                         is_input: false, | ||||
|                         ty: UInt<8>, | ||||
|                         .. | ||||
|                     }, | ||||
|                 }, | ||||
|                 did_initial_settle: true, | ||||
|             }, | ||||
|             sim: ExternModuleSimulation { | ||||
|                 generator: SimGeneratorFn { | ||||
|                     args: ( | ||||
|                         ModuleIO { | ||||
|                             name: extern_module2::en, | ||||
|                             is_input: true, | ||||
|                             ty: Bool, | ||||
|                             .. | ||||
|                         }, | ||||
|                         ModuleIO { | ||||
|                             name: extern_module2::clk, | ||||
|                             is_input: true, | ||||
|                             ty: Clock, | ||||
|                             .. | ||||
|                         }, | ||||
|                         ModuleIO { | ||||
|                             name: extern_module2::o, | ||||
|                             is_input: false, | ||||
|                             ty: UInt<8>, | ||||
|                             .. | ||||
|                         }, | ||||
|                     ), | ||||
|                     f: ..., | ||||
|                 }, | ||||
|                 source_location: SourceLocation( | ||||
|                     module-XXXXXXXXXX.rs:5:1, | ||||
|                 ), | ||||
|             }, | ||||
|             running_generator: Some( | ||||
|                 ..., | ||||
|             ), | ||||
|             wait_targets: { | ||||
|                 Change { | ||||
|                     key: CompiledValue { | ||||
|                         layout: CompiledTypeLayout { | ||||
|                             ty: Clock, | ||||
|                             layout: TypeLayout { | ||||
|                                 small_slots: StatePartLayout<SmallSlots> { | ||||
|                                     len: 0, | ||||
|                                     debug_data: [], | ||||
|                                     .. | ||||
|                                 }, | ||||
|                                 big_slots: StatePartLayout<BigSlots> { | ||||
|                                     len: 1, | ||||
|                                     debug_data: [ | ||||
|                                         SlotDebugData { | ||||
|                                             name: "InstantiatedModule(extern_module2: extern_module2).extern_module2::clk", | ||||
|                                             ty: Clock, | ||||
|                                         }, | ||||
|                                     ], | ||||
|                                     .. | ||||
|                                 }, | ||||
|                             }, | ||||
|                             body: Scalar, | ||||
|                         }, | ||||
|                         range: TypeIndexRange { | ||||
|                             small_slots: StatePartIndexRange<SmallSlots> { start: 0, len: 0 }, | ||||
|                             big_slots: StatePartIndexRange<BigSlots> { start: 1, len: 1 }, | ||||
|                         }, | ||||
|                         write: None, | ||||
|                     }, | ||||
|                     value: SimValue { | ||||
|                         ty: Clock, | ||||
|                         value: OpaqueSimValue { | ||||
|                             bits: 0x1_u1, | ||||
|                         }, | ||||
|                     }, | ||||
|                 }, | ||||
|             }, | ||||
|         }, | ||||
|     ], | ||||
|     state_ready_to_run: false, | ||||
|     trace_decls: TraceModule { | ||||
|         name: "extern_module2", | ||||
|         children: [ | ||||
|             TraceModuleIO { | ||||
|                 name: "en", | ||||
|                 child: TraceBool { | ||||
|                     location: TraceScalarId(0), | ||||
|                     name: "en", | ||||
|                     flow: Source, | ||||
|                 }, | ||||
|                 ty: Bool, | ||||
|                 flow: Source, | ||||
|             }, | ||||
|             TraceModuleIO { | ||||
|                 name: "clk", | ||||
|                 child: TraceClock { | ||||
|                     location: TraceScalarId(1), | ||||
|                     name: "clk", | ||||
|                     flow: Source, | ||||
|                 }, | ||||
|                 ty: Clock, | ||||
|                 flow: Source, | ||||
|             }, | ||||
|             TraceModuleIO { | ||||
|                 name: "o", | ||||
|                 child: TraceUInt { | ||||
|                     location: TraceScalarId(2), | ||||
|                     name: "o", | ||||
|                     ty: UInt<8>, | ||||
|                     flow: Sink, | ||||
|                 }, | ||||
|                 ty: UInt<8>, | ||||
|                 flow: Sink, | ||||
|             }, | ||||
|         ], | ||||
|     }, | ||||
|     traces: [ | ||||
|         SimTrace { | ||||
|             id: TraceScalarId(0), | ||||
|             kind: BigBool { | ||||
|                 index: StatePartIndex<BigSlots>(0), | ||||
|             }, | ||||
|             state: 0x0, | ||||
|             last_state: 0x0, | ||||
|         }, | ||||
|         SimTrace { | ||||
|             id: TraceScalarId(1), | ||||
|             kind: BigClock { | ||||
|                 index: StatePartIndex<BigSlots>(1), | ||||
|             }, | ||||
|             state: 0x1, | ||||
|             last_state: 0x1, | ||||
|         }, | ||||
|         SimTrace { | ||||
|             id: TraceScalarId(2), | ||||
|             kind: BigUInt { | ||||
|                 index: StatePartIndex<BigSlots>(2), | ||||
|                 ty: UInt<8>, | ||||
|             }, | ||||
|             state: 0x65, | ||||
|             last_state: 0x65, | ||||
|         }, | ||||
|     ], | ||||
|     trace_memories: {}, | ||||
|     trace_writers: [ | ||||
|         Running( | ||||
|             VcdWriter { | ||||
|                 finished_init: true, | ||||
|                 timescale: 1 ps, | ||||
|                 .. | ||||
|             }, | ||||
|         ), | ||||
|     ], | ||||
|     instant: 60 μs, | ||||
|     clocks_triggered: [], | ||||
|     .. | ||||
| } | ||||
							
								
								
									
										150
									
								
								crates/fayalite/tests/sim/expected/extern_module2.vcd
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										150
									
								
								crates/fayalite/tests/sim/expected/extern_module2.vcd
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,150 @@ | |||
| $timescale 1 ps $end | ||||
| $scope module extern_module2 $end | ||||
| $var wire 1 ! en $end | ||||
| $var wire 1 " clk $end | ||||
| $var wire 8 # o $end | ||||
| $upscope $end | ||||
| $enddefinitions $end | ||||
| $dumpvars | ||||
| 1! | ||||
| 0" | ||||
| b1001000 # | ||||
| $end | ||||
| #1000000 | ||||
| 1" | ||||
| b1100101 # | ||||
| #2000000 | ||||
| 0" | ||||
| #3000000 | ||||
| 1" | ||||
| b1101100 # | ||||
| #4000000 | ||||
| 0" | ||||
| #5000000 | ||||
| 1" | ||||
| #6000000 | ||||
| 0" | ||||
| #7000000 | ||||
| 1" | ||||
| b1101111 # | ||||
| #8000000 | ||||
| 0" | ||||
| #9000000 | ||||
| 1" | ||||
| b101100 # | ||||
| #10000000 | ||||
| 0! | ||||
| 0" | ||||
| #11000000 | ||||
| 1" | ||||
| #12000000 | ||||
| 0" | ||||
| #13000000 | ||||
| 1" | ||||
| #14000000 | ||||
| 0" | ||||
| #15000000 | ||||
| 1" | ||||
| #16000000 | ||||
| 0" | ||||
| #17000000 | ||||
| 1" | ||||
| #18000000 | ||||
| 0" | ||||
| #19000000 | ||||
| 1" | ||||
| #20000000 | ||||
| 1! | ||||
| 0" | ||||
| #21000000 | ||||
| 1" | ||||
| b100000 # | ||||
| #22000000 | ||||
| 0" | ||||
| #23000000 | ||||
| 1" | ||||
| b1010111 # | ||||
| #24000000 | ||||
| 0" | ||||
| #25000000 | ||||
| 1" | ||||
| b1101111 # | ||||
| #26000000 | ||||
| 0" | ||||
| #27000000 | ||||
| 1" | ||||
| b1110010 # | ||||
| #28000000 | ||||
| 0" | ||||
| #29000000 | ||||
| 1" | ||||
| b1101100 # | ||||
| #30000000 | ||||
| 0! | ||||
| 0" | ||||
| #31000000 | ||||
| 1" | ||||
| #32000000 | ||||
| 0" | ||||
| #33000000 | ||||
| 1" | ||||
| #34000000 | ||||
| 0" | ||||
| #35000000 | ||||
| 1" | ||||
| #36000000 | ||||
| 0" | ||||
| #37000000 | ||||
| 1" | ||||
| #38000000 | ||||
| 0" | ||||
| #39000000 | ||||
| 1" | ||||
| #40000000 | ||||
| 1! | ||||
| 0" | ||||
| #41000000 | ||||
| 1" | ||||
| b1100100 # | ||||
| #42000000 | ||||
| 0" | ||||
| #43000000 | ||||
| 1" | ||||
| b100001 # | ||||
| #44000000 | ||||
| 0" | ||||
| #45000000 | ||||
| 1" | ||||
| b1010 # | ||||
| #46000000 | ||||
| 0" | ||||
| #47000000 | ||||
| 1" | ||||
| b1001000 # | ||||
| #48000000 | ||||
| 0" | ||||
| #49000000 | ||||
| 1" | ||||
| b1100101 # | ||||
| #50000000 | ||||
| 0! | ||||
| 0" | ||||
| #51000000 | ||||
| 1" | ||||
| #52000000 | ||||
| 0" | ||||
| #53000000 | ||||
| 1" | ||||
| #54000000 | ||||
| 0" | ||||
| #55000000 | ||||
| 1" | ||||
| #56000000 | ||||
| 0" | ||||
| #57000000 | ||||
| 1" | ||||
| #58000000 | ||||
| 0" | ||||
| #59000000 | ||||
| 1" | ||||
| #60000000 | ||||
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							|  | @ -598,7 +598,17 @@ Simulation { | |||
|             .. | ||||
|         }, | ||||
|     }, | ||||
|     uninitialized_inputs: {}, | ||||
|     main_module: SimulationModuleState { | ||||
|         base_targets: [ | ||||
|             Instance { | ||||
|                 name: <simulator>::memories2, | ||||
|                 instantiated: Module { | ||||
|                     name: memories2, | ||||
|                     .. | ||||
|                 }, | ||||
|             }.rw, | ||||
|         ], | ||||
|         uninitialized_ios: {}, | ||||
|         io_targets: { | ||||
|             Instance { | ||||
|                 name: <simulator>::memories2, | ||||
|  | @ -606,506 +616,61 @@ Simulation { | |||
|                     name: memories2, | ||||
|                     .. | ||||
|                 }, | ||||
|         }.rw: CompiledValue { | ||||
|             layout: CompiledTypeLayout { | ||||
|                 ty: Bundle { | ||||
|                     /* offset = 0 */ | ||||
|                     addr: UInt<3>, | ||||
|                     /* offset = 3 */ | ||||
|                     en: Bool, | ||||
|                     /* offset = 4 */ | ||||
|                     clk: Clock, | ||||
|                     #[hdl(flip)] /* offset = 5 */ | ||||
|                     rdata: UInt<2>, | ||||
|                     /* offset = 7 */ | ||||
|                     wmode: Bool, | ||||
|                     /* offset = 8 */ | ||||
|                     wdata: UInt<2>, | ||||
|                     /* offset = 10 */ | ||||
|                     wmask: Bool, | ||||
|                 }, | ||||
|                 layout: TypeLayout { | ||||
|                     small_slots: StatePartLayout<SmallSlots> { | ||||
|                         len: 0, | ||||
|                         debug_data: [], | ||||
|                         .. | ||||
|                     }, | ||||
|                     big_slots: StatePartLayout<BigSlots> { | ||||
|                         len: 7, | ||||
|                         debug_data: [ | ||||
|                             SlotDebugData { | ||||
|                                 name: "InstantiatedModule(memories2: memories2).memories2::rw.addr", | ||||
|                                 ty: UInt<3>, | ||||
|                             }, | ||||
|                             SlotDebugData { | ||||
|                                 name: "InstantiatedModule(memories2: memories2).memories2::rw.en", | ||||
|                                 ty: Bool, | ||||
|                             }, | ||||
|                             SlotDebugData { | ||||
|                                 name: "InstantiatedModule(memories2: memories2).memories2::rw.clk", | ||||
|                                 ty: Clock, | ||||
|                             }, | ||||
|                             SlotDebugData { | ||||
|                                 name: "InstantiatedModule(memories2: memories2).memories2::rw.rdata", | ||||
|                                 ty: UInt<2>, | ||||
|                             }, | ||||
|                             SlotDebugData { | ||||
|                                 name: "InstantiatedModule(memories2: memories2).memories2::rw.wmode", | ||||
|                                 ty: Bool, | ||||
|                             }, | ||||
|                             SlotDebugData { | ||||
|                                 name: "InstantiatedModule(memories2: memories2).memories2::rw.wdata", | ||||
|                                 ty: UInt<2>, | ||||
|                             }, | ||||
|                             SlotDebugData { | ||||
|                                 name: "InstantiatedModule(memories2: memories2).memories2::rw.wmask", | ||||
|                                 ty: Bool, | ||||
|                             }, | ||||
|                         ], | ||||
|                         .. | ||||
|                     }, | ||||
|                 }, | ||||
|                 body: Bundle { | ||||
|                     fields: [ | ||||
|                         CompiledBundleField { | ||||
|                             offset: TypeIndex { | ||||
|                                 small_slots: StatePartIndex<SmallSlots>(0), | ||||
|                                 big_slots: StatePartIndex<BigSlots>(0), | ||||
|                             }, | ||||
|                             ty: CompiledTypeLayout { | ||||
|                                 ty: UInt<3>, | ||||
|                                 layout: TypeLayout { | ||||
|                                     small_slots: StatePartLayout<SmallSlots> { | ||||
|                                         len: 0, | ||||
|                                         debug_data: [], | ||||
|                                         .. | ||||
|                                     }, | ||||
|                                     big_slots: StatePartLayout<BigSlots> { | ||||
|                                         len: 1, | ||||
|                                         debug_data: [ | ||||
|                                             SlotDebugData { | ||||
|                                                 name: "", | ||||
|                                                 ty: UInt<3>, | ||||
|                                             }, | ||||
|                                         ], | ||||
|                                         .. | ||||
|                                     }, | ||||
|                                 }, | ||||
|                                 body: Scalar, | ||||
|                             }, | ||||
|                         }, | ||||
|                         CompiledBundleField { | ||||
|                             offset: TypeIndex { | ||||
|                                 small_slots: StatePartIndex<SmallSlots>(0), | ||||
|                                 big_slots: StatePartIndex<BigSlots>(1), | ||||
|                             }, | ||||
|                             ty: CompiledTypeLayout { | ||||
|                                 ty: Bool, | ||||
|                                 layout: TypeLayout { | ||||
|                                     small_slots: StatePartLayout<SmallSlots> { | ||||
|                                         len: 0, | ||||
|                                         debug_data: [], | ||||
|                                         .. | ||||
|                                     }, | ||||
|                                     big_slots: StatePartLayout<BigSlots> { | ||||
|                                         len: 1, | ||||
|                                         debug_data: [ | ||||
|                                             SlotDebugData { | ||||
|                                                 name: "", | ||||
|                                                 ty: Bool, | ||||
|                                             }, | ||||
|                                         ], | ||||
|                                         .. | ||||
|                                     }, | ||||
|                                 }, | ||||
|                                 body: Scalar, | ||||
|                             }, | ||||
|                         }, | ||||
|                         CompiledBundleField { | ||||
|                             offset: TypeIndex { | ||||
|                                 small_slots: StatePartIndex<SmallSlots>(0), | ||||
|                                 big_slots: StatePartIndex<BigSlots>(2), | ||||
|                             }, | ||||
|                             ty: CompiledTypeLayout { | ||||
|                                 ty: Clock, | ||||
|                                 layout: TypeLayout { | ||||
|                                     small_slots: StatePartLayout<SmallSlots> { | ||||
|                                         len: 0, | ||||
|                                         debug_data: [], | ||||
|                                         .. | ||||
|                                     }, | ||||
|                                     big_slots: StatePartLayout<BigSlots> { | ||||
|                                         len: 1, | ||||
|                                         debug_data: [ | ||||
|                                             SlotDebugData { | ||||
|                                                 name: "", | ||||
|                                                 ty: Clock, | ||||
|                                             }, | ||||
|                                         ], | ||||
|                                         .. | ||||
|                                     }, | ||||
|                                 }, | ||||
|                                 body: Scalar, | ||||
|                             }, | ||||
|                         }, | ||||
|                         CompiledBundleField { | ||||
|                             offset: TypeIndex { | ||||
|                                 small_slots: StatePartIndex<SmallSlots>(0), | ||||
|                                 big_slots: StatePartIndex<BigSlots>(3), | ||||
|                             }, | ||||
|                             ty: CompiledTypeLayout { | ||||
|                                 ty: UInt<2>, | ||||
|                                 layout: TypeLayout { | ||||
|                                     small_slots: StatePartLayout<SmallSlots> { | ||||
|                                         len: 0, | ||||
|                                         debug_data: [], | ||||
|                                         .. | ||||
|                                     }, | ||||
|                                     big_slots: StatePartLayout<BigSlots> { | ||||
|                                         len: 1, | ||||
|                                         debug_data: [ | ||||
|                                             SlotDebugData { | ||||
|                                                 name: "", | ||||
|                                                 ty: UInt<2>, | ||||
|                                             }, | ||||
|                                         ], | ||||
|                                         .. | ||||
|                                     }, | ||||
|                                 }, | ||||
|                                 body: Scalar, | ||||
|                             }, | ||||
|                         }, | ||||
|                         CompiledBundleField { | ||||
|                             offset: TypeIndex { | ||||
|                                 small_slots: StatePartIndex<SmallSlots>(0), | ||||
|                                 big_slots: StatePartIndex<BigSlots>(4), | ||||
|                             }, | ||||
|                             ty: CompiledTypeLayout { | ||||
|                                 ty: Bool, | ||||
|                                 layout: TypeLayout { | ||||
|                                     small_slots: StatePartLayout<SmallSlots> { | ||||
|                                         len: 0, | ||||
|                                         debug_data: [], | ||||
|                                         .. | ||||
|                                     }, | ||||
|                                     big_slots: StatePartLayout<BigSlots> { | ||||
|                                         len: 1, | ||||
|                                         debug_data: [ | ||||
|                                             SlotDebugData { | ||||
|                                                 name: "", | ||||
|                                                 ty: Bool, | ||||
|                                             }, | ||||
|                                         ], | ||||
|                                         .. | ||||
|                                     }, | ||||
|                                 }, | ||||
|                                 body: Scalar, | ||||
|                             }, | ||||
|                         }, | ||||
|                         CompiledBundleField { | ||||
|                             offset: TypeIndex { | ||||
|                                 small_slots: StatePartIndex<SmallSlots>(0), | ||||
|                                 big_slots: StatePartIndex<BigSlots>(5), | ||||
|                             }, | ||||
|                             ty: CompiledTypeLayout { | ||||
|                                 ty: UInt<2>, | ||||
|                                 layout: TypeLayout { | ||||
|                                     small_slots: StatePartLayout<SmallSlots> { | ||||
|                                         len: 0, | ||||
|                                         debug_data: [], | ||||
|                                         .. | ||||
|                                     }, | ||||
|                                     big_slots: StatePartLayout<BigSlots> { | ||||
|                                         len: 1, | ||||
|                                         debug_data: [ | ||||
|                                             SlotDebugData { | ||||
|                                                 name: "", | ||||
|                                                 ty: UInt<2>, | ||||
|                                             }, | ||||
|                                         ], | ||||
|                                         .. | ||||
|                                     }, | ||||
|                                 }, | ||||
|                                 body: Scalar, | ||||
|                             }, | ||||
|                         }, | ||||
|                         CompiledBundleField { | ||||
|                             offset: TypeIndex { | ||||
|                                 small_slots: StatePartIndex<SmallSlots>(0), | ||||
|                                 big_slots: StatePartIndex<BigSlots>(6), | ||||
|                             }, | ||||
|                             ty: CompiledTypeLayout { | ||||
|                                 ty: Bool, | ||||
|                                 layout: TypeLayout { | ||||
|                                     small_slots: StatePartLayout<SmallSlots> { | ||||
|                                         len: 0, | ||||
|                                         debug_data: [], | ||||
|                                         .. | ||||
|                                     }, | ||||
|                                     big_slots: StatePartLayout<BigSlots> { | ||||
|                                         len: 1, | ||||
|                                         debug_data: [ | ||||
|                                             SlotDebugData { | ||||
|                                                 name: "", | ||||
|                                                 ty: Bool, | ||||
|                                             }, | ||||
|                                         ], | ||||
|                                         .. | ||||
|                                     }, | ||||
|                                 }, | ||||
|                                 body: Scalar, | ||||
|                             }, | ||||
|                         }, | ||||
|                     ], | ||||
|                 }, | ||||
|             }, | ||||
|             range: TypeIndexRange { | ||||
|                 small_slots: StatePartIndexRange<SmallSlots> { start: 0, len: 0 }, | ||||
|                 big_slots: StatePartIndexRange<BigSlots> { start: 0, len: 7 }, | ||||
|             }, | ||||
|             write: None, | ||||
|         }, | ||||
|             }.rw, | ||||
|             Instance { | ||||
|                 name: <simulator>::memories2, | ||||
|                 instantiated: Module { | ||||
|                     name: memories2, | ||||
|                     .. | ||||
|                 }, | ||||
|         }.rw.addr: CompiledValue { | ||||
|             layout: CompiledTypeLayout { | ||||
|                 ty: UInt<3>, | ||||
|                 layout: TypeLayout { | ||||
|                     small_slots: StatePartLayout<SmallSlots> { | ||||
|                         len: 0, | ||||
|                         debug_data: [], | ||||
|                         .. | ||||
|                     }, | ||||
|                     big_slots: StatePartLayout<BigSlots> { | ||||
|                         len: 1, | ||||
|                         debug_data: [ | ||||
|                             SlotDebugData { | ||||
|                                 name: "", | ||||
|                                 ty: UInt<3>, | ||||
|                             }, | ||||
|                         ], | ||||
|                         .. | ||||
|                     }, | ||||
|                 }, | ||||
|                 body: Scalar, | ||||
|             }, | ||||
|             range: TypeIndexRange { | ||||
|                 small_slots: StatePartIndexRange<SmallSlots> { start: 0, len: 0 }, | ||||
|                 big_slots: StatePartIndexRange<BigSlots> { start: 0, len: 1 }, | ||||
|             }, | ||||
|             write: None, | ||||
|         }, | ||||
|             }.rw.addr, | ||||
|             Instance { | ||||
|                 name: <simulator>::memories2, | ||||
|                 instantiated: Module { | ||||
|                     name: memories2, | ||||
|                     .. | ||||
|                 }, | ||||
|         }.rw.clk: CompiledValue { | ||||
|             layout: CompiledTypeLayout { | ||||
|                 ty: Clock, | ||||
|                 layout: TypeLayout { | ||||
|                     small_slots: StatePartLayout<SmallSlots> { | ||||
|                         len: 0, | ||||
|                         debug_data: [], | ||||
|                         .. | ||||
|                     }, | ||||
|                     big_slots: StatePartLayout<BigSlots> { | ||||
|                         len: 1, | ||||
|                         debug_data: [ | ||||
|                             SlotDebugData { | ||||
|                                 name: "", | ||||
|                                 ty: Clock, | ||||
|                             }, | ||||
|                         ], | ||||
|                         .. | ||||
|                     }, | ||||
|                 }, | ||||
|                 body: Scalar, | ||||
|             }, | ||||
|             range: TypeIndexRange { | ||||
|                 small_slots: StatePartIndexRange<SmallSlots> { start: 0, len: 0 }, | ||||
|                 big_slots: StatePartIndexRange<BigSlots> { start: 2, len: 1 }, | ||||
|             }, | ||||
|             write: None, | ||||
|         }, | ||||
|             }.rw.clk, | ||||
|             Instance { | ||||
|                 name: <simulator>::memories2, | ||||
|                 instantiated: Module { | ||||
|                     name: memories2, | ||||
|                     .. | ||||
|                 }, | ||||
|         }.rw.en: CompiledValue { | ||||
|             layout: CompiledTypeLayout { | ||||
|                 ty: Bool, | ||||
|                 layout: TypeLayout { | ||||
|                     small_slots: StatePartLayout<SmallSlots> { | ||||
|                         len: 0, | ||||
|                         debug_data: [], | ||||
|                         .. | ||||
|                     }, | ||||
|                     big_slots: StatePartLayout<BigSlots> { | ||||
|                         len: 1, | ||||
|                         debug_data: [ | ||||
|                             SlotDebugData { | ||||
|                                 name: "", | ||||
|                                 ty: Bool, | ||||
|                             }, | ||||
|                         ], | ||||
|                         .. | ||||
|                     }, | ||||
|                 }, | ||||
|                 body: Scalar, | ||||
|             }, | ||||
|             range: TypeIndexRange { | ||||
|                 small_slots: StatePartIndexRange<SmallSlots> { start: 0, len: 0 }, | ||||
|                 big_slots: StatePartIndexRange<BigSlots> { start: 1, len: 1 }, | ||||
|             }, | ||||
|             write: None, | ||||
|         }, | ||||
|             }.rw.en, | ||||
|             Instance { | ||||
|                 name: <simulator>::memories2, | ||||
|                 instantiated: Module { | ||||
|                     name: memories2, | ||||
|                     .. | ||||
|                 }, | ||||
|         }.rw.rdata: CompiledValue { | ||||
|             layout: CompiledTypeLayout { | ||||
|                 ty: UInt<2>, | ||||
|                 layout: TypeLayout { | ||||
|                     small_slots: StatePartLayout<SmallSlots> { | ||||
|                         len: 0, | ||||
|                         debug_data: [], | ||||
|                         .. | ||||
|                     }, | ||||
|                     big_slots: StatePartLayout<BigSlots> { | ||||
|                         len: 1, | ||||
|                         debug_data: [ | ||||
|                             SlotDebugData { | ||||
|                                 name: "", | ||||
|                                 ty: UInt<2>, | ||||
|                             }, | ||||
|                         ], | ||||
|                         .. | ||||
|                     }, | ||||
|                 }, | ||||
|                 body: Scalar, | ||||
|             }, | ||||
|             range: TypeIndexRange { | ||||
|                 small_slots: StatePartIndexRange<SmallSlots> { start: 0, len: 0 }, | ||||
|                 big_slots: StatePartIndexRange<BigSlots> { start: 3, len: 1 }, | ||||
|             }, | ||||
|             write: None, | ||||
|         }, | ||||
|             }.rw.rdata, | ||||
|             Instance { | ||||
|                 name: <simulator>::memories2, | ||||
|                 instantiated: Module { | ||||
|                     name: memories2, | ||||
|                     .. | ||||
|                 }, | ||||
|         }.rw.wdata: CompiledValue { | ||||
|             layout: CompiledTypeLayout { | ||||
|                 ty: UInt<2>, | ||||
|                 layout: TypeLayout { | ||||
|                     small_slots: StatePartLayout<SmallSlots> { | ||||
|                         len: 0, | ||||
|                         debug_data: [], | ||||
|                         .. | ||||
|                     }, | ||||
|                     big_slots: StatePartLayout<BigSlots> { | ||||
|                         len: 1, | ||||
|                         debug_data: [ | ||||
|                             SlotDebugData { | ||||
|                                 name: "", | ||||
|                                 ty: UInt<2>, | ||||
|                             }, | ||||
|                         ], | ||||
|                         .. | ||||
|                     }, | ||||
|                 }, | ||||
|                 body: Scalar, | ||||
|             }, | ||||
|             range: TypeIndexRange { | ||||
|                 small_slots: StatePartIndexRange<SmallSlots> { start: 0, len: 0 }, | ||||
|                 big_slots: StatePartIndexRange<BigSlots> { start: 5, len: 1 }, | ||||
|             }, | ||||
|             write: None, | ||||
|         }, | ||||
|             }.rw.wdata, | ||||
|             Instance { | ||||
|                 name: <simulator>::memories2, | ||||
|                 instantiated: Module { | ||||
|                     name: memories2, | ||||
|                     .. | ||||
|                 }, | ||||
|         }.rw.wmask: CompiledValue { | ||||
|             layout: CompiledTypeLayout { | ||||
|                 ty: Bool, | ||||
|                 layout: TypeLayout { | ||||
|                     small_slots: StatePartLayout<SmallSlots> { | ||||
|                         len: 0, | ||||
|                         debug_data: [], | ||||
|                         .. | ||||
|                     }, | ||||
|                     big_slots: StatePartLayout<BigSlots> { | ||||
|                         len: 1, | ||||
|                         debug_data: [ | ||||
|                             SlotDebugData { | ||||
|                                 name: "", | ||||
|                                 ty: Bool, | ||||
|                             }, | ||||
|                         ], | ||||
|                         .. | ||||
|                     }, | ||||
|                 }, | ||||
|                 body: Scalar, | ||||
|             }, | ||||
|             range: TypeIndexRange { | ||||
|                 small_slots: StatePartIndexRange<SmallSlots> { start: 0, len: 0 }, | ||||
|                 big_slots: StatePartIndexRange<BigSlots> { start: 6, len: 1 }, | ||||
|             }, | ||||
|             write: None, | ||||
|         }, | ||||
|             }.rw.wmask, | ||||
|             Instance { | ||||
|                 name: <simulator>::memories2, | ||||
|                 instantiated: Module { | ||||
|                     name: memories2, | ||||
|                     .. | ||||
|                 }, | ||||
|         }.rw.wmode: CompiledValue { | ||||
|             layout: CompiledTypeLayout { | ||||
|                 ty: Bool, | ||||
|                 layout: TypeLayout { | ||||
|                     small_slots: StatePartLayout<SmallSlots> { | ||||
|                         len: 0, | ||||
|                         debug_data: [], | ||||
|                         .. | ||||
|             }.rw.wmode, | ||||
|         }, | ||||
|                     big_slots: StatePartLayout<BigSlots> { | ||||
|                         len: 1, | ||||
|                         debug_data: [ | ||||
|                             SlotDebugData { | ||||
|                                 name: "", | ||||
|                                 ty: Bool, | ||||
|         did_initial_settle: true, | ||||
|     }, | ||||
|                         ], | ||||
|                         .. | ||||
|                     }, | ||||
|                 }, | ||||
|                 body: Scalar, | ||||
|             }, | ||||
|             range: TypeIndexRange { | ||||
|                 small_slots: StatePartIndexRange<SmallSlots> { start: 0, len: 0 }, | ||||
|                 big_slots: StatePartIndexRange<BigSlots> { start: 4, len: 1 }, | ||||
|             }, | ||||
|             write: None, | ||||
|         }, | ||||
|     }, | ||||
|     made_initial_step: true, | ||||
|     needs_settle: false, | ||||
|     extern_modules: [], | ||||
|     state_ready_to_run: false, | ||||
|     trace_decls: TraceModule { | ||||
|         name: "memories2", | ||||
|         children: [ | ||||
|  |  | |||
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							|  | @ -216,7 +216,17 @@ Simulation { | |||
|             .. | ||||
|         }, | ||||
|     }, | ||||
|     uninitialized_inputs: {}, | ||||
|     main_module: SimulationModuleState { | ||||
|         base_targets: [ | ||||
|             Instance { | ||||
|                 name: <simulator>::mod1, | ||||
|                 instantiated: Module { | ||||
|                     name: mod1, | ||||
|                     .. | ||||
|                 }, | ||||
|             }.o, | ||||
|         ], | ||||
|         uninitialized_ios: {}, | ||||
|         io_targets: { | ||||
|             Instance { | ||||
|                 name: <simulator>::mod1, | ||||
|  | @ -224,305 +234,40 @@ Simulation { | |||
|                     name: mod1, | ||||
|                     .. | ||||
|                 }, | ||||
|         }.o: CompiledValue { | ||||
|             layout: CompiledTypeLayout { | ||||
|                 ty: Bundle { | ||||
|                     #[hdl(flip)] /* offset = 0 */ | ||||
|                     i: UInt<4>, | ||||
|                     /* offset = 4 */ | ||||
|                     o: SInt<2>, | ||||
|                     #[hdl(flip)] /* offset = 6 */ | ||||
|                     i2: SInt<2>, | ||||
|                     /* offset = 8 */ | ||||
|                     o2: UInt<4>, | ||||
|                 }, | ||||
|                 layout: TypeLayout { | ||||
|                     small_slots: StatePartLayout<SmallSlots> { | ||||
|                         len: 0, | ||||
|                         debug_data: [], | ||||
|                         .. | ||||
|                     }, | ||||
|                     big_slots: StatePartLayout<BigSlots> { | ||||
|                         len: 4, | ||||
|                         debug_data: [ | ||||
|                             SlotDebugData { | ||||
|                                 name: "InstantiatedModule(mod1: mod1).mod1::o.i", | ||||
|                                 ty: UInt<4>, | ||||
|                             }, | ||||
|                             SlotDebugData { | ||||
|                                 name: "InstantiatedModule(mod1: mod1).mod1::o.o", | ||||
|                                 ty: SInt<2>, | ||||
|                             }, | ||||
|                             SlotDebugData { | ||||
|                                 name: "InstantiatedModule(mod1: mod1).mod1::o.i2", | ||||
|                                 ty: SInt<2>, | ||||
|                             }, | ||||
|                             SlotDebugData { | ||||
|                                 name: "InstantiatedModule(mod1: mod1).mod1::o.o2", | ||||
|                                 ty: UInt<4>, | ||||
|                             }, | ||||
|                         ], | ||||
|                         .. | ||||
|                     }, | ||||
|                 }, | ||||
|                 body: Bundle { | ||||
|                     fields: [ | ||||
|                         CompiledBundleField { | ||||
|                             offset: TypeIndex { | ||||
|                                 small_slots: StatePartIndex<SmallSlots>(0), | ||||
|                                 big_slots: StatePartIndex<BigSlots>(0), | ||||
|                             }, | ||||
|                             ty: CompiledTypeLayout { | ||||
|                                 ty: UInt<4>, | ||||
|                                 layout: TypeLayout { | ||||
|                                     small_slots: StatePartLayout<SmallSlots> { | ||||
|                                         len: 0, | ||||
|                                         debug_data: [], | ||||
|                                         .. | ||||
|                                     }, | ||||
|                                     big_slots: StatePartLayout<BigSlots> { | ||||
|                                         len: 1, | ||||
|                                         debug_data: [ | ||||
|                                             SlotDebugData { | ||||
|                                                 name: "", | ||||
|                                                 ty: UInt<4>, | ||||
|                                             }, | ||||
|                                         ], | ||||
|                                         .. | ||||
|                                     }, | ||||
|                                 }, | ||||
|                                 body: Scalar, | ||||
|                             }, | ||||
|                         }, | ||||
|                         CompiledBundleField { | ||||
|                             offset: TypeIndex { | ||||
|                                 small_slots: StatePartIndex<SmallSlots>(0), | ||||
|                                 big_slots: StatePartIndex<BigSlots>(1), | ||||
|                             }, | ||||
|                             ty: CompiledTypeLayout { | ||||
|                                 ty: SInt<2>, | ||||
|                                 layout: TypeLayout { | ||||
|                                     small_slots: StatePartLayout<SmallSlots> { | ||||
|                                         len: 0, | ||||
|                                         debug_data: [], | ||||
|                                         .. | ||||
|                                     }, | ||||
|                                     big_slots: StatePartLayout<BigSlots> { | ||||
|                                         len: 1, | ||||
|                                         debug_data: [ | ||||
|                                             SlotDebugData { | ||||
|                                                 name: "", | ||||
|                                                 ty: SInt<2>, | ||||
|                                             }, | ||||
|                                         ], | ||||
|                                         .. | ||||
|                                     }, | ||||
|                                 }, | ||||
|                                 body: Scalar, | ||||
|                             }, | ||||
|                         }, | ||||
|                         CompiledBundleField { | ||||
|                             offset: TypeIndex { | ||||
|                                 small_slots: StatePartIndex<SmallSlots>(0), | ||||
|                                 big_slots: StatePartIndex<BigSlots>(2), | ||||
|                             }, | ||||
|                             ty: CompiledTypeLayout { | ||||
|                                 ty: SInt<2>, | ||||
|                                 layout: TypeLayout { | ||||
|                                     small_slots: StatePartLayout<SmallSlots> { | ||||
|                                         len: 0, | ||||
|                                         debug_data: [], | ||||
|                                         .. | ||||
|                                     }, | ||||
|                                     big_slots: StatePartLayout<BigSlots> { | ||||
|                                         len: 1, | ||||
|                                         debug_data: [ | ||||
|                                             SlotDebugData { | ||||
|                                                 name: "", | ||||
|                                                 ty: SInt<2>, | ||||
|                                             }, | ||||
|                                         ], | ||||
|                                         .. | ||||
|                                     }, | ||||
|                                 }, | ||||
|                                 body: Scalar, | ||||
|                             }, | ||||
|                         }, | ||||
|                         CompiledBundleField { | ||||
|                             offset: TypeIndex { | ||||
|                                 small_slots: StatePartIndex<SmallSlots>(0), | ||||
|                                 big_slots: StatePartIndex<BigSlots>(3), | ||||
|                             }, | ||||
|                             ty: CompiledTypeLayout { | ||||
|                                 ty: UInt<4>, | ||||
|                                 layout: TypeLayout { | ||||
|                                     small_slots: StatePartLayout<SmallSlots> { | ||||
|                                         len: 0, | ||||
|                                         debug_data: [], | ||||
|                                         .. | ||||
|                                     }, | ||||
|                                     big_slots: StatePartLayout<BigSlots> { | ||||
|                                         len: 1, | ||||
|                                         debug_data: [ | ||||
|                                             SlotDebugData { | ||||
|                                                 name: "", | ||||
|                                                 ty: UInt<4>, | ||||
|                                             }, | ||||
|                                         ], | ||||
|                                         .. | ||||
|                                     }, | ||||
|                                 }, | ||||
|                                 body: Scalar, | ||||
|                             }, | ||||
|                         }, | ||||
|                     ], | ||||
|                 }, | ||||
|             }, | ||||
|             range: TypeIndexRange { | ||||
|                 small_slots: StatePartIndexRange<SmallSlots> { start: 0, len: 0 }, | ||||
|                 big_slots: StatePartIndexRange<BigSlots> { start: 0, len: 4 }, | ||||
|             }, | ||||
|             write: None, | ||||
|         }, | ||||
|             }.o, | ||||
|             Instance { | ||||
|                 name: <simulator>::mod1, | ||||
|                 instantiated: Module { | ||||
|                     name: mod1, | ||||
|                     .. | ||||
|                 }, | ||||
|         }.o.i: CompiledValue { | ||||
|             layout: CompiledTypeLayout { | ||||
|                 ty: UInt<4>, | ||||
|                 layout: TypeLayout { | ||||
|                     small_slots: StatePartLayout<SmallSlots> { | ||||
|                         len: 0, | ||||
|                         debug_data: [], | ||||
|                         .. | ||||
|                     }, | ||||
|                     big_slots: StatePartLayout<BigSlots> { | ||||
|                         len: 1, | ||||
|                         debug_data: [ | ||||
|                             SlotDebugData { | ||||
|                                 name: "", | ||||
|                                 ty: UInt<4>, | ||||
|                             }, | ||||
|                         ], | ||||
|                         .. | ||||
|                     }, | ||||
|                 }, | ||||
|                 body: Scalar, | ||||
|             }, | ||||
|             range: TypeIndexRange { | ||||
|                 small_slots: StatePartIndexRange<SmallSlots> { start: 0, len: 0 }, | ||||
|                 big_slots: StatePartIndexRange<BigSlots> { start: 0, len: 1 }, | ||||
|             }, | ||||
|             write: None, | ||||
|         }, | ||||
|             }.o.i, | ||||
|             Instance { | ||||
|                 name: <simulator>::mod1, | ||||
|                 instantiated: Module { | ||||
|                     name: mod1, | ||||
|                     .. | ||||
|                 }, | ||||
|         }.o.i2: CompiledValue { | ||||
|             layout: CompiledTypeLayout { | ||||
|                 ty: SInt<2>, | ||||
|                 layout: TypeLayout { | ||||
|                     small_slots: StatePartLayout<SmallSlots> { | ||||
|                         len: 0, | ||||
|                         debug_data: [], | ||||
|                         .. | ||||
|                     }, | ||||
|                     big_slots: StatePartLayout<BigSlots> { | ||||
|                         len: 1, | ||||
|                         debug_data: [ | ||||
|                             SlotDebugData { | ||||
|                                 name: "", | ||||
|                                 ty: SInt<2>, | ||||
|                             }, | ||||
|                         ], | ||||
|                         .. | ||||
|                     }, | ||||
|                 }, | ||||
|                 body: Scalar, | ||||
|             }, | ||||
|             range: TypeIndexRange { | ||||
|                 small_slots: StatePartIndexRange<SmallSlots> { start: 0, len: 0 }, | ||||
|                 big_slots: StatePartIndexRange<BigSlots> { start: 2, len: 1 }, | ||||
|             }, | ||||
|             write: None, | ||||
|         }, | ||||
|             }.o.i2, | ||||
|             Instance { | ||||
|                 name: <simulator>::mod1, | ||||
|                 instantiated: Module { | ||||
|                     name: mod1, | ||||
|                     .. | ||||
|                 }, | ||||
|         }.o.o: CompiledValue { | ||||
|             layout: CompiledTypeLayout { | ||||
|                 ty: SInt<2>, | ||||
|                 layout: TypeLayout { | ||||
|                     small_slots: StatePartLayout<SmallSlots> { | ||||
|                         len: 0, | ||||
|                         debug_data: [], | ||||
|                         .. | ||||
|                     }, | ||||
|                     big_slots: StatePartLayout<BigSlots> { | ||||
|                         len: 1, | ||||
|                         debug_data: [ | ||||
|                             SlotDebugData { | ||||
|                                 name: "", | ||||
|                                 ty: SInt<2>, | ||||
|                             }, | ||||
|                         ], | ||||
|                         .. | ||||
|                     }, | ||||
|                 }, | ||||
|                 body: Scalar, | ||||
|             }, | ||||
|             range: TypeIndexRange { | ||||
|                 small_slots: StatePartIndexRange<SmallSlots> { start: 0, len: 0 }, | ||||
|                 big_slots: StatePartIndexRange<BigSlots> { start: 1, len: 1 }, | ||||
|             }, | ||||
|             write: None, | ||||
|         }, | ||||
|             }.o.o, | ||||
|             Instance { | ||||
|                 name: <simulator>::mod1, | ||||
|                 instantiated: Module { | ||||
|                     name: mod1, | ||||
|                     .. | ||||
|                 }, | ||||
|         }.o.o2: CompiledValue { | ||||
|             layout: CompiledTypeLayout { | ||||
|                 ty: UInt<4>, | ||||
|                 layout: TypeLayout { | ||||
|                     small_slots: StatePartLayout<SmallSlots> { | ||||
|                         len: 0, | ||||
|                         debug_data: [], | ||||
|                         .. | ||||
|             }.o.o2, | ||||
|         }, | ||||
|                     big_slots: StatePartLayout<BigSlots> { | ||||
|                         len: 1, | ||||
|                         debug_data: [ | ||||
|                             SlotDebugData { | ||||
|                                 name: "", | ||||
|                                 ty: UInt<4>, | ||||
|         did_initial_settle: true, | ||||
|     }, | ||||
|                         ], | ||||
|                         .. | ||||
|                     }, | ||||
|                 }, | ||||
|                 body: Scalar, | ||||
|             }, | ||||
|             range: TypeIndexRange { | ||||
|                 small_slots: StatePartIndexRange<SmallSlots> { start: 0, len: 0 }, | ||||
|                 big_slots: StatePartIndexRange<BigSlots> { start: 3, len: 1 }, | ||||
|             }, | ||||
|             write: None, | ||||
|         }, | ||||
|     }, | ||||
|     made_initial_step: true, | ||||
|     needs_settle: false, | ||||
|     extern_modules: [], | ||||
|     state_ready_to_run: false, | ||||
|     trace_decls: TraceModule { | ||||
|         name: "mod1", | ||||
|         children: [ | ||||
|  |  | |||
							
								
								
									
										1498
									
								
								crates/fayalite/tests/sim/expected/ripple_counter.txt
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										1498
									
								
								crates/fayalite/tests/sim/expected/ripple_counter.txt
									
										
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							
							
								
								
									
										1753
									
								
								crates/fayalite/tests/sim/expected/ripple_counter.vcd
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										1753
									
								
								crates/fayalite/tests/sim/expected/ripple_counter.vcd
									
										
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							|  | @ -265,7 +265,31 @@ Simulation { | |||
|             .. | ||||
|         }, | ||||
|     }, | ||||
|     uninitialized_inputs: {}, | ||||
|     main_module: SimulationModuleState { | ||||
|         base_targets: [ | ||||
|             Instance { | ||||
|                 name: <simulator>::shift_register, | ||||
|                 instantiated: Module { | ||||
|                     name: shift_register, | ||||
|                     .. | ||||
|                 }, | ||||
|             }.cd, | ||||
|             Instance { | ||||
|                 name: <simulator>::shift_register, | ||||
|                 instantiated: Module { | ||||
|                     name: shift_register, | ||||
|                     .. | ||||
|                 }, | ||||
|             }.d, | ||||
|             Instance { | ||||
|                 name: <simulator>::shift_register, | ||||
|                 instantiated: Module { | ||||
|                     name: shift_register, | ||||
|                     .. | ||||
|                 }, | ||||
|             }.q, | ||||
|         ], | ||||
|         uninitialized_ios: {}, | ||||
|         io_targets: { | ||||
|             Instance { | ||||
|                 name: <simulator>::shift_register, | ||||
|  | @ -273,239 +297,40 @@ Simulation { | |||
|                     name: shift_register, | ||||
|                     .. | ||||
|                 }, | ||||
|         }.cd: CompiledValue { | ||||
|             layout: CompiledTypeLayout { | ||||
|                 ty: Bundle { | ||||
|                     /* offset = 0 */ | ||||
|                     clk: Clock, | ||||
|                     /* offset = 1 */ | ||||
|                     rst: SyncReset, | ||||
|                 }, | ||||
|                 layout: TypeLayout { | ||||
|                     small_slots: StatePartLayout<SmallSlots> { | ||||
|                         len: 0, | ||||
|                         debug_data: [], | ||||
|                         .. | ||||
|                     }, | ||||
|                     big_slots: StatePartLayout<BigSlots> { | ||||
|                         len: 2, | ||||
|                         debug_data: [ | ||||
|                             SlotDebugData { | ||||
|                                 name: "InstantiatedModule(shift_register: shift_register).shift_register::cd.clk", | ||||
|                                 ty: Clock, | ||||
|                             }, | ||||
|                             SlotDebugData { | ||||
|                                 name: "InstantiatedModule(shift_register: shift_register).shift_register::cd.rst", | ||||
|                                 ty: SyncReset, | ||||
|                             }, | ||||
|                         ], | ||||
|                         .. | ||||
|                     }, | ||||
|                 }, | ||||
|                 body: Bundle { | ||||
|                     fields: [ | ||||
|                         CompiledBundleField { | ||||
|                             offset: TypeIndex { | ||||
|                                 small_slots: StatePartIndex<SmallSlots>(0), | ||||
|                                 big_slots: StatePartIndex<BigSlots>(0), | ||||
|                             }, | ||||
|                             ty: CompiledTypeLayout { | ||||
|                                 ty: Clock, | ||||
|                                 layout: TypeLayout { | ||||
|                                     small_slots: StatePartLayout<SmallSlots> { | ||||
|                                         len: 0, | ||||
|                                         debug_data: [], | ||||
|                                         .. | ||||
|                                     }, | ||||
|                                     big_slots: StatePartLayout<BigSlots> { | ||||
|                                         len: 1, | ||||
|                                         debug_data: [ | ||||
|                                             SlotDebugData { | ||||
|                                                 name: "", | ||||
|                                                 ty: Clock, | ||||
|                                             }, | ||||
|                                         ], | ||||
|                                         .. | ||||
|                                     }, | ||||
|                                 }, | ||||
|                                 body: Scalar, | ||||
|                             }, | ||||
|                         }, | ||||
|                         CompiledBundleField { | ||||
|                             offset: TypeIndex { | ||||
|                                 small_slots: StatePartIndex<SmallSlots>(0), | ||||
|                                 big_slots: StatePartIndex<BigSlots>(1), | ||||
|                             }, | ||||
|                             ty: CompiledTypeLayout { | ||||
|                                 ty: SyncReset, | ||||
|                                 layout: TypeLayout { | ||||
|                                     small_slots: StatePartLayout<SmallSlots> { | ||||
|                                         len: 0, | ||||
|                                         debug_data: [], | ||||
|                                         .. | ||||
|                                     }, | ||||
|                                     big_slots: StatePartLayout<BigSlots> { | ||||
|                                         len: 1, | ||||
|                                         debug_data: [ | ||||
|                                             SlotDebugData { | ||||
|                                                 name: "", | ||||
|                                                 ty: SyncReset, | ||||
|                                             }, | ||||
|                                         ], | ||||
|                                         .. | ||||
|                                     }, | ||||
|                                 }, | ||||
|                                 body: Scalar, | ||||
|                             }, | ||||
|                         }, | ||||
|                     ], | ||||
|                 }, | ||||
|             }, | ||||
|             range: TypeIndexRange { | ||||
|                 small_slots: StatePartIndexRange<SmallSlots> { start: 0, len: 0 }, | ||||
|                 big_slots: StatePartIndexRange<BigSlots> { start: 0, len: 2 }, | ||||
|             }, | ||||
|             write: None, | ||||
|         }, | ||||
|             }.cd, | ||||
|             Instance { | ||||
|                 name: <simulator>::shift_register, | ||||
|                 instantiated: Module { | ||||
|                     name: shift_register, | ||||
|                     .. | ||||
|                 }, | ||||
|         }.cd.clk: CompiledValue { | ||||
|             layout: CompiledTypeLayout { | ||||
|                 ty: Clock, | ||||
|                 layout: TypeLayout { | ||||
|                     small_slots: StatePartLayout<SmallSlots> { | ||||
|                         len: 0, | ||||
|                         debug_data: [], | ||||
|                         .. | ||||
|                     }, | ||||
|                     big_slots: StatePartLayout<BigSlots> { | ||||
|                         len: 1, | ||||
|                         debug_data: [ | ||||
|                             SlotDebugData { | ||||
|                                 name: "", | ||||
|                                 ty: Clock, | ||||
|                             }, | ||||
|                         ], | ||||
|                         .. | ||||
|                     }, | ||||
|                 }, | ||||
|                 body: Scalar, | ||||
|             }, | ||||
|             range: TypeIndexRange { | ||||
|                 small_slots: StatePartIndexRange<SmallSlots> { start: 0, len: 0 }, | ||||
|                 big_slots: StatePartIndexRange<BigSlots> { start: 0, len: 1 }, | ||||
|             }, | ||||
|             write: None, | ||||
|         }, | ||||
|             }.cd.clk, | ||||
|             Instance { | ||||
|                 name: <simulator>::shift_register, | ||||
|                 instantiated: Module { | ||||
|                     name: shift_register, | ||||
|                     .. | ||||
|                 }, | ||||
|         }.cd.rst: CompiledValue { | ||||
|             layout: CompiledTypeLayout { | ||||
|                 ty: SyncReset, | ||||
|                 layout: TypeLayout { | ||||
|                     small_slots: StatePartLayout<SmallSlots> { | ||||
|                         len: 0, | ||||
|                         debug_data: [], | ||||
|                         .. | ||||
|                     }, | ||||
|                     big_slots: StatePartLayout<BigSlots> { | ||||
|                         len: 1, | ||||
|                         debug_data: [ | ||||
|                             SlotDebugData { | ||||
|                                 name: "", | ||||
|                                 ty: SyncReset, | ||||
|                             }, | ||||
|                         ], | ||||
|                         .. | ||||
|                     }, | ||||
|                 }, | ||||
|                 body: Scalar, | ||||
|             }, | ||||
|             range: TypeIndexRange { | ||||
|                 small_slots: StatePartIndexRange<SmallSlots> { start: 0, len: 0 }, | ||||
|                 big_slots: StatePartIndexRange<BigSlots> { start: 1, len: 1 }, | ||||
|             }, | ||||
|             write: None, | ||||
|         }, | ||||
|             }.cd.rst, | ||||
|             Instance { | ||||
|                 name: <simulator>::shift_register, | ||||
|                 instantiated: Module { | ||||
|                     name: shift_register, | ||||
|                     .. | ||||
|                 }, | ||||
|         }.d: CompiledValue { | ||||
|             layout: CompiledTypeLayout { | ||||
|                 ty: Bool, | ||||
|                 layout: TypeLayout { | ||||
|                     small_slots: StatePartLayout<SmallSlots> { | ||||
|                         len: 0, | ||||
|                         debug_data: [], | ||||
|                         .. | ||||
|                     }, | ||||
|                     big_slots: StatePartLayout<BigSlots> { | ||||
|                         len: 1, | ||||
|                         debug_data: [ | ||||
|                             SlotDebugData { | ||||
|                                 name: "InstantiatedModule(shift_register: shift_register).shift_register::d", | ||||
|                                 ty: Bool, | ||||
|                             }, | ||||
|                         ], | ||||
|                         .. | ||||
|                     }, | ||||
|                 }, | ||||
|                 body: Scalar, | ||||
|             }, | ||||
|             range: TypeIndexRange { | ||||
|                 small_slots: StatePartIndexRange<SmallSlots> { start: 0, len: 0 }, | ||||
|                 big_slots: StatePartIndexRange<BigSlots> { start: 2, len: 1 }, | ||||
|             }, | ||||
|             write: None, | ||||
|         }, | ||||
|             }.d, | ||||
|             Instance { | ||||
|                 name: <simulator>::shift_register, | ||||
|                 instantiated: Module { | ||||
|                     name: shift_register, | ||||
|                     .. | ||||
|                 }, | ||||
|         }.q: CompiledValue { | ||||
|             layout: CompiledTypeLayout { | ||||
|                 ty: Bool, | ||||
|                 layout: TypeLayout { | ||||
|                     small_slots: StatePartLayout<SmallSlots> { | ||||
|                         len: 0, | ||||
|                         debug_data: [], | ||||
|                         .. | ||||
|             }.q, | ||||
|         }, | ||||
|                     big_slots: StatePartLayout<BigSlots> { | ||||
|                         len: 1, | ||||
|                         debug_data: [ | ||||
|                             SlotDebugData { | ||||
|                                 name: "InstantiatedModule(shift_register: shift_register).shift_register::q", | ||||
|                                 ty: Bool, | ||||
|         did_initial_settle: true, | ||||
|     }, | ||||
|                         ], | ||||
|                         .. | ||||
|                     }, | ||||
|                 }, | ||||
|                 body: Scalar, | ||||
|             }, | ||||
|             range: TypeIndexRange { | ||||
|                 small_slots: StatePartIndexRange<SmallSlots> { start: 0, len: 0 }, | ||||
|                 big_slots: StatePartIndexRange<BigSlots> { start: 3, len: 1 }, | ||||
|             }, | ||||
|             write: None, | ||||
|         }, | ||||
|     }, | ||||
|     made_initial_step: true, | ||||
|     needs_settle: false, | ||||
|     extern_modules: [], | ||||
|     state_ready_to_run: false, | ||||
|     trace_decls: TraceModule { | ||||
|         name: "shift_register", | ||||
|         children: [ | ||||
|  |  | |||
							
								
								
									
										15
									
								
								crates/fayalite/tests/ui/simvalue_is_not_internable.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								crates/fayalite/tests/ui/simvalue_is_not_internable.rs
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,15 @@ | |||
| // SPDX-License-Identifier: LGPL-3.0-or-later
 | ||||
| // See Notices.txt for copyright information
 | ||||
| 
 | ||||
| //! check that SimValue can't be interned, since equality may ignore types
 | ||||
| 
 | ||||
| use fayalite::{ | ||||
|     intern::{Intern, Interned}, | ||||
|     sim::value::SimValue, | ||||
| }; | ||||
| 
 | ||||
| fn f(v: SimValue<()>) -> Interned<SimValue<()>> { | ||||
|     Intern::intern_sized(v) | ||||
| } | ||||
| 
 | ||||
| fn main() {} | ||||
							
								
								
									
										178
									
								
								crates/fayalite/tests/ui/simvalue_is_not_internable.stderr
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										178
									
								
								crates/fayalite/tests/ui/simvalue_is_not_internable.stderr
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,178 @@ | |||
| error[E0277]: `Cell<util::alternating_cell::State>` cannot be shared between threads safely | ||||
|   --> tests/ui/simvalue_is_not_internable.rs:11:26 | ||||
|    | | ||||
| 11 | fn f(v: SimValue<()>) -> Interned<SimValue<()>> { | ||||
|    |                          ^^^^^^^^^^^^^^^^^^^^^^ `Cell<util::alternating_cell::State>` cannot be shared between threads safely | ||||
|    | | ||||
|    = help: within `SimValue<()>`, the trait `Sync` is not implemented for `Cell<util::alternating_cell::State>`, which is required by `SimValue<()>: Sync` | ||||
|    = note: if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` | ||||
| note: required because it appears within the type `util::alternating_cell::AlternatingCell<value::SimValueInner<()>>` | ||||
|   --> src/util/alternating_cell.rs | ||||
|    | | ||||
|    | pub(crate) struct AlternatingCell<T: ?Sized> { | ||||
|    |                   ^^^^^^^^^^^^^^^ | ||||
| note: required because it appears within the type `SimValue<()>` | ||||
|   --> src/sim/value.rs | ||||
|    | | ||||
|    | pub struct SimValue<T: Type> { | ||||
|    |            ^^^^^^^^ | ||||
| note: required by a bound in `fayalite::intern::Interned` | ||||
|   --> src/intern.rs | ||||
|    | | ||||
|    | pub struct Interned<T: ?Sized + 'static + Send + Sync> { | ||||
|    |                                                  ^^^^ required by this bound in `Interned` | ||||
| 
 | ||||
| error[E0277]: `UnsafeCell<value::SimValueInner<()>>` cannot be shared between threads safely | ||||
|   --> tests/ui/simvalue_is_not_internable.rs:11:26 | ||||
|    | | ||||
| 11 | fn f(v: SimValue<()>) -> Interned<SimValue<()>> { | ||||
|    |                          ^^^^^^^^^^^^^^^^^^^^^^ `UnsafeCell<value::SimValueInner<()>>` cannot be shared between threads safely | ||||
|    | | ||||
|    = help: within `SimValue<()>`, the trait `Sync` is not implemented for `UnsafeCell<value::SimValueInner<()>>`, which is required by `SimValue<()>: Sync` | ||||
| note: required because it appears within the type `util::alternating_cell::AlternatingCell<value::SimValueInner<()>>` | ||||
|   --> src/util/alternating_cell.rs | ||||
|    | | ||||
|    | pub(crate) struct AlternatingCell<T: ?Sized> { | ||||
|    |                   ^^^^^^^^^^^^^^^ | ||||
| note: required because it appears within the type `SimValue<()>` | ||||
|   --> src/sim/value.rs | ||||
|    | | ||||
|    | pub struct SimValue<T: Type> { | ||||
|    |            ^^^^^^^^ | ||||
| note: required by a bound in `fayalite::intern::Interned` | ||||
|   --> src/intern.rs | ||||
|    | | ||||
|    | pub struct Interned<T: ?Sized + 'static + Send + Sync> { | ||||
|    |                                                  ^^^^ required by this bound in `Interned` | ||||
| 
 | ||||
| error[E0277]: the trait bound `SimValue<()>: Intern` is not satisfied | ||||
|   --> tests/ui/simvalue_is_not_internable.rs:12:26 | ||||
|    | | ||||
| 12 |     Intern::intern_sized(v) | ||||
|    |     -------------------- ^ the trait `Hash` is not implemented for `SimValue<()>`, which is required by `SimValue<()>: Intern` | ||||
|    |     | | ||||
|    |     required by a bound introduced by this call | ||||
|    | | ||||
|    = help: the following other types implement trait `Intern`: | ||||
|              BitSlice | ||||
|              [T] | ||||
|              str | ||||
|    = note: required for `SimValue<()>` to implement `Intern` | ||||
| 
 | ||||
| error[E0277]: the trait bound `SimValue<()>: Intern` is not satisfied | ||||
|   --> tests/ui/simvalue_is_not_internable.rs:12:26 | ||||
|    | | ||||
| 12 |     Intern::intern_sized(v) | ||||
|    |     -------------------- ^ the trait `std::cmp::Eq` is not implemented for `SimValue<()>`, which is required by `SimValue<()>: Intern` | ||||
|    |     | | ||||
|    |     required by a bound introduced by this call | ||||
|    | | ||||
|    = help: the following other types implement trait `Intern`: | ||||
|              BitSlice | ||||
|              [T] | ||||
|              str | ||||
|    = note: required for `SimValue<()>` to implement `Intern` | ||||
| 
 | ||||
| error[E0277]: `Cell<util::alternating_cell::State>` cannot be shared between threads safely | ||||
|   --> tests/ui/simvalue_is_not_internable.rs:12:26 | ||||
|    | | ||||
| 12 |     Intern::intern_sized(v) | ||||
|    |     -------------------- ^ `Cell<util::alternating_cell::State>` cannot be shared between threads safely | ||||
|    |     | | ||||
|    |     required by a bound introduced by this call | ||||
|    | | ||||
|    = help: within `SimValue<()>`, the trait `Sync` is not implemented for `Cell<util::alternating_cell::State>`, which is required by `SimValue<()>: Sync` | ||||
|    = note: if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` | ||||
| note: required because it appears within the type `util::alternating_cell::AlternatingCell<value::SimValueInner<()>>` | ||||
|   --> src/util/alternating_cell.rs | ||||
|    | | ||||
|    | pub(crate) struct AlternatingCell<T: ?Sized> { | ||||
|    |                   ^^^^^^^^^^^^^^^ | ||||
| note: required because it appears within the type `SimValue<()>` | ||||
|   --> src/sim/value.rs | ||||
|    | | ||||
|    | pub struct SimValue<T: Type> { | ||||
|    |            ^^^^^^^^ | ||||
| note: required by a bound in `intern_sized` | ||||
|   --> src/intern.rs | ||||
|    | | ||||
|    | pub trait Intern: Any + Send + Sync { | ||||
|    |                                ^^^^ required by this bound in `Intern::intern_sized` | ||||
|    |     fn intern(&self) -> Interned<Self>; | ||||
|    |     fn intern_sized(self) -> Interned<Self> | ||||
|    |        ------------ required by a bound in this associated function | ||||
| 
 | ||||
| error[E0277]: `UnsafeCell<value::SimValueInner<()>>` cannot be shared between threads safely | ||||
|   --> tests/ui/simvalue_is_not_internable.rs:12:26 | ||||
|    | | ||||
| 12 |     Intern::intern_sized(v) | ||||
|    |     -------------------- ^ `UnsafeCell<value::SimValueInner<()>>` cannot be shared between threads safely | ||||
|    |     | | ||||
|    |     required by a bound introduced by this call | ||||
|    | | ||||
|    = help: within `SimValue<()>`, the trait `Sync` is not implemented for `UnsafeCell<value::SimValueInner<()>>`, which is required by `SimValue<()>: Sync` | ||||
| note: required because it appears within the type `util::alternating_cell::AlternatingCell<value::SimValueInner<()>>` | ||||
|   --> src/util/alternating_cell.rs | ||||
|    | | ||||
|    | pub(crate) struct AlternatingCell<T: ?Sized> { | ||||
|    |                   ^^^^^^^^^^^^^^^ | ||||
| note: required because it appears within the type `SimValue<()>` | ||||
|   --> src/sim/value.rs | ||||
|    | | ||||
|    | pub struct SimValue<T: Type> { | ||||
|    |            ^^^^^^^^ | ||||
| note: required by a bound in `intern_sized` | ||||
|   --> src/intern.rs | ||||
|    | | ||||
|    | pub trait Intern: Any + Send + Sync { | ||||
|    |                                ^^^^ required by this bound in `Intern::intern_sized` | ||||
|    |     fn intern(&self) -> Interned<Self>; | ||||
|    |     fn intern_sized(self) -> Interned<Self> | ||||
|    |        ------------ required by a bound in this associated function | ||||
| 
 | ||||
| error[E0277]: `Cell<util::alternating_cell::State>` cannot be shared between threads safely | ||||
|   --> tests/ui/simvalue_is_not_internable.rs:12:5 | ||||
|    | | ||||
| 12 |     Intern::intern_sized(v) | ||||
|    |     ^^^^^^^^^^^^^^^^^^^^^^^ `Cell<util::alternating_cell::State>` cannot be shared between threads safely | ||||
|    | | ||||
|    = help: within `SimValue<()>`, the trait `Sync` is not implemented for `Cell<util::alternating_cell::State>`, which is required by `SimValue<()>: Sync` | ||||
|    = note: if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` | ||||
| note: required because it appears within the type `util::alternating_cell::AlternatingCell<value::SimValueInner<()>>` | ||||
|   --> src/util/alternating_cell.rs | ||||
|    | | ||||
|    | pub(crate) struct AlternatingCell<T: ?Sized> { | ||||
|    |                   ^^^^^^^^^^^^^^^ | ||||
| note: required because it appears within the type `SimValue<()>` | ||||
|   --> src/sim/value.rs | ||||
|    | | ||||
|    | pub struct SimValue<T: Type> { | ||||
|    |            ^^^^^^^^ | ||||
| note: required by a bound in `fayalite::intern::Interned` | ||||
|   --> src/intern.rs | ||||
|    | | ||||
|    | pub struct Interned<T: ?Sized + 'static + Send + Sync> { | ||||
|    |                                                  ^^^^ required by this bound in `Interned` | ||||
| 
 | ||||
| error[E0277]: `UnsafeCell<value::SimValueInner<()>>` cannot be shared between threads safely | ||||
|   --> tests/ui/simvalue_is_not_internable.rs:12:5 | ||||
|    | | ||||
| 12 |     Intern::intern_sized(v) | ||||
|    |     ^^^^^^^^^^^^^^^^^^^^^^^ `UnsafeCell<value::SimValueInner<()>>` cannot be shared between threads safely | ||||
|    | | ||||
|    = help: within `SimValue<()>`, the trait `Sync` is not implemented for `UnsafeCell<value::SimValueInner<()>>`, which is required by `SimValue<()>: Sync` | ||||
| note: required because it appears within the type `util::alternating_cell::AlternatingCell<value::SimValueInner<()>>` | ||||
|   --> src/util/alternating_cell.rs | ||||
|    | | ||||
|    | pub(crate) struct AlternatingCell<T: ?Sized> { | ||||
|    |                   ^^^^^^^^^^^^^^^ | ||||
| note: required because it appears within the type `SimValue<()>` | ||||
|   --> src/sim/value.rs | ||||
|    | | ||||
|    | pub struct SimValue<T: Type> { | ||||
|    |            ^^^^^^^^ | ||||
| note: required by a bound in `fayalite::intern::Interned` | ||||
|   --> src/intern.rs | ||||
|    | | ||||
|    | pub struct Interned<T: ?Sized + 'static + Send + Sync> { | ||||
|    |                                                  ^^^^ required by this bound in `Interned` | ||||
|  | @ -49,7 +49,8 @@ | |||
|                 "AsyncReset": "Visible", | ||||
|                 "SyncReset": "Visible", | ||||
|                 "Reset": "Visible", | ||||
|                 "Clock": "Visible" | ||||
|                 "Clock": "Visible", | ||||
|                 "PhantomConst": "Visible" | ||||
|             } | ||||
|         }, | ||||
|         "Bundle": { | ||||
|  | @ -159,7 +160,8 @@ | |||
|             "data": { | ||||
|                 "$kind": "Struct", | ||||
|                 "verilog_name": "Visible", | ||||
|                 "parameters": "Visible" | ||||
|                 "parameters": "Visible", | ||||
|                 "simulation": "Visible" | ||||
|             } | ||||
|         }, | ||||
|         "ExternModuleParameter": { | ||||
|  | @ -1262,6 +1264,17 @@ | |||
|                 "ArrayElement": "Visible", | ||||
|                 "DynArrayElement": "Visible" | ||||
|             } | ||||
|         }, | ||||
|         "PhantomConst": { | ||||
|             "data": { | ||||
|                 "$kind": "Opaque" | ||||
|             }, | ||||
|             "generics": "<T: ?Sized + crate::phantom_const::PhantomConstValue>" | ||||
|         }, | ||||
|         "ExternModuleSimulation": { | ||||
|             "data": { | ||||
|                 "$kind": "Opaque" | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue