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: |     outputs: | ||||||
|       cache-primary-key: ${{ steps.restore-deps.outputs.cache-primary-key }} |       cache-primary-key: ${{ steps.restore-deps.outputs.cache-primary-key }} | ||||||
|     steps: |     steps: | ||||||
|       - uses: https://code.forgejo.org/actions/checkout@v3 |       - uses: https://git.libre-chip.org/mirrors/checkout@v3 | ||||||
|         with: |         with: | ||||||
|           fetch-depth: 0 |           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 |         id: restore-deps | ||||||
|         with: |         with: | ||||||
|           path: deps |           path: deps | ||||||
|  | @ -58,19 +58,19 @@ jobs: | ||||||
|       - name: Get SymbiYosys |       - name: Get SymbiYosys | ||||||
|         if: steps.restore-deps.outputs.cache-hit != 'true' |         if: steps.restore-deps.outputs.cache-hit != 'true' | ||||||
|         run: | |         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 |       - name: Build Z3 | ||||||
|         if: steps.restore-deps.outputs.cache-hit != 'true' |         if: steps.restore-deps.outputs.cache-hit != 'true' | ||||||
|         run: | |         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) |           (cd deps/z3; PYTHON=python3 ./configure --prefix=/usr/local) | ||||||
|           make -C deps/z3/build -j"$(nproc)" |           make -C deps/z3/build -j"$(nproc)" | ||||||
|       - name: Build Yosys |       - name: Build Yosys | ||||||
|         if: steps.restore-deps.outputs.cache-hit != 'true' |         if: steps.restore-deps.outputs.cache-hit != 'true' | ||||||
|         run: | |         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)" |           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' |         if: steps.restore-deps.outputs.cache-hit != 'true' | ||||||
|         with: |         with: | ||||||
|           path: deps |           path: deps | ||||||
|  |  | ||||||
|  | @ -9,7 +9,7 @@ jobs: | ||||||
|     runs-on: debian-12 |     runs-on: debian-12 | ||||||
|     needs: deps |     needs: deps | ||||||
|     steps: |     steps: | ||||||
|       - uses: https://code.forgejo.org/actions/checkout@v3 |       - uses: https://git.libre-chip.org/mirrors/checkout@v3 | ||||||
|         with: |         with: | ||||||
|           fetch-depth: 0 |           fetch-depth: 0 | ||||||
|       - run: | |       - run: | | ||||||
|  | @ -41,7 +41,7 @@ jobs: | ||||||
|           curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain 1.82.0 |           curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain 1.82.0 | ||||||
|           source "$HOME/.cargo/env" |           source "$HOME/.cargo/env" | ||||||
|           echo "$PATH" >> "$GITHUB_PATH" |           echo "$PATH" >> "$GITHUB_PATH" | ||||||
|       - uses: https://code.forgejo.org/actions/cache/restore@v3 |       - uses: https://git.libre-chip.org/mirrors/cache/restore@v3 | ||||||
|         with: |         with: | ||||||
|           path: deps |           path: deps | ||||||
|           key: ${{ needs.deps.outputs.cache-primary-key }} |           key: ${{ needs.deps.outputs.cache-primary-key }} | ||||||
|  | @ -52,10 +52,11 @@ jobs: | ||||||
|           make -C deps/yosys install |           make -C deps/yosys install | ||||||
|           export PATH="$(realpath deps/firtool/bin):$PATH" |           export PATH="$(realpath deps/firtool/bin):$PATH" | ||||||
|           echo "$PATH" >> "$GITHUB_PATH" |           echo "$PATH" >> "$GITHUB_PATH" | ||||||
|       - uses: https://github.com/Swatinem/rust-cache@v2 |       - uses: https://git.libre-chip.org/mirrors/rust-cache@v2 | ||||||
|         with: |         with: | ||||||
|           save-if: ${{ github.ref == 'refs/heads/master' }} |           save-if: ${{ github.ref == 'refs/heads/master' }} | ||||||
|       - run: cargo test |       - run: cargo test | ||||||
|       - run: cargo build --tests --features=unstable-doc |       - run: cargo build --tests --features=unstable-doc | ||||||
|       - run: cargo test --doc --features=unstable-doc |       - run: cargo test --doc --features=unstable-doc | ||||||
|       - run: cargo 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. | # It is not intended for manual editing. | ||||||
| version = 3 | 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]] | [[package]] | ||||||
| name = "allocator-api2" | name = "allocator-api2" | ||||||
| version = "0.2.16" | version = "0.2.16" | ||||||
|  | @ -365,6 +353,12 @@ version = "0.5.7" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "1d674e81391d1e1ab681a28d99df07927c6d4aa5b027d7da16ba32d1d21ecd99" | checksum = "1d674e81391d1e1ab681a28d99df07927c6d4aa5b027d7da16ba32d1d21ecd99" | ||||||
| 
 | 
 | ||||||
|  | [[package]] | ||||||
|  | name = "foldhash" | ||||||
|  | version = "0.1.5" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" | ||||||
|  | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "funty" | name = "funty" | ||||||
| version = "2.0.0" | version = "2.0.0" | ||||||
|  | @ -400,12 +394,13 @@ checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" | ||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "hashbrown" | name = "hashbrown" | ||||||
| version = "0.14.3" | version = "0.15.2" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" | checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "ahash", |  | ||||||
|  "allocator-api2", |  "allocator-api2", | ||||||
|  |  "equivalent", | ||||||
|  |  "foldhash", | ||||||
| ] | ] | ||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
|  | @ -431,9 +426,9 @@ checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683" | ||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "indexmap" | name = "indexmap" | ||||||
| version = "2.5.0" | version = "2.9.0" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "68b900aa2f7301e21c36462b170ee99994de34dff39a4a6a528e80e7376d07e5" | checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "equivalent", |  "equivalent", | ||||||
|  "hashbrown", |  "hashbrown", | ||||||
|  | @ -524,11 +519,14 @@ dependencies = [ | ||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "petgraph" | name = "petgraph" | ||||||
| version = "0.6.5" | version = "0.8.1" | ||||||
| source = "git+https://github.com/programmerjake/petgraph.git?rev=258ea8071209a924b73fe96f9f87a3b7b45cbc9f#258ea8071209a924b73fe96f9f87a3b7b45cbc9f" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "7a98c6720655620a521dcc722d0ad66cd8afd5d86e34a89ef691c50b7b24de06" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "fixedbitset", |  "fixedbitset", | ||||||
|  |  "hashbrown", | ||||||
|  "indexmap", |  "indexmap", | ||||||
|  |  "serde", | ||||||
| ] | ] | ||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
|  | @ -893,23 +891,3 @@ checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "tap", |  "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"] } | clap = { version = "4.5.9", features = ["derive", "env", "string"] } | ||||||
| ctor = "0.2.8" | ctor = "0.2.8" | ||||||
| eyre = "0.6.12" | eyre = "0.6.12" | ||||||
| hashbrown = "0.14.3" | hashbrown = "0.15.2" | ||||||
| indexmap = { version = "2.5.0", features = ["serde"] } | indexmap = { version = "2.5.0", features = ["serde"] } | ||||||
| jobslot = "0.2.19" | jobslot = "0.2.19" | ||||||
| num-bigint = "0.4.6" | num-bigint = "0.4.6" | ||||||
| num-traits = "0.2.16" | num-traits = "0.2.16" | ||||||
| os_pipe = "1.2.1" | os_pipe = "1.2.1" | ||||||
| # TODO: switch back to crates.io once petgraph accepts PR #684 and releases a new version | petgraph = "0.8.1" | ||||||
| petgraph = { git = "https://github.com/programmerjake/petgraph.git", rev = "258ea8071209a924b73fe96f9f87a3b7b45cbc9f" } |  | ||||||
| prettyplease = "0.2.20" | prettyplease = "0.2.20" | ||||||
| proc-macro2 = "1.0.83" | proc-macro2 = "1.0.83" | ||||||
| quote = "1.0.36" | 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::ExprCall => fold_expr_call); | ||||||
| forward_fold!(syn::ExprIf => fold_expr_if); | forward_fold!(syn::ExprIf => fold_expr_if); | ||||||
| forward_fold!(syn::ExprMatch => fold_expr_match); | 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::ExprPath => fold_expr_path); | ||||||
| forward_fold!(syn::ExprRepeat => fold_expr_repeat); | forward_fold!(syn::ExprRepeat => fold_expr_repeat); | ||||||
| forward_fold!(syn::ExprStruct => fold_expr_struct); | 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) field_flips: Vec<Option<HdlAttr<kw::flip, kw::hdl>>>, | ||||||
|     pub(crate) mask_type_ident: Ident, |     pub(crate) mask_type_ident: Ident, | ||||||
|     pub(crate) mask_type_match_variant_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) match_variant_ident: Ident, | ||||||
|  |     pub(crate) sim_value_ident: Ident, | ||||||
|     pub(crate) builder_ident: Ident, |     pub(crate) builder_ident: Ident, | ||||||
|     pub(crate) mask_type_builder_ident: Ident, |     pub(crate) mask_type_builder_ident: Ident, | ||||||
| } | } | ||||||
|  | @ -83,6 +85,7 @@ impl ParsedBundle { | ||||||
|             custom_bounds, |             custom_bounds, | ||||||
|             no_static: _, |             no_static: _, | ||||||
|             no_runtime_generics: _, |             no_runtime_generics: _, | ||||||
|  |             cmp_eq: _, | ||||||
|         } = options.body; |         } = options.body; | ||||||
|         let mut fields = match fields { |         let mut fields = match fields { | ||||||
|             syn::Fields::Named(fields) => fields, |             syn::Fields::Named(fields) => fields, | ||||||
|  | @ -124,7 +127,9 @@ impl ParsedBundle { | ||||||
|             field_flips, |             field_flips, | ||||||
|             mask_type_ident: format_ident!("__{}__MaskType", ident), |             mask_type_ident: format_ident!("__{}__MaskType", ident), | ||||||
|             mask_type_match_variant_ident: format_ident!("__{}__MaskType__MatchVariant", 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), |             match_variant_ident: format_ident!("__{}__MatchVariant", ident), | ||||||
|  |             sim_value_ident: format_ident!("__{}__SimValue", ident), | ||||||
|             mask_type_builder_ident: format_ident!("__{}__MaskType__Builder", ident), |             mask_type_builder_ident: format_ident!("__{}__MaskType__Builder", ident), | ||||||
|             builder_ident: format_ident!("__{}__Builder", ident), |             builder_ident: format_ident!("__{}__Builder", ident), | ||||||
|             ident, |             ident, | ||||||
|  | @ -426,7 +431,9 @@ impl ToTokens for ParsedBundle { | ||||||
|             field_flips, |             field_flips, | ||||||
|             mask_type_ident, |             mask_type_ident, | ||||||
|             mask_type_match_variant_ident, |             mask_type_match_variant_ident, | ||||||
|  |             mask_type_sim_value_ident, | ||||||
|             match_variant_ident, |             match_variant_ident, | ||||||
|  |             sim_value_ident, | ||||||
|             builder_ident, |             builder_ident, | ||||||
|             mask_type_builder_ident, |             mask_type_builder_ident, | ||||||
|         } = self; |         } = self; | ||||||
|  | @ -437,6 +444,7 @@ impl ToTokens for ParsedBundle { | ||||||
|             custom_bounds: _, |             custom_bounds: _, | ||||||
|             no_static, |             no_static, | ||||||
|             no_runtime_generics, |             no_runtime_generics, | ||||||
|  |             cmp_eq, | ||||||
|         } = &options.body; |         } = &options.body; | ||||||
|         let target = get_target(target, ident); |         let target = get_target(target, ident); | ||||||
|         let mut item_attrs = attrs.clone(); |         let mut item_attrs = attrs.clone(); | ||||||
|  | @ -521,7 +529,7 @@ impl ToTokens for ParsedBundle { | ||||||
|             semi_token: None, |             semi_token: None, | ||||||
|         } |         } | ||||||
|         .to_tokens(tokens); |         .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 { |         for Field { ty, .. } in &mut mask_type_match_variant_fields.named { | ||||||
|             *ty = parse_quote_spanned! {span=> |             *ty = parse_quote_spanned! {span=> | ||||||
|                 ::fayalite::expr::Expr<#ty> |                 ::fayalite::expr::Expr<#ty> | ||||||
|  | @ -563,6 +571,58 @@ impl ToTokens for ParsedBundle { | ||||||
|             semi_token: None, |             semi_token: None, | ||||||
|         } |         } | ||||||
|         .to_tokens(tokens); |         .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 this_token = Ident::new("__this", span); | ||||||
|         let fields_token = Ident::new("__fields", span); |         let fields_token = Ident::new("__fields", span); | ||||||
|         let self_token = Token; |         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(); |         let fields_len = fields.named().into_iter().len(); | ||||||
|         quote_spanned! {span=> |         quote_spanned! {span=> | ||||||
|             #[automatically_derived] |             #[automatically_derived] | ||||||
|  | @ -621,6 +706,7 @@ impl ToTokens for ParsedBundle { | ||||||
|             { |             { | ||||||
|                 type BaseType = ::fayalite::bundle::Bundle; |                 type BaseType = ::fayalite::bundle::Bundle; | ||||||
|                 type MaskType = #mask_type_ident #type_generics; |                 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 MatchVariant = #mask_type_match_variant_ident #type_generics; | ||||||
|                 type MatchActiveScope = (); |                 type MatchActiveScope = (); | ||||||
|                 type MatchVariantAndInactiveScope = ::fayalite::ty::MatchVariantWithoutScope< |                 type MatchVariantAndInactiveScope = ::fayalite::ty::MatchVariantWithoutScope< | ||||||
|  | @ -658,6 +744,34 @@ impl ToTokens for ParsedBundle { | ||||||
|                 fn source_location() -> ::fayalite::source_location::SourceLocation { |                 fn source_location() -> ::fayalite::source_location::SourceLocation { | ||||||
|                     ::fayalite::source_location::SourceLocation::caller() |                     ::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] |             #[automatically_derived] | ||||||
|             impl #impl_generics ::fayalite::bundle::BundleType for #mask_type_ident #type_generics |             impl #impl_generics ::fayalite::bundle::BundleType for #mask_type_ident #type_generics | ||||||
|  | @ -689,11 +803,57 @@ impl ToTokens for ParsedBundle { | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|             #[automatically_derived] |             #[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 |             impl #impl_generics ::fayalite::ty::Type for #target #type_generics | ||||||
|             #where_clause |             #where_clause | ||||||
|             { |             { | ||||||
|                 type BaseType = ::fayalite::bundle::Bundle; |                 type BaseType = ::fayalite::bundle::Bundle; | ||||||
|                 type MaskType = #mask_type_ident #type_generics; |                 type MaskType = #mask_type_ident #type_generics; | ||||||
|  |                 type SimValue = #sim_value_ident #type_generics; | ||||||
|                 type MatchVariant = #match_variant_ident #type_generics; |                 type MatchVariant = #match_variant_ident #type_generics; | ||||||
|                 type MatchActiveScope = (); |                 type MatchActiveScope = (); | ||||||
|                 type MatchVariantAndInactiveScope = ::fayalite::ty::MatchVariantWithoutScope< |                 type MatchVariantAndInactiveScope = ::fayalite::ty::MatchVariantWithoutScope< | ||||||
|  | @ -733,6 +893,34 @@ impl ToTokens for ParsedBundle { | ||||||
|                 fn source_location() -> ::fayalite::source_location::SourceLocation { |                 fn source_location() -> ::fayalite::source_location::SourceLocation { | ||||||
|                     ::fayalite::source_location::SourceLocation::caller() |                     ::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] |             #[automatically_derived] | ||||||
|             impl #impl_generics ::fayalite::bundle::BundleType for #target #type_generics |             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)) |                     ::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); |         .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) { |         if let (None, MaybeParsed::Parsed(generics)) = (no_static, &self.generics) { | ||||||
|             let static_generics = generics.clone().for_static_type(); |             let static_generics = generics.clone().for_static_type(); | ||||||
|             let (static_impl_generics, static_type_generics, static_where_clause) = |             let (static_impl_generics, static_type_generics, static_where_clause) = | ||||||
|  | @ -800,6 +1124,14 @@ impl ToTokens for ParsedBundle { | ||||||
|                 } |                 } | ||||||
|             })); |             })); | ||||||
|             quote_spanned! {span=> |             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] |                 #[automatically_derived] | ||||||
|                 impl #static_impl_generics ::fayalite::ty::StaticType for #mask_type_ident #static_type_generics |                 impl #static_impl_generics ::fayalite::ty::StaticType for #mask_type_ident #static_type_generics | ||||||
|                 #static_where_clause |                 #static_where_clause | ||||||
|  | @ -822,6 +1154,15 @@ impl ToTokens for ParsedBundle { | ||||||
|                     }; |                     }; | ||||||
|                 } |                 } | ||||||
|                 #[automatically_derived] |                 #[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 |                 impl #static_impl_generics ::fayalite::ty::StaticType for #target #static_type_generics | ||||||
|                 #static_where_clause |                 #static_where_clause | ||||||
|                 { |                 { | ||||||
|  |  | ||||||
|  | @ -129,6 +129,9 @@ pub(crate) struct ParsedEnum { | ||||||
|     pub(crate) brace_token: Brace, |     pub(crate) brace_token: Brace, | ||||||
|     pub(crate) variants: Punctuated<ParsedVariant, Token![,]>, |     pub(crate) variants: Punctuated<ParsedVariant, Token![,]>, | ||||||
|     pub(crate) match_variant_ident: Ident, |     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 { | impl ParsedEnum { | ||||||
|  | @ -155,7 +158,11 @@ impl ParsedEnum { | ||||||
|             custom_bounds, |             custom_bounds, | ||||||
|             no_static: _, |             no_static: _, | ||||||
|             no_runtime_generics: _, |             no_runtime_generics: _, | ||||||
|  |             cmp_eq, | ||||||
|         } = options.body; |         } = 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| { |         attrs.retain(|attr| { | ||||||
|             if attr.path().is_ident("repr") { |             if attr.path().is_ident("repr") { | ||||||
|                 errors.error(attr, "#[repr] is not supported on #[hdl] enums"); |                 errors.error(attr, "#[repr] is not supported on #[hdl] enums"); | ||||||
|  | @ -186,6 +193,9 @@ impl ParsedEnum { | ||||||
|             brace_token, |             brace_token, | ||||||
|             variants, |             variants, | ||||||
|             match_variant_ident: format_ident!("__{}__MatchVariant", ident), |             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, |             ident, | ||||||
|         }) |         }) | ||||||
|     } |     } | ||||||
|  | @ -203,6 +213,9 @@ impl ToTokens for ParsedEnum { | ||||||
|             brace_token, |             brace_token, | ||||||
|             variants, |             variants, | ||||||
|             match_variant_ident, |             match_variant_ident, | ||||||
|  |             sim_value_ident, | ||||||
|  |             sim_builder_ident, | ||||||
|  |             sim_builder_ty_field_ident, | ||||||
|         } = self; |         } = self; | ||||||
|         let span = ident.span(); |         let span = ident.span(); | ||||||
|         let ItemOptions { |         let ItemOptions { | ||||||
|  | @ -211,6 +224,7 @@ impl ToTokens for ParsedEnum { | ||||||
|             custom_bounds: _, |             custom_bounds: _, | ||||||
|             no_static, |             no_static, | ||||||
|             no_runtime_generics, |             no_runtime_generics, | ||||||
|  |             cmp_eq: _, // TODO: implement cmp_eq for enums
 | ||||||
|         } = &options.body; |         } = &options.body; | ||||||
|         let target = get_target(target, ident); |         let target = get_target(target, ident); | ||||||
|         let mut struct_attrs = attrs.clone(); |         let mut struct_attrs = attrs.clone(); | ||||||
|  | @ -404,6 +418,133 @@ impl ToTokens for ParsedEnum { | ||||||
|             )), |             )), | ||||||
|         } |         } | ||||||
|         .to_tokens(tokens); |         .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; |         let self_token = Token; | ||||||
|         for (index, ParsedVariant { ident, field, .. }) in variants.iter().enumerate() { |         for (index, ParsedVariant { ident, field, .. }) in variants.iter().enumerate() { | ||||||
|             if let Some(ParsedVariantField { ty, .. }) = field { |             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 { |             } else { | ||||||
|                 quote_spanned! {span=> |                 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); |             .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(); |         let variants_len = variants.len(); | ||||||
|         quote_spanned! {span=> |         quote_spanned! {span=> | ||||||
|             #[automatically_derived] |             #[automatically_derived] | ||||||
|  | @ -537,6 +845,7 @@ impl ToTokens for ParsedEnum { | ||||||
|             { |             { | ||||||
|                 type BaseType = ::fayalite::enum_::Enum; |                 type BaseType = ::fayalite::enum_::Enum; | ||||||
|                 type MaskType = ::fayalite::int::Bool; |                 type MaskType = ::fayalite::int::Bool; | ||||||
|  |                 type SimValue = #sim_value_ident #type_generics; | ||||||
|                 type MatchVariant = #match_variant_ident #type_generics; |                 type MatchVariant = #match_variant_ident #type_generics; | ||||||
|                 type MatchActiveScope = ::fayalite::module::Scope; |                 type MatchActiveScope = ::fayalite::module::Scope; | ||||||
|                 type MatchVariantAndInactiveScope = ::fayalite::enum_::EnumMatchVariantAndInactiveScope<Self>; |                 type MatchVariantAndInactiveScope = ::fayalite::enum_::EnumMatchVariantAndInactiveScope<Self>; | ||||||
|  | @ -569,11 +878,41 @@ impl ToTokens for ParsedEnum { | ||||||
|                 fn source_location() -> ::fayalite::source_location::SourceLocation { |                 fn source_location() -> ::fayalite::source_location::SourceLocation { | ||||||
|                     ::fayalite::source_location::SourceLocation::caller() |                     ::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] |             #[automatically_derived] | ||||||
|             impl #impl_generics ::fayalite::enum_::EnumType for #target #type_generics |             impl #impl_generics ::fayalite::enum_::EnumType for #target #type_generics | ||||||
|             #where_clause |             #where_clause | ||||||
|             { |             { | ||||||
|  |                 type SimBuilder = #sim_builder_ident #type_generics; | ||||||
|                 fn match_activate_scope( |                 fn match_activate_scope( | ||||||
|                     v: <Self as ::fayalite::ty::Type>::MatchVariantAndInactiveScope, |                     v: <Self as ::fayalite::ty::Type>::MatchVariantAndInactiveScope, | ||||||
|                 ) -> (<Self as ::fayalite::ty::Type>::MatchVariant, <Self as ::fayalite::ty::Type>::MatchActiveScope) { |                 ) -> (<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); |         .to_tokens(tokens); | ||||||
|         if let (None, MaybeParsed::Parsed(generics)) = (no_static, &self.generics) { |         if let (None, MaybeParsed::Parsed(generics)) = (no_static, &self.generics) { | ||||||
|  | @ -629,6 +995,15 @@ impl ToTokens for ParsedEnum { | ||||||
|                     } |                     } | ||||||
|                 })); |                 })); | ||||||
|             quote_spanned! {span=> |             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] |                 #[automatically_derived] | ||||||
|                 impl #static_impl_generics ::fayalite::ty::StaticType |                 impl #static_impl_generics ::fayalite::ty::StaticType | ||||||
|                 for #target #static_type_generics |                 for #target #static_type_generics | ||||||
|  | @ -647,6 +1022,34 @@ impl ToTokens for ParsedEnum { | ||||||
|                     const MASK_TYPE_PROPERTIES: ::fayalite::ty::TypeProperties = |                     const MASK_TYPE_PROPERTIES: ::fayalite::ty::TypeProperties = | ||||||
|                         <::fayalite::int::Bool as ::fayalite::ty::StaticType>::TYPE_PROPERTIES; |                         <::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); |             .to_tokens(tokens); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|  | @ -49,10 +49,14 @@ impl ParsedTypeAlias { | ||||||
|             custom_bounds, |             custom_bounds, | ||||||
|             no_static, |             no_static, | ||||||
|             no_runtime_generics: _, |             no_runtime_generics: _, | ||||||
|  |             cmp_eq, | ||||||
|         } = options.body; |         } = options.body; | ||||||
|         if let Some((no_static,)) = no_static { |         if let Some((no_static,)) = no_static { | ||||||
|             errors.error(no_static, "no_static is not valid on type aliases"); |             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() { |         let generics = if custom_bounds.is_some() { | ||||||
|             MaybeParsed::Unrecognized(generics) |             MaybeParsed::Unrecognized(generics) | ||||||
|         } else if let Some(generics) = errors.ok(ParsedGenerics::parse(&mut generics)) { |         } else if let Some(generics) = errors.ok(ParsedGenerics::parse(&mut generics)) { | ||||||
|  | @ -95,6 +99,7 @@ impl ToTokens for ParsedTypeAlias { | ||||||
|             custom_bounds: _, |             custom_bounds: _, | ||||||
|             no_static: _, |             no_static: _, | ||||||
|             no_runtime_generics, |             no_runtime_generics, | ||||||
|  |             cmp_eq: _, | ||||||
|         } = &options.body; |         } = &options.body; | ||||||
|         let target = get_target(target, ident); |         let target = get_target(target, ident); | ||||||
|         let mut type_attrs = attrs.clone(); |         let mut type_attrs = attrs.clone(); | ||||||
|  |  | ||||||
|  | @ -26,6 +26,7 @@ crate::options! { | ||||||
|         CustomBounds(custom_bounds), |         CustomBounds(custom_bounds), | ||||||
|         NoStatic(no_static), |         NoStatic(no_static), | ||||||
|         NoRuntimeGenerics(no_runtime_generics), |         NoRuntimeGenerics(no_runtime_generics), | ||||||
|  |         CmpEq(cmp_eq), | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -2069,11 +2070,16 @@ macro_rules! impl_bounds { | ||||||
|             $( |             $( | ||||||
|                 $Variant:ident, |                 $Variant:ident, | ||||||
|             )* |             )* | ||||||
|  |             $( | ||||||
|  |                 #[unknown] | ||||||
|  |                 $Unknown:ident, | ||||||
|  |             )? | ||||||
|         } |         } | ||||||
|     ) => { |     ) => { | ||||||
|         #[derive(Clone, Debug)] |         #[derive(Clone, Debug)] | ||||||
|         $vis enum $enum_type { |         $vis enum $enum_type { | ||||||
|             $($Variant(known_items::$Variant),)* |             $($Variant(known_items::$Variant),)* | ||||||
|  |             $($Unknown(syn::TypeParamBound),)? | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         $(impl From<known_items::$Variant> for $enum_type { |         $(impl From<known_items::$Variant> for $enum_type { | ||||||
|  | @ -2086,28 +2092,54 @@ macro_rules! impl_bounds { | ||||||
|             fn to_tokens(&self, tokens: &mut TokenStream) { |             fn to_tokens(&self, tokens: &mut TokenStream) { | ||||||
|                 match self { |                 match self { | ||||||
|                     $(Self::$Variant(v) => v.to_tokens(tokens),)* |                     $(Self::$Variant(v) => v.to_tokens(tokens),)* | ||||||
|  |                     $(Self::$Unknown(v) => v.to_tokens(tokens),)? | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         impl $enum_type { |         impl $enum_type { | ||||||
|             $vis fn parse_path(path: Path) -> Result<Self, Path> { |             $vis fn parse_path(path: Path) -> Result<Self, Path> { | ||||||
|  |                 #![allow(unreachable_code)] | ||||||
|                 $(let path = match known_items::$Variant::parse_path(path) { |                 $(let path = match known_items::$Variant::parse_path(path) { | ||||||
|                     Ok(v) => return Ok(Self::$Variant(v)), |                     Ok(v) => return Ok(Self::$Variant(v)), | ||||||
|                     Err(path) => path, |                     Err(path) => path, | ||||||
|                 };)* |                 };)* | ||||||
|  |                 $(return Ok(Self::$Unknown(syn::TraitBound { | ||||||
|  |                     paren_token: None, | ||||||
|  |                     modifier: syn::TraitBoundModifier::None, | ||||||
|  |                     lifetimes: None, | ||||||
|  |                     path, | ||||||
|  |                 }.into()));)? | ||||||
|                 Err(path) |                 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 { |         impl Parse for $enum_type { | ||||||
|             fn parse(input: ParseStream) -> syn::Result<Self> { |             fn parse(input: ParseStream) -> syn::Result<Self> { | ||||||
|                 Self::parse_path(Path::parse_mod_style(input)?).map_err(|path| { |                 Self::parse_type_param_bound(input.parse()?) | ||||||
|                     syn::Error::new_spanned( |                     .map_err(|type_param_bound| syn::Error::new_spanned( | ||||||
|                         path, |                         type_param_bound, | ||||||
|                         format_args!("expected one of: {}", [$(stringify!($Variant)),*].join(", ")), |                         format_args!("expected one of: {}", [$(stringify!($Variant)),*].join(", ")), | ||||||
|                     ) |                     )) | ||||||
|                 }) |  | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|  | @ -2115,6 +2147,7 @@ macro_rules! impl_bounds { | ||||||
|         #[allow(non_snake_case)] |         #[allow(non_snake_case)] | ||||||
|         $vis struct $struct_type { |         $vis struct $struct_type { | ||||||
|             $($vis $Variant: Option<known_items::$Variant>,)* |             $($vis $Variant: Option<known_items::$Variant>,)* | ||||||
|  |             $($vis $Unknown: Vec<syn::TypeParamBound>,)? | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         impl ToTokens for $struct_type { |         impl ToTokens for $struct_type { | ||||||
|  | @ -2126,42 +2159,63 @@ macro_rules! impl_bounds { | ||||||
|                     separator = Some(<Token![+]>::default()); |                     separator = Some(<Token![+]>::default()); | ||||||
|                     v.to_tokens(tokens); |                     v.to_tokens(tokens); | ||||||
|                 })* |                 })* | ||||||
|  |                 $(for v in &self.$Unknown { | ||||||
|  |                     separator.to_tokens(tokens); | ||||||
|  |                     separator = Some(<Token![+]>::default()); | ||||||
|  |                     v.to_tokens(tokens); | ||||||
|  |                 })* | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         const _: () = { |         const _: () = { | ||||||
|             #[derive(Clone, Debug)] |             #[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 { |             impl IntoIterator for $struct_type { | ||||||
|                 type Item = $enum_type; |                 type Item = $enum_type; | ||||||
|                 type IntoIter = Iter; |                 type IntoIter = Iter; | ||||||
| 
 | 
 | ||||||
|                 fn into_iter(self) -> Self::IntoIter { |                 fn into_iter(self) -> Self::IntoIter { | ||||||
|                     Iter(self) |                     Iter { | ||||||
|  |                         $($Variant: self.$Variant,)* | ||||||
|  |                         $($Unknown: self.$Unknown.into_iter(),)? | ||||||
|  |                     } | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             impl Iterator for Iter { |             impl Iterator for Iter { | ||||||
|                 type Item = $enum_type; |                 type Item = $enum_type; | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
|                 fn next(&mut self) -> Option<Self::Item> { |                 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)); |                             return Some($enum_type::$Variant(value)); | ||||||
|                         } |                         } | ||||||
|                     )* |                     )* | ||||||
|  |                     $( | ||||||
|  |                         if let Some(value) = self.$Unknown.next() { | ||||||
|  |                             return Some($enum_type::$Unknown(value)); | ||||||
|  |                         } | ||||||
|  |                     )? | ||||||
|                     None |                     None | ||||||
|                 } |                 } | ||||||
| 
 | 
 | ||||||
|                 #[allow(unused_mut, unused_variables)] |                 #[allow(unused_mut, unused_variables)] | ||||||
|                 fn fold<B, F: FnMut(B, Self::Item) -> B>(mut self, mut init: B, mut f: F) -> B { |                 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)); |                             init = f(init, $enum_type::$Variant(value)); | ||||||
|                         } |                         } | ||||||
|                     )* |                     )* | ||||||
|  |                     $( | ||||||
|  |                         if let Some(value) = self.$Unknown.next() { | ||||||
|  |                             init = f(init, $enum_type::$Unknown(value)); | ||||||
|  |                         } | ||||||
|  |                     )? | ||||||
|                     init |                     init | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|  | @ -2173,6 +2227,9 @@ macro_rules! impl_bounds { | ||||||
|                     $($enum_type::$Variant(v) => { |                     $($enum_type::$Variant(v) => { | ||||||
|                         self.$Variant = Some(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 { |                     $(if let Some(v) = v.$Variant { | ||||||
|                         self.$Variant = Some(v); |                         self.$Variant = Some(v); | ||||||
|                     })* |                     })* | ||||||
|  |                     $(self.$Unknown.extend(v.$Unknown);)* | ||||||
|                 }); |                 }); | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|  | @ -2244,6 +2302,8 @@ impl_bounds! { | ||||||
|         Size, |         Size, | ||||||
|         StaticType, |         StaticType, | ||||||
|         Type, |         Type, | ||||||
|  |         #[unknown] | ||||||
|  |         Unknown, | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -2257,6 +2317,8 @@ impl_bounds! { | ||||||
|         ResetType, |         ResetType, | ||||||
|         StaticType, |         StaticType, | ||||||
|         Type, |         Type, | ||||||
|  |         #[unknown] | ||||||
|  |         Unknown, | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -2270,6 +2332,7 @@ impl From<ParsedTypeBound> for ParsedBound { | ||||||
|             ParsedTypeBound::ResetType(v) => ParsedBound::ResetType(v), |             ParsedTypeBound::ResetType(v) => ParsedBound::ResetType(v), | ||||||
|             ParsedTypeBound::StaticType(v) => ParsedBound::StaticType(v), |             ParsedTypeBound::StaticType(v) => ParsedBound::StaticType(v), | ||||||
|             ParsedTypeBound::Type(v) => ParsedBound::Type(v), |             ParsedTypeBound::Type(v) => ParsedBound::Type(v), | ||||||
|  |             ParsedTypeBound::Unknown(v) => ParsedBound::Unknown(v), | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | @ -2284,6 +2347,7 @@ impl From<ParsedTypeBounds> for ParsedBounds { | ||||||
|             ResetType, |             ResetType, | ||||||
|             StaticType, |             StaticType, | ||||||
|             Type, |             Type, | ||||||
|  |             Unknown, | ||||||
|         } = value; |         } = value; | ||||||
|         Self { |         Self { | ||||||
|             BoolOrIntType, |             BoolOrIntType, | ||||||
|  | @ -2295,6 +2359,7 @@ impl From<ParsedTypeBounds> for ParsedBounds { | ||||||
|             Size: None, |             Size: None, | ||||||
|             StaticType, |             StaticType, | ||||||
|             Type, |             Type, | ||||||
|  |             Unknown, | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | @ -2330,6 +2395,7 @@ impl ParsedTypeBound { | ||||||
|                 ParsedTypeBound::Type(known_items::Type(span)), |                 ParsedTypeBound::Type(known_items::Type(span)), | ||||||
|             ]), |             ]), | ||||||
|             Self::Type(v) => ParsedTypeBounds::from_iter([ParsedTypeBound::from(v)]), |             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, |             Size, | ||||||
|             StaticType: None, |             StaticType: None, | ||||||
|             Type: None, |             Type: None, | ||||||
|  |             Unknown: vec![], | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | @ -2391,6 +2458,7 @@ impl ParsedBounds { | ||||||
|     fn categorize(self, errors: &mut Errors, span: Span) -> ParsedBoundsCategory { |     fn categorize(self, errors: &mut Errors, span: Span) -> ParsedBoundsCategory { | ||||||
|         let mut type_bounds = None; |         let mut type_bounds = None; | ||||||
|         let mut size_type_bounds = None; |         let mut size_type_bounds = None; | ||||||
|  |         let mut unknown_bounds = vec![]; | ||||||
|         self.into_iter().for_each(|bound| match bound.categorize() { |         self.into_iter().for_each(|bound| match bound.categorize() { | ||||||
|             ParsedBoundCategory::Type(bound) => { |             ParsedBoundCategory::Type(bound) => { | ||||||
|                 type_bounds |                 type_bounds | ||||||
|  | @ -2402,15 +2470,37 @@ impl ParsedBounds { | ||||||
|                     .get_or_insert_with(ParsedSizeTypeBounds::default) |                     .get_or_insert_with(ParsedSizeTypeBounds::default) | ||||||
|                     .extend([bound]); |                     .extend([bound]); | ||||||
|             } |             } | ||||||
|  |             ParsedBoundCategory::Unknown(bound) => unknown_bounds.push(bound), | ||||||
|         }); |         }); | ||||||
|         match (type_bounds, size_type_bounds) { |         match (type_bounds, size_type_bounds, unknown_bounds.is_empty()) { | ||||||
|             (None, None) => ParsedBoundsCategory::Type(ParsedTypeBounds { |             (None, None, true) => ParsedBoundsCategory::Type(ParsedTypeBounds { | ||||||
|                 Type: Some(known_items::Type(span)), |                 Type: Some(known_items::Type(span)), | ||||||
|                 ..Default::default() |                 ..Default::default() | ||||||
|             }), |             }), | ||||||
|             (None, Some(bounds)) => ParsedBoundsCategory::SizeType(bounds), |             (None, None, false) => { | ||||||
|             (Some(bounds), None) => ParsedBoundsCategory::Type(bounds), |                 errors.error( | ||||||
|             (Some(type_bounds), Some(size_type_bounds)) => { |                     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( |                 errors.error( | ||||||
|                     size_type_bounds |                     size_type_bounds | ||||||
|                         .Size |                         .Size | ||||||
|  | @ -2427,6 +2517,7 @@ impl ParsedBounds { | ||||||
| pub(crate) enum ParsedBoundCategory { | pub(crate) enum ParsedBoundCategory { | ||||||
|     Type(ParsedTypeBound), |     Type(ParsedTypeBound), | ||||||
|     SizeType(ParsedSizeTypeBound), |     SizeType(ParsedSizeTypeBound), | ||||||
|  |     Unknown(syn::TypeParamBound), | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl ParsedBound { | impl ParsedBound { | ||||||
|  | @ -2441,12 +2532,14 @@ impl ParsedBound { | ||||||
|             Self::Size(v) => ParsedBoundCategory::SizeType(ParsedSizeTypeBound::Size(v)), |             Self::Size(v) => ParsedBoundCategory::SizeType(ParsedSizeTypeBound::Size(v)), | ||||||
|             Self::StaticType(v) => ParsedBoundCategory::Type(ParsedTypeBound::StaticType(v)), |             Self::StaticType(v) => ParsedBoundCategory::Type(ParsedTypeBound::StaticType(v)), | ||||||
|             Self::Type(v) => ParsedBoundCategory::Type(ParsedTypeBound::Type(v)), |             Self::Type(v) => ParsedBoundCategory::Type(ParsedTypeBound::Type(v)), | ||||||
|  |             Self::Unknown(v) => ParsedBoundCategory::Unknown(v), | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|     fn implied_bounds(self) -> ParsedBounds { |     fn implied_bounds(self) -> ParsedBounds { | ||||||
|         match self.categorize() { |         match self.categorize() { | ||||||
|             ParsedBoundCategory::Type(v) => v.implied_bounds().into(), |             ParsedBoundCategory::Type(v) => v.implied_bounds().into(), | ||||||
|             ParsedBoundCategory::SizeType(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::EnumType(_) | ||||||
|                                 | ParsedTypeBound::IntType(_) |                                 | ParsedTypeBound::IntType(_) | ||||||
|                                 | ParsedTypeBound::ResetType(_) => { |                                 | ParsedTypeBound::ResetType(_) => { | ||||||
|                                     errors.error(bound, "bound on mask type not implemented"); |                                     errors.error(bound, "bounds on mask types are not implemented"); | ||||||
|                                 } |                                 } | ||||||
|                                 ParsedTypeBound::StaticType(bound) => { |                                 ParsedTypeBound::StaticType(bound) => { | ||||||
|                                     if bounds.StaticType.is_none() { |                                     if bounds.StaticType.is_none() { | ||||||
|  | @ -3337,6 +3430,12 @@ impl ParsedGenerics { | ||||||
|                                     } |                                     } | ||||||
|                                 } |                                 } | ||||||
|                                 ParsedTypeBound::Type(_) => {} |                                 ParsedTypeBound::Type(_) => {} | ||||||
|  |                                 ParsedTypeBound::Unknown(_) => { | ||||||
|  |                                     errors.error( | ||||||
|  |                                         bound, | ||||||
|  |                                         "unknown bounds on mask types are not implemented", | ||||||
|  |                                     ); | ||||||
|  |                                 } | ||||||
|                             } |                             } | ||||||
|                         } |                         } | ||||||
|                         bounds.add_implied_bounds(); |                         bounds.add_implied_bounds(); | ||||||
|  |  | ||||||
|  | @ -72,13 +72,14 @@ mod kw { | ||||||
|     custom_keyword!(cfg); |     custom_keyword!(cfg); | ||||||
|     custom_keyword!(cfg_attr); |     custom_keyword!(cfg_attr); | ||||||
|     custom_keyword!(clock_domain); |     custom_keyword!(clock_domain); | ||||||
|  |     custom_keyword!(cmp_eq); | ||||||
|     custom_keyword!(connect_inexact); |     custom_keyword!(connect_inexact); | ||||||
|     custom_keyword!(custom_bounds); |     custom_keyword!(custom_bounds); | ||||||
|     custom_keyword!(flip); |     custom_keyword!(flip); | ||||||
|     custom_keyword!(hdl); |     custom_keyword!(hdl); | ||||||
|     custom_keyword!(hdl_module); |     custom_keyword!(hdl_module); | ||||||
|     custom_keyword!(input); |  | ||||||
|     custom_keyword!(incomplete_wire); |     custom_keyword!(incomplete_wire); | ||||||
|  |     custom_keyword!(input); | ||||||
|     custom_keyword!(instance); |     custom_keyword!(instance); | ||||||
|     custom_keyword!(m); |     custom_keyword!(m); | ||||||
|     custom_keyword!(memory); |     custom_keyword!(memory); | ||||||
|  | @ -92,6 +93,7 @@ mod kw { | ||||||
|     custom_keyword!(output); |     custom_keyword!(output); | ||||||
|     custom_keyword!(reg_builder); |     custom_keyword!(reg_builder); | ||||||
|     custom_keyword!(reset); |     custom_keyword!(reset); | ||||||
|  |     custom_keyword!(sim); | ||||||
|     custom_keyword!(skip); |     custom_keyword!(skip); | ||||||
|     custom_keyword!(target); |     custom_keyword!(target); | ||||||
|     custom_keyword!(wire); |     custom_keyword!(wire); | ||||||
|  |  | ||||||
|  | @ -377,7 +377,7 @@ impl ModuleFn { | ||||||
|             module_kind, |             module_kind, | ||||||
|             vis, |             vis, | ||||||
|             sig, |             sig, | ||||||
|             block, |             mut block, | ||||||
|             struct_generics, |             struct_generics, | ||||||
|             the_struct, |             the_struct, | ||||||
|         } = match self.0 { |         } = match self.0 { | ||||||
|  | @ -439,6 +439,12 @@ impl ModuleFn { | ||||||
|         body_sig |         body_sig | ||||||
|             .inputs |             .inputs | ||||||
|             .insert(0, parse_quote! { m: &::fayalite::module::ModuleBuilder }); |             .insert(0, parse_quote! { m: &::fayalite::module::ModuleBuilder }); | ||||||
|  |         block.stmts.insert( | ||||||
|  |             0, | ||||||
|  |             parse_quote! { | ||||||
|  |                 let _ = m; | ||||||
|  |             }, | ||||||
|  |         ); | ||||||
|         let body_fn = ItemFn { |         let body_fn = ItemFn { | ||||||
|             attrs: vec![], |             attrs: vec![], | ||||||
|             vis: Visibility::Inherited, |             vis: Visibility::Inherited, | ||||||
|  |  | ||||||
|  | @ -16,7 +16,7 @@ use std::{borrow::Borrow, convert::Infallible}; | ||||||
| use syn::{ | use syn::{ | ||||||
|     fold::{fold_expr, fold_expr_lit, fold_expr_unary, fold_local, fold_stmt, Fold}, |     fold::{fold_expr, fold_expr_lit, fold_expr_unary, fold_local, fold_stmt, Fold}, | ||||||
|     parenthesized, |     parenthesized, | ||||||
|     parse::{Nothing, Parse, ParseStream}, |     parse::{Parse, ParseStream}, | ||||||
|     parse_quote, parse_quote_spanned, |     parse_quote, parse_quote_spanned, | ||||||
|     spanned::Spanned, |     spanned::Spanned, | ||||||
|     token::Paren, |     token::Paren, | ||||||
|  | @ -27,6 +27,13 @@ use syn::{ | ||||||
| mod expand_aggregate_literals; | mod expand_aggregate_literals; | ||||||
| mod expand_match; | mod expand_match; | ||||||
| 
 | 
 | ||||||
|  | options! { | ||||||
|  |     #[options = ExprOptions] | ||||||
|  |     pub(crate) enum ExprOption { | ||||||
|  |         Sim(sim), | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
| options! { | options! { | ||||||
|     pub(crate) enum LetFnKind { |     pub(crate) enum LetFnKind { | ||||||
|         Input(input), |         Input(input), | ||||||
|  | @ -952,7 +959,7 @@ with_debug_clone_and_fold! { | ||||||
|     #[allow(dead_code)] |     #[allow(dead_code)] | ||||||
|     pub(crate) struct HdlLet<Kind = HdlLetKind> { |     pub(crate) struct HdlLet<Kind = HdlLetKind> { | ||||||
|         pub(crate) attrs: Vec<Attribute>, |         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) let_token: Token![let], | ||||||
|         pub(crate) mut_token: Option<Token![mut]>, |         pub(crate) mut_token: Option<Token![mut]>, | ||||||
|         pub(crate) name: Ident, |         pub(crate) name: Ident, | ||||||
|  | @ -1173,7 +1180,7 @@ impl Visitor<'_> { | ||||||
|             Some(_) => {} |             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 { |         let ExprIf { | ||||||
|             attrs, |             attrs, | ||||||
|             if_token, |             if_token, | ||||||
|  | @ -1181,10 +1188,10 @@ impl Visitor<'_> { | ||||||
|             then_branch, |             then_branch, | ||||||
|             else_branch, |             else_branch, | ||||||
|         } = expr_if; |         } = expr_if; | ||||||
|         self.require_normal_module_or_fn(if_token); |         let (else_token, else_expr) = else_branch.unzip(); | ||||||
|         let else_expr = else_branch.unzip().1.map(|else_expr| match *else_expr { |         let else_expr = else_expr.map(|else_expr| match *else_expr { | ||||||
|             Expr::If(expr_if) => self.process_hdl_if(hdl_attr.clone(), expr_if), |             Expr::If(expr_if) => Box::new(self.process_hdl_if(hdl_attr.clone(), expr_if)), | ||||||
|             expr => expr, |             _ => else_expr, | ||||||
|         }); |         }); | ||||||
|         if let Expr::Let(ExprLet { |         if let Expr::Let(ExprLet { | ||||||
|             attrs: let_attrs, |             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=> |             parse_quote_spanned! {if_token.span=> | ||||||
|                 #(#attrs)* |                 #(#attrs)* | ||||||
|                 { |                 { | ||||||
|  | @ -1668,6 +1687,8 @@ impl Fold for Visitor<'_> { | ||||||
|                 Repeat => process_hdl_repeat, |                 Repeat => process_hdl_repeat, | ||||||
|                 Struct => process_hdl_struct, |                 Struct => process_hdl_struct, | ||||||
|                 Tuple => process_hdl_tuple, |                 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 { |     fn fold_local(&mut self, mut let_stmt: Local) -> Local { | ||||||
|         match self |         match self | ||||||
|             .errors |             .errors | ||||||
|             .ok(HdlAttr::<Nothing, kw::hdl>::parse_and_leave_attr( |             .ok(HdlAttr::<ExprOptions, kw::hdl>::parse_and_leave_attr( | ||||||
|                 &let_stmt.attrs, |                 &let_stmt.attrs, | ||||||
|             )) { |             )) { | ||||||
|             None => return empty_let(), |             None => return empty_let(), | ||||||
|  | @ -1694,10 +1715,11 @@ impl Fold for Visitor<'_> { | ||||||
|             subpat: None, |             subpat: None, | ||||||
|         }) = pat |         }) = pat | ||||||
|         else { |         else { | ||||||
|             let hdl_attr = HdlAttr::<Nothing, kw::hdl>::parse_and_take_attr(&mut let_stmt.attrs) |             let hdl_attr = | ||||||
|                 .ok() |                 HdlAttr::<ExprOptions, kw::hdl>::parse_and_take_attr(&mut let_stmt.attrs) | ||||||
|                 .flatten() |                     .ok() | ||||||
|                 .expect("already checked above"); |                     .flatten() | ||||||
|  |                     .expect("already checked above"); | ||||||
|             let let_stmt = fold_local(self, let_stmt); |             let let_stmt = fold_local(self, let_stmt); | ||||||
|             return self.process_hdl_let_pat(hdl_attr, let_stmt); |             return self.process_hdl_let_pat(hdl_attr, let_stmt); | ||||||
|         }; |         }; | ||||||
|  |  | ||||||
|  | @ -1,45 +1,103 @@ | ||||||
| // SPDX-License-Identifier: LGPL-3.0-or-later
 | // SPDX-License-Identifier: LGPL-3.0-or-later
 | ||||||
| // See Notices.txt for copyright information
 | // 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 quote::{format_ident, quote_spanned}; | ||||||
|  | use std::mem; | ||||||
| use syn::{ | use syn::{ | ||||||
|     parse::Nothing, parse_quote, parse_quote_spanned, spanned::Spanned, Expr, ExprArray, ExprPath, |     parse_quote_spanned, punctuated::Punctuated, spanned::Spanned, token::Paren, Expr, ExprArray, | ||||||
|     ExprRepeat, ExprStruct, ExprTuple, FieldValue, TypePath, |     ExprCall, ExprGroup, ExprMethodCall, ExprParen, ExprPath, ExprRepeat, ExprStruct, ExprTuple, | ||||||
|  |     FieldValue, Token, TypePath, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| impl Visitor<'_> { | impl Visitor<'_> { | ||||||
|     pub(crate) fn process_hdl_array( |     pub(crate) fn process_hdl_array( | ||||||
|         &mut self, |         &mut self, | ||||||
|         hdl_attr: HdlAttr<Nothing, kw::hdl>, |         hdl_attr: HdlAttr<ExprOptions, kw::hdl>, | ||||||
|         mut expr_array: ExprArray, |         mut expr_array: ExprArray, | ||||||
|     ) -> Expr { |     ) -> Expr { | ||||||
|         self.require_normal_module_or_fn(hdl_attr); |         let ExprOptions { sim } = hdl_attr.body; | ||||||
|         for elem in &mut expr_array.elems { |         let span = hdl_attr.kw.span; | ||||||
|             *elem = parse_quote_spanned! {elem.span()=> |         if sim.is_some() { | ||||||
|                 ::fayalite::expr::ToExpr::to_expr(&(#elem)) |             for elem in &mut expr_array.elems { | ||||||
|             }; |                 *elem = parse_quote_spanned! {elem.span()=> | ||||||
|  |                     ::fayalite::sim::value::ToSimValue::to_sim_value(&(#elem)) | ||||||
|  |                 }; | ||||||
|  |             } | ||||||
|  |             parse_quote_spanned! {span=> | ||||||
|  |                 ::fayalite::sim::value::ToSimValue::into_sim_value(#expr_array) | ||||||
|  |             } | ||||||
|  |         } else { | ||||||
|  |             for elem in &mut expr_array.elems { | ||||||
|  |                 *elem = parse_quote_spanned! {elem.span()=> | ||||||
|  |                     ::fayalite::expr::ToExpr::to_expr(&(#elem)) | ||||||
|  |                 }; | ||||||
|  |             } | ||||||
|  |             parse_quote_spanned! {span=> | ||||||
|  |                 ::fayalite::expr::ToExpr::to_expr(&#expr_array) | ||||||
|  |             } | ||||||
|         } |         } | ||||||
|         parse_quote! {::fayalite::expr::ToExpr::to_expr(&#expr_array)} |  | ||||||
|     } |     } | ||||||
|     pub(crate) fn process_hdl_repeat( |     pub(crate) fn process_hdl_repeat( | ||||||
|         &mut self, |         &mut self, | ||||||
|         hdl_attr: HdlAttr<Nothing, kw::hdl>, |         hdl_attr: HdlAttr<ExprOptions, kw::hdl>, | ||||||
|         mut expr_repeat: ExprRepeat, |         mut expr_repeat: ExprRepeat, | ||||||
|     ) -> Expr { |     ) -> Expr { | ||||||
|         self.require_normal_module_or_fn(hdl_attr); |  | ||||||
|         let repeated_value = &expr_repeat.expr; |         let repeated_value = &expr_repeat.expr; | ||||||
|         *expr_repeat.expr = parse_quote_spanned! {repeated_value.span()=> |         let ExprOptions { sim } = hdl_attr.body; | ||||||
|             ::fayalite::expr::ToExpr::to_expr(&(#repeated_value)) |         let span = hdl_attr.kw.span; | ||||||
|         }; |         if sim.is_some() { | ||||||
|         parse_quote! {::fayalite::expr::ToExpr::to_expr(&#expr_repeat)} |             *expr_repeat.expr = parse_quote_spanned! {repeated_value.span()=> | ||||||
|  |                 ::fayalite::sim::value::ToSimValue::to_sim_value(&(#repeated_value)) | ||||||
|  |             }; | ||||||
|  |             parse_quote_spanned! {span=> | ||||||
|  |                 ::fayalite::sim::value::ToSimValue::into_sim_value(#expr_repeat) | ||||||
|  |             } | ||||||
|  |         } else { | ||||||
|  |             *expr_repeat.expr = parse_quote_spanned! {repeated_value.span()=> | ||||||
|  |                 ::fayalite::expr::ToExpr::to_expr(&(#repeated_value)) | ||||||
|  |             }; | ||||||
|  |             parse_quote_spanned! {span=> | ||||||
|  |                 ::fayalite::expr::ToExpr::to_expr(&#expr_repeat) | ||||||
|  |             } | ||||||
|  |         } | ||||||
|     } |     } | ||||||
|     pub(crate) fn process_hdl_struct( |     pub(crate) fn process_hdl_struct( | ||||||
|         &mut self, |         &mut self, | ||||||
|         hdl_attr: HdlAttr<Nothing, kw::hdl>, |         hdl_attr: HdlAttr<ExprOptions, kw::hdl>, | ||||||
|         expr_struct: ExprStruct, |         mut expr_struct: ExprStruct, | ||||||
|     ) -> Expr { |     ) -> Expr { | ||||||
|         self.require_normal_module_or_fn(&hdl_attr); |  | ||||||
|         let name_span = expr_struct.path.segments.last().unwrap().ident.span(); |         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 builder_ident = format_ident!("__builder", span = name_span); | ||||||
|         let empty_builder = if expr_struct.qself.is_some() |         let empty_builder = if expr_struct.qself.is_some() | ||||||
|             || expr_struct |             || expr_struct | ||||||
|  | @ -91,12 +149,126 @@ impl Visitor<'_> { | ||||||
|     } |     } | ||||||
|     pub(crate) fn process_hdl_tuple( |     pub(crate) fn process_hdl_tuple( | ||||||
|         &mut self, |         &mut self, | ||||||
|         hdl_attr: HdlAttr<Nothing, kw::hdl>, |         hdl_attr: HdlAttr<ExprOptions, kw::hdl>, | ||||||
|         expr_tuple: ExprTuple, |         mut expr_tuple: ExprTuple, | ||||||
|     ) -> Expr { |     ) -> Expr { | ||||||
|         self.require_normal_module_or_fn(hdl_attr); |         let ExprOptions { sim } = hdl_attr.body; | ||||||
|         parse_quote_spanned! {expr_tuple.span()=> |         if sim.is_some() { | ||||||
|             ::fayalite::expr::ToExpr::to_expr(&#expr_tuple) |             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::{ | use crate::{ | ||||||
|     fold::{impl_fold, DoFold}, |     fold::{impl_fold, DoFold}, | ||||||
|     kw, |     kw, | ||||||
|     module::transform_body::{empty_let, with_debug_clone_and_fold, wrap_ty_with_expr, Visitor}, |     module::transform_body::{ | ||||||
|  |         empty_let, with_debug_clone_and_fold, wrap_ty_with_expr, ExprOptions, Visitor, | ||||||
|  |     }, | ||||||
|     Errors, HdlAttr, PairsIterExt, |     Errors, HdlAttr, PairsIterExt, | ||||||
| }; | }; | ||||||
| use proc_macro2::{Span, TokenStream}; | use proc_macro2::{Span, TokenStream}; | ||||||
|  | @ -11,7 +13,6 @@ use quote::{format_ident, quote_spanned, ToTokens, TokenStreamExt}; | ||||||
| use std::collections::BTreeSet; | use std::collections::BTreeSet; | ||||||
| use syn::{ | use syn::{ | ||||||
|     fold::{fold_arm, fold_expr_match, fold_local, fold_pat, Fold}, |     fold::{fold_arm, fold_expr_match, fold_local, fold_pat, Fold}, | ||||||
|     parse::Nothing, |  | ||||||
|     parse_quote_spanned, |     parse_quote_spanned, | ||||||
|     punctuated::Punctuated, |     punctuated::Punctuated, | ||||||
|     spanned::Spanned, |     spanned::Spanned, | ||||||
|  | @ -82,7 +83,14 @@ visit_trait! { | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|     fn visit_match_pat_enum_variant(state: _, v: &MatchPatEnumVariant) { |     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 { |         if let Some((_, v)) = field { | ||||||
|             state.visit_match_pat_simple(v); |             state.visit_match_pat_simple(v); | ||||||
|         } |         } | ||||||
|  | @ -292,6 +300,7 @@ impl ToTokens for MatchPatTuple { | ||||||
| with_debug_clone_and_fold! { | with_debug_clone_and_fold! { | ||||||
|     struct MatchPatEnumVariant<> { |     struct MatchPatEnumVariant<> { | ||||||
|         match_span: Span, |         match_span: Span, | ||||||
|  |         sim: Option<(kw::sim,)>, | ||||||
|         variant_path: Path, |         variant_path: Path, | ||||||
|         enum_path: Path, |         enum_path: Path, | ||||||
|         variant_name: Ident, |         variant_name: Ident, | ||||||
|  | @ -303,6 +312,7 @@ impl ToTokens for MatchPatEnumVariant { | ||||||
|     fn to_tokens(&self, tokens: &mut TokenStream) { |     fn to_tokens(&self, tokens: &mut TokenStream) { | ||||||
|         let Self { |         let Self { | ||||||
|             match_span, |             match_span, | ||||||
|  |             sim, | ||||||
|             variant_path: _, |             variant_path: _, | ||||||
|             enum_path, |             enum_path, | ||||||
|             variant_name, |             variant_name, | ||||||
|  | @ -312,7 +322,28 @@ impl ToTokens for MatchPatEnumVariant { | ||||||
|             __MatchTy::<#enum_path>::#variant_name |             __MatchTy::<#enum_path>::#variant_name | ||||||
|         } |         } | ||||||
|         .to_tokens(tokens); |         .to_tokens(tokens); | ||||||
|         if let Some((paren_token, field)) = field { |         if sim.is_some() { | ||||||
|  |             if let Some((paren_token, field)) = field { | ||||||
|  |                 paren_token.surround(tokens, |tokens| { | ||||||
|  |                     field.to_tokens(tokens); | ||||||
|  |                     match field { | ||||||
|  |                         MatchPatSimple::Paren(_) | ||||||
|  |                         | MatchPatSimple::Or(_) | ||||||
|  |                         | MatchPatSimple::Binding(_) | ||||||
|  |                         | MatchPatSimple::Wild(_) => quote_spanned! {*match_span=> | ||||||
|  |                             , _ | ||||||
|  |                         } | ||||||
|  |                         .to_tokens(tokens), | ||||||
|  |                         MatchPatSimple::Rest(_) => {} | ||||||
|  |                     } | ||||||
|  |                 }); | ||||||
|  |             } else { | ||||||
|  |                 quote_spanned! {*match_span=> | ||||||
|  |                     (_) | ||||||
|  |                 } | ||||||
|  |                 .to_tokens(tokens); | ||||||
|  |             } | ||||||
|  |         } else if let Some((paren_token, field)) = field { | ||||||
|             paren_token.surround(tokens, |tokens| field.to_tokens(tokens)); |             paren_token.surround(tokens, |tokens| field.to_tokens(tokens)); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  | @ -349,13 +380,13 @@ impl ToTokens for MatchPatSimple { | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| struct EnumPath { | pub(crate) struct EnumPath { | ||||||
|     variant_path: Path, |     pub(crate) variant_path: Path, | ||||||
|     enum_path: Path, |     pub(crate) enum_path: Path, | ||||||
|     variant_name: Ident, |     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 { |     let TypePath { | ||||||
|         qself: None, |         qself: None, | ||||||
|         path: variant_path, |         path: variant_path, | ||||||
|  | @ -447,6 +478,7 @@ trait ParseMatchPat: Sized { | ||||||
|                         state, |                         state, | ||||||
|                         MatchPatEnumVariant { |                         MatchPatEnumVariant { | ||||||
|                             match_span: state.match_span, |                             match_span: state.match_span, | ||||||
|  |                             sim: state.sim, | ||||||
|                             variant_path, |                             variant_path, | ||||||
|                             enum_path, |                             enum_path, | ||||||
|                             variant_name, |                             variant_name, | ||||||
|  | @ -493,6 +525,7 @@ trait ParseMatchPat: Sized { | ||||||
|                     state, |                     state, | ||||||
|                     MatchPatEnumVariant { |                     MatchPatEnumVariant { | ||||||
|                         match_span: state.match_span, |                         match_span: state.match_span, | ||||||
|  |                         sim: state.sim, | ||||||
|                         variant_path, |                         variant_path, | ||||||
|                         enum_path, |                         enum_path, | ||||||
|                         variant_name, |                         variant_name, | ||||||
|  | @ -577,6 +610,7 @@ trait ParseMatchPat: Sized { | ||||||
|                     state, |                     state, | ||||||
|                     MatchPatEnumVariant { |                     MatchPatEnumVariant { | ||||||
|                         match_span: state.match_span, |                         match_span: state.match_span, | ||||||
|  |                         sim: state.sim, | ||||||
|                         variant_path, |                         variant_path, | ||||||
|                         enum_path, |                         enum_path, | ||||||
|                         variant_name, |                         variant_name, | ||||||
|  | @ -939,6 +973,7 @@ impl Fold for RewriteAsCheckMatch { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| struct HdlMatchParseState<'a> { | struct HdlMatchParseState<'a> { | ||||||
|  |     sim: Option<(kw::sim,)>, | ||||||
|     match_span: Span, |     match_span: Span, | ||||||
|     errors: &'a mut Errors, |     errors: &'a mut Errors, | ||||||
| } | } | ||||||
|  | @ -981,10 +1016,11 @@ impl<'a> VisitMatchPat<'a> for HdlLetPatVisitState<'a> { | ||||||
| impl Visitor<'_> { | impl Visitor<'_> { | ||||||
|     pub(crate) fn process_hdl_let_pat( |     pub(crate) fn process_hdl_let_pat( | ||||||
|         &mut self, |         &mut self, | ||||||
|         _hdl_attr: HdlAttr<Nothing, kw::hdl>, |         hdl_attr: HdlAttr<ExprOptions, kw::hdl>, | ||||||
|         mut let_stmt: Local, |         mut let_stmt: Local, | ||||||
|     ) -> Local { |     ) -> Local { | ||||||
|         let span = let_stmt.let_token.span(); |         let span = let_stmt.let_token.span(); | ||||||
|  |         let ExprOptions { sim } = hdl_attr.body; | ||||||
|         if let Pat::Type(pat) = &mut let_stmt.pat { |         if let Pat::Type(pat) = &mut let_stmt.pat { | ||||||
|             *pat.ty = wrap_ty_with_expr((*pat.ty).clone()); |             *pat.ty = wrap_ty_with_expr((*pat.ty).clone()); | ||||||
|         } |         } | ||||||
|  | @ -996,7 +1032,6 @@ impl Visitor<'_> { | ||||||
|             init, |             init, | ||||||
|             semi_token, |             semi_token, | ||||||
|         } = let_stmt; |         } = let_stmt; | ||||||
|         self.require_normal_module_or_fn(let_token); |  | ||||||
|         let Some(syn::LocalInit { |         let Some(syn::LocalInit { | ||||||
|             eq_token, |             eq_token, | ||||||
|             expr, |             expr, | ||||||
|  | @ -1015,6 +1050,7 @@ impl Visitor<'_> { | ||||||
|         } |         } | ||||||
|         let Ok(pat) = MatchPat::parse( |         let Ok(pat) = MatchPat::parse( | ||||||
|             &mut HdlMatchParseState { |             &mut HdlMatchParseState { | ||||||
|  |                 sim, | ||||||
|                 match_span: span, |                 match_span: span, | ||||||
|                 errors: &mut self.errors, |                 errors: &mut self.errors, | ||||||
|             }, |             }, | ||||||
|  | @ -1031,29 +1067,47 @@ impl Visitor<'_> { | ||||||
|             errors: _, |             errors: _, | ||||||
|             bindings, |             bindings, | ||||||
|         } = state; |         } = state; | ||||||
|         let retval = parse_quote_spanned! {span=> |         let retval = if sim.is_some() { | ||||||
|             let (#(#bindings,)* __scope,) = { |             parse_quote_spanned! {span=> | ||||||
|                 type __MatchTy<T> = <T as ::fayalite::ty::Type>::MatchVariant; |                 let (#(#bindings,)*) = { | ||||||
|                 let __match_expr = ::fayalite::expr::ToExpr::to_expr(&(#expr)); |                     type __MatchTy<T> = <T as ::fayalite::ty::Type>::SimValue; | ||||||
|                 ::fayalite::expr::check_match_expr(__match_expr, |__match_value, __infallible| { |                     let __match_value = ::fayalite::sim::value::ToSimValue::to_sim_value(&(#expr)); | ||||||
|                     #[allow(unused_variables)] |                     #let_token #pat #eq_token ::fayalite::sim::value::SimValue::into_value(__match_value) #semi_token | ||||||
|                     #check_let_stmt |                     (#(#bindings,)*) | ||||||
|                     match __infallible {} |  | ||||||
|                 }); |  | ||||||
|                 let mut __match_iter = ::fayalite::module::match_(__match_expr); |  | ||||||
|                 let ::fayalite::__std::option::Option::Some(__match_variant) = ::fayalite::__std::iter::Iterator::next(&mut __match_iter) else { |  | ||||||
|                     ::fayalite::__std::unreachable!("#[hdl] let with uninhabited type"); |  | ||||||
|                 }; |                 }; | ||||||
|                 let ::fayalite::__std::option::Option::None = ::fayalite::__std::iter::Iterator::next(&mut __match_iter) else { |             } | ||||||
|                     ::fayalite::__std::unreachable!("#[hdl] let with refutable pattern"); |         } else { | ||||||
|                 }; |             parse_quote_spanned! {span=> | ||||||
|                 let (__match_variant, __scope) = |                 let (#(#bindings,)* __scope,) = { | ||||||
|                     ::fayalite::ty::MatchVariantAndInactiveScope::match_activate_scope( |                     type __MatchTy<T> = <T as ::fayalite::ty::Type>::MatchVariant; | ||||||
|                         __match_variant, |                     let __match_expr = ::fayalite::expr::ToExpr::to_expr(&(#expr)); | ||||||
|  |                     ::fayalite::expr::check_match_expr( | ||||||
|  |                         __match_expr, | ||||||
|  |                         |__match_value, __infallible| { | ||||||
|  |                             #[allow(unused_variables)] | ||||||
|  |                             #check_let_stmt | ||||||
|  |                             match __infallible {} | ||||||
|  |                         }, | ||||||
|                     ); |                     ); | ||||||
|                 #let_token #pat #eq_token __match_variant #semi_token |                     let mut __match_iter = ::fayalite::module::match_(__match_expr); | ||||||
|                 (#(#bindings,)* __scope,) |                     let ::fayalite::__std::option::Option::Some(__match_variant) = | ||||||
|             }; |                         ::fayalite::__std::iter::Iterator::next(&mut __match_iter) | ||||||
|  |                     else { | ||||||
|  |                         ::fayalite::__std::unreachable!("#[hdl] let with uninhabited type"); | ||||||
|  |                     }; | ||||||
|  |                     let ::fayalite::__std::option::Option::None = | ||||||
|  |                         ::fayalite::__std::iter::Iterator::next(&mut __match_iter) | ||||||
|  |                     else { | ||||||
|  |                         ::fayalite::__std::unreachable!("#[hdl] let with refutable pattern"); | ||||||
|  |                     }; | ||||||
|  |                     let (__match_variant, __scope) = | ||||||
|  |                         ::fayalite::ty::MatchVariantAndInactiveScope::match_activate_scope( | ||||||
|  |                             __match_variant, | ||||||
|  |                         ); | ||||||
|  |                     #let_token #pat #eq_token __match_variant #semi_token | ||||||
|  |                     (#(#bindings,)* __scope,) | ||||||
|  |                 }; | ||||||
|  |             } | ||||||
|         }; |         }; | ||||||
|         match retval { |         match retval { | ||||||
|             syn::Stmt::Local(retval) => retval, |             syn::Stmt::Local(retval) => retval, | ||||||
|  | @ -1062,7 +1116,7 @@ impl Visitor<'_> { | ||||||
|     } |     } | ||||||
|     pub(crate) fn process_hdl_match( |     pub(crate) fn process_hdl_match( | ||||||
|         &mut self, |         &mut self, | ||||||
|         _hdl_attr: HdlAttr<Nothing, kw::hdl>, |         hdl_attr: HdlAttr<ExprOptions, kw::hdl>, | ||||||
|         expr_match: ExprMatch, |         expr_match: ExprMatch, | ||||||
|     ) -> Expr { |     ) -> Expr { | ||||||
|         let span = expr_match.match_token.span(); |         let span = expr_match.match_token.span(); | ||||||
|  | @ -1074,8 +1128,9 @@ impl Visitor<'_> { | ||||||
|             brace_token: _, |             brace_token: _, | ||||||
|             arms, |             arms, | ||||||
|         } = expr_match; |         } = expr_match; | ||||||
|         self.require_normal_module_or_fn(match_token); |         let ExprOptions { sim } = hdl_attr.body; | ||||||
|         let mut state = HdlMatchParseState { |         let mut state = HdlMatchParseState { | ||||||
|  |             sim, | ||||||
|             match_span: span, |             match_span: span, | ||||||
|             errors: &mut self.errors, |             errors: &mut self.errors, | ||||||
|         }; |         }; | ||||||
|  | @ -1083,24 +1138,36 @@ impl Visitor<'_> { | ||||||
|             arms.into_iter() |             arms.into_iter() | ||||||
|                 .filter_map(|arm| MatchArm::parse(&mut state, arm).ok()), |                 .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>::MatchVariant; |                 { | ||||||
|                 let __match_expr = ::fayalite::expr::ToExpr::to_expr(&(#expr)); |                     type __MatchTy<T> = <T as ::fayalite::ty::Type>::SimValue; | ||||||
|                 ::fayalite::expr::check_match_expr(__match_expr, |__match_value, __infallible| { |                     let __match_expr = ::fayalite::sim::value::ToSimValue::to_sim_value(&(#expr)); | ||||||
|                     #[allow(unused_variables)] |                     #match_token ::fayalite::sim::value::SimValue::into_value(__match_expr) { | ||||||
|                     #check_match |  | ||||||
|                 }); |  | ||||||
|                 for __match_variant in ::fayalite::module::match_(__match_expr) { |  | ||||||
|                     let (__match_variant, __scope) = |  | ||||||
|                         ::fayalite::ty::MatchVariantAndInactiveScope::match_activate_scope( |  | ||||||
|                             __match_variant, |  | ||||||
|                         ); |  | ||||||
|                     #match_token __match_variant { |  | ||||||
|                         #(#arms)* |                         #(#arms)* | ||||||
|                     } |                     } | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|  |         } else { | ||||||
|  |             quote_spanned! {span=> | ||||||
|  |                 { | ||||||
|  |                     type __MatchTy<T> = <T as ::fayalite::ty::Type>::MatchVariant; | ||||||
|  |                     let __match_expr = ::fayalite::expr::ToExpr::to_expr(&(#expr)); | ||||||
|  |                     ::fayalite::expr::check_match_expr(__match_expr, |__match_value, __infallible| { | ||||||
|  |                         #[allow(unused_variables)] | ||||||
|  |                         #check_match | ||||||
|  |                     }); | ||||||
|  |                     for __match_variant in ::fayalite::module::match_(__match_expr) { | ||||||
|  |                         let (__match_variant, __scope) = | ||||||
|  |                             ::fayalite::ty::MatchVariantAndInactiveScope::match_activate_scope( | ||||||
|  |                                 __match_variant, | ||||||
|  |                             ); | ||||||
|  |                         #match_token __match_variant { | ||||||
|  |                             #(#arms)* | ||||||
|  |                         } | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|         }; |         }; | ||||||
|         syn::parse2(expr).unwrap() |         syn::parse2(expr).unwrap() | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  | @ -40,6 +40,7 @@ fayalite-visit-gen.workspace = true | ||||||
| 
 | 
 | ||||||
| [features] | [features] | ||||||
| unstable-doc = [] | unstable-doc = [] | ||||||
|  | unstable-test-hasher = [] | ||||||
| 
 | 
 | ||||||
| [package.metadata.docs.rs] | [package.metadata.docs.rs] | ||||||
| features = ["unstable-doc"] | features = ["unstable-doc"] | ||||||
|  |  | ||||||
|  | @ -12,7 +12,7 @@ use std::{ | ||||||
|     ops::Deref, |     ops::Deref, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| #[derive(Clone)] | #[derive(Clone, Debug)] | ||||||
| struct CustomFirrtlAnnotationFieldsImpl { | struct CustomFirrtlAnnotationFieldsImpl { | ||||||
|     value: serde_json::Map<String, serde_json::Value>, |     value: serde_json::Map<String, serde_json::Value>, | ||||||
|     serialized: Interned<str>, |     serialized: Interned<str>, | ||||||
|  |  | ||||||
|  | @ -2,17 +2,24 @@ | ||||||
| // See Notices.txt for copyright information
 | // See Notices.txt for copyright information
 | ||||||
| 
 | 
 | ||||||
| use crate::{ | use crate::{ | ||||||
|     expr::{ops::ArrayIndex, Expr, ToExpr}, |     expr::{ | ||||||
|     int::{DynSize, KnownSize, Size, SizeType, DYN_SIZE}, |         ops::{ArrayLiteral, ExprFromIterator, ExprIntoIterator, ExprPartialEq}, | ||||||
|  |         CastToBits, Expr, HdlPartialEq, ReduceBits, ToExpr, | ||||||
|  |     }, | ||||||
|  |     int::{Bool, DynSize, KnownSize, Size, SizeType, DYN_SIZE}, | ||||||
|     intern::{Intern, Interned, LazyInterned}, |     intern::{Intern, Interned, LazyInterned}, | ||||||
|     module::transform::visit::{Fold, Folder, Visit, Visitor}, |     module::transform::visit::{Fold, Folder, Visit, Visitor}, | ||||||
|  |     sim::value::{SimValue, SimValuePartialEq}, | ||||||
|     source_location::SourceLocation, |     source_location::SourceLocation, | ||||||
|     ty::{ |     ty::{ | ||||||
|         CanonicalType, MatchVariantWithoutScope, StaticType, Type, TypeProperties, TypeWithDeref, |         serde_impls::SerdeCanonicalType, CanonicalType, MatchVariantWithoutScope, StaticType, Type, | ||||||
|  |         TypeProperties, TypeWithDeref, | ||||||
|     }, |     }, | ||||||
|     util::ConstUsize, |     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)] | #[derive(Copy, Clone, PartialEq, Eq, Hash)] | ||||||
| pub struct ArrayType<T: Type = CanonicalType, Len: Size = DynSize> { | 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> { | impl<T: StaticType, Len: KnownSize> StaticType for ArrayType<T, Len> { | ||||||
|     const TYPE: Self = Self { |     const TYPE: Self = Self { | ||||||
|         element: LazyInterned::new_lazy(&|| T::TYPE.intern_sized()), |         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> { | impl<T: Type, Len: Size> Type for ArrayType<T, Len> { | ||||||
|     type BaseType = Array; |     type BaseType = Array; | ||||||
|     type MaskType = ArrayType<T::MaskType, Len>; |     type MaskType = ArrayType<T::MaskType, Len>; | ||||||
|  |     type SimValue = Len::ArraySimValue<T>; | ||||||
|     type MatchVariant = Len::ArrayMatch<T>; |     type MatchVariant = Len::ArrayMatch<T>; | ||||||
|     type MatchActiveScope = (); |     type MatchActiveScope = (); | ||||||
|     type MatchVariantAndInactiveScope = MatchVariantWithoutScope<Len::ArrayMatch<T>>; |     type MatchVariantAndInactiveScope = MatchVariantWithoutScope<Len::ArrayMatch<T>>; | ||||||
|  | @ -148,10 +162,8 @@ impl<T: Type, Len: Size> Type for ArrayType<T, Len> { | ||||||
|         this: Expr<Self>, |         this: Expr<Self>, | ||||||
|         source_location: SourceLocation, |         source_location: SourceLocation, | ||||||
|     ) -> Self::MatchVariantsIter { |     ) -> Self::MatchVariantsIter { | ||||||
|         let base = Expr::as_dyn_array(this); |  | ||||||
|         let base_ty = Expr::ty(base); |  | ||||||
|         let _ = source_location; |         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( |         std::iter::once(MatchVariantWithoutScope( | ||||||
|             Len::ArrayMatch::<T>::try_from(retval) |             Len::ArrayMatch::<T>::try_from(retval) | ||||||
|                 .ok() |                 .ok() | ||||||
|  | @ -177,16 +189,97 @@ impl<T: Type, Len: Size> Type for ArrayType<T, Len> { | ||||||
|             Len::from_usize(array.len()), |             Len::from_usize(array.len()), | ||||||
|         ) |         ) | ||||||
|     } |     } | ||||||
|  | 
 | ||||||
|     fn source_location() -> SourceLocation { |     fn source_location() -> SourceLocation { | ||||||
|         SourceLocation::builtin() |         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> { | impl<T: Type, Len: Size> TypeWithDeref for ArrayType<T, Len> { | ||||||
|     fn expr_deref(this: &Expr<Self>) -> &Self::MatchVariant { |     fn expr_deref(this: &Expr<Self>) -> &Self::MatchVariant { | ||||||
|         let base = Expr::as_dyn_array(*this); |         let retval = Vec::from_iter(*this); | ||||||
|         let base_ty = Expr::ty(base); |  | ||||||
|         let retval = Vec::from_iter((0..base_ty.len()).map(|i| ArrayIndex::new(base, i).to_expr())); |  | ||||||
|         Interned::into_inner(Intern::intern_sized( |         Interned::into_inner(Intern::intern_sized( | ||||||
|             Len::ArrayMatch::<T>::try_from(retval) |             Len::ArrayMatch::<T>::try_from(retval) | ||||||
|                 .ok() |                 .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))) |         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
 | // See Notices.txt for copyright information
 | ||||||
| 
 | 
 | ||||||
| use crate::{ | use crate::{ | ||||||
|     expr::{ops::BundleLiteral, Expr, ToExpr}, |     expr::{ | ||||||
|  |         ops::{ArrayLiteral, BundleLiteral, ExprPartialEq}, | ||||||
|  |         CastToBits, Expr, ReduceBits, ToExpr, | ||||||
|  |     }, | ||||||
|  |     int::{Bool, DynSize}, | ||||||
|     intern::{Intern, Interned}, |     intern::{Intern, Interned}, | ||||||
|     sim::{SimValue, ToSimValue}, |     sim::value::{SimValue, SimValuePartialEq, ToSimValue, ToSimValueWithType}, | ||||||
|     source_location::SourceLocation, |     source_location::SourceLocation, | ||||||
|     ty::{ |     ty::{ | ||||||
|         impl_match_variant_as_self, CanonicalType, MatchVariantWithoutScope, StaticType, Type, |         impl_match_variant_as_self, CanonicalType, MatchVariantWithoutScope, OpaqueSimValue, | ||||||
|         TypeProperties, TypeWithDeref, |         StaticType, Type, TypeProperties, TypeWithDeref, | ||||||
|     }, |     }, | ||||||
|  |     util::HashMap, | ||||||
| }; | }; | ||||||
| use bitvec::vec::BitVec; | use bitvec::{slice::BitSlice, vec::BitVec}; | ||||||
| use hashbrown::HashMap; | use serde::{Deserialize, Serialize}; | ||||||
| use std::{fmt, marker::PhantomData}; | 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 struct BundleField { | ||||||
|     pub name: Interned<str>, |     pub name: Interned<str>, | ||||||
|     pub flipped: bool, |     pub flipped: bool, | ||||||
|  | @ -155,7 +160,7 @@ impl Default for BundleTypePropertiesBuilder { | ||||||
| impl Bundle { | impl Bundle { | ||||||
|     #[track_caller] |     #[track_caller] | ||||||
|     pub fn new(fields: Interned<[BundleField]>) -> Self { |     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 field_offsets = Vec::with_capacity(fields.len()); | ||||||
|         let mut type_props_builder = BundleTypePropertiesBuilder::new(); |         let mut type_props_builder = BundleTypePropertiesBuilder::new(); | ||||||
|         for (index, &BundleField { name, flipped, ty }) in fields.iter().enumerate() { |         for (index, &BundleField { name, flipped, ty }) in fields.iter().enumerate() { | ||||||
|  | @ -212,6 +217,7 @@ impl Bundle { | ||||||
| impl Type for Bundle { | impl Type for Bundle { | ||||||
|     type BaseType = Bundle; |     type BaseType = Bundle; | ||||||
|     type MaskType = Bundle; |     type MaskType = Bundle; | ||||||
|  |     type SimValue = OpaqueSimValue; | ||||||
|     impl_match_variant_as_self!(); |     impl_match_variant_as_self!(); | ||||||
|     fn mask_type(&self) -> Self::MaskType { |     fn mask_type(&self) -> Self::MaskType { | ||||||
|         Self::new(Interned::from_iter(self.0.fields.into_iter().map( |         Self::new(Interned::from_iter(self.0.fields.into_iter().map( | ||||||
|  | @ -235,6 +241,20 @@ impl Type for Bundle { | ||||||
|     fn source_location() -> SourceLocation { |     fn source_location() -> SourceLocation { | ||||||
|         SourceLocation::builtin() |         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> { | pub trait BundleType: Type<BaseType = Bundle> { | ||||||
|  | @ -243,6 +263,93 @@ pub trait BundleType: Type<BaseType = Bundle> { | ||||||
|     fn fields(&self) -> Interned<[BundleField]>; |     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)] | #[derive(Default)] | ||||||
| pub struct NoBuilder; | pub struct NoBuilder; | ||||||
| 
 | 
 | ||||||
|  | @ -325,7 +432,19 @@ macro_rules! impl_tuple_builder_fields { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| macro_rules! impl_tuples { | 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! { |         impl_tuple_builder_fields! { | ||||||
|             {} |             {} | ||||||
|             [$({ |             [$({ | ||||||
|  | @ -337,6 +456,7 @@ macro_rules! impl_tuples { | ||||||
|         impl<$($T: Type,)*> Type for ($($T,)*) { |         impl<$($T: Type,)*> Type for ($($T,)*) { | ||||||
|             type BaseType = Bundle; |             type BaseType = Bundle; | ||||||
|             type MaskType = ($($T::MaskType,)*); |             type MaskType = ($($T::MaskType,)*); | ||||||
|  |             type SimValue = ($(SimValue<$T>,)*); | ||||||
|             type MatchVariant = ($(Expr<$T>,)*); |             type MatchVariant = ($(Expr<$T>,)*); | ||||||
|             type MatchActiveScope = (); |             type MatchActiveScope = (); | ||||||
|             type MatchVariantAndInactiveScope = MatchVariantWithoutScope<Self::MatchVariant>; |             type MatchVariantAndInactiveScope = MatchVariantWithoutScope<Self::MatchVariant>; | ||||||
|  | @ -375,6 +495,24 @@ macro_rules! impl_tuples { | ||||||
|             fn source_location() -> SourceLocation { |             fn source_location() -> SourceLocation { | ||||||
|                 SourceLocation::builtin() |                 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,)*) { |         impl<$($T: Type,)*> BundleType for ($($T,)*) { | ||||||
|             type Builder = TupleBuilder<($(Unfilled<$T>,)*)>; |             type Builder = TupleBuilder<($(Unfilled<$T>,)*)>; | ||||||
|  | @ -425,77 +563,104 @@ macro_rules! impl_tuples { | ||||||
|                 BundleLiteral::new(ty, field_values[..].intern()).to_expr() |                 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] |             #[track_caller] | ||||||
|             fn to_sim_value(&self, ty: CanonicalType) -> SimValue<CanonicalType> { |             fn to_sim_value_with_type(&self, ty: CanonicalType) -> SimValue<CanonicalType> { | ||||||
|                 ToSimValue::<Bundle>::to_sim_value(self, Bundle::from_canonical(ty)).into_canonical() |                 SimValue::into_canonical(ToSimValueWithType::<Bundle>::to_sim_value_with_type(self, Bundle::from_canonical(ty))) | ||||||
|             } |             } | ||||||
|             #[track_caller] |             #[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))) | ||||||
|             } |  | ||||||
|             #[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,)*) { |         impl<$($T: ToSimValueWithType<CanonicalType>,)*> ToSimValueWithType<Bundle> for ($($T,)*) { | ||||||
|             #[track_caller] |             #[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 ($($var,)*) = self; | ||||||
|                 let [$($ty_var,)*] = *ty.fields() else { |                 let [$($ty_var,)*] = *ty.fields() else { | ||||||
|                     panic!("bundle has wrong number of fields"); |                     panic!("bundle has wrong number of fields"); | ||||||
|                 }; |                 }; | ||||||
|                 $(let $var = $var.to_sim_value($ty_var.ty);)* |                 $(let $var = $var.to_sim_value_with_type($ty_var.ty);)* | ||||||
|                 ToSimValue::into_sim_value(($($var,)*), ty) |                 ToSimValueWithType::into_sim_value_with_type(($($var,)*), ty) | ||||||
|             } |             } | ||||||
|             #[track_caller] |             #[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(unused_mut)] | ||||||
|                 #![allow(clippy::unused_unit)] |                 #![allow(clippy::unused_unit)] | ||||||
|                 let ($($var,)*) = self; |                 let ($($var,)*) = self; | ||||||
|                 let [$($ty_var,)*] = *ty.fields() else { |                 let [$($ty_var,)*] = *ty.fields() else { | ||||||
|                     panic!("bundle has wrong number of fields"); |                     panic!("bundle has wrong number of fields"); | ||||||
|                 }; |                 }; | ||||||
|                 let mut bits: Option<BitVec> = None; |                 let mut bits = BitVec::new(); | ||||||
|                 $(let $var = $var.into_sim_value($ty_var.ty); |                 $(let $var = $var.into_sim_value_with_type($ty_var.ty); | ||||||
|                 assert_eq!($var.ty(), $ty_var.ty); |                 assert_eq!(SimValue::ty(&$var), $ty_var.ty); | ||||||
|                 if !$var.bits().is_empty() { |                 bits.extend_from_bitslice(SimValue::bits(&$var).bits()); | ||||||
|                     if let Some(bits) = &mut bits { |  | ||||||
|                         bits.extend_from_bitslice($var.bits()); |  | ||||||
|                     } else { |  | ||||||
|                         let mut $var = $var.into_bits(); |  | ||||||
|                         $var.reserve(ty.type_properties().bit_width - $var.len()); |  | ||||||
|                         bits = Some($var); |  | ||||||
|                     } |  | ||||||
|                 } |  | ||||||
|                 )* |                 )* | ||||||
|                 bits.unwrap_or_else(BitVec::new).into_sim_value(ty) |                 bits.into_sim_value_with_type(ty) | ||||||
|             } |  | ||||||
|             #[track_caller] |  | ||||||
|             fn box_into_sim_value(self: Box<Self>, ty: Bundle) -> SimValue<Bundle> { |  | ||||||
|                 Self::into_sim_value(*self, ty) |  | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|         impl<$($T: ToSimValue<$Ty>, $Ty: Type,)*> ToSimValue<($($Ty,)*)> for ($($T,)*) { |         impl<$($T: ToSimValueWithType<$Ty>, $Ty: Type,)*> ToSimValueWithType<($($Ty,)*)> for ($($T,)*) { | ||||||
|             #[track_caller] |             #[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 ($($var,)*) = self; | ||||||
|                 let ($($ty_var,)*) = ty; |                 let ($($ty_var,)*) = ty; | ||||||
|                 $(let $var = $var.to_sim_value($ty_var).into_canonical();)* |                 $(let $var = $var.to_sim_value_with_type($ty_var);)* | ||||||
|                 SimValue::from_canonical(ToSimValue::into_sim_value(($($var,)*), ty.canonical())) |                 SimValue::from_value(ty, ($($var,)*)) | ||||||
|             } |             } | ||||||
|             #[track_caller] |             #[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 ($($var,)*) = self; | ||||||
|                 let ($($ty_var,)*) = ty; |                 let ($($ty_var,)*) = ty; | ||||||
|                 $(let $var = $var.into_sim_value($ty_var).into_canonical();)* |                 $(let $var = $var.into_sim_value_with_type($ty_var);)* | ||||||
|                 SimValue::from_canonical(ToSimValue::into_sim_value(($($var,)*), ty.canonical())) |                 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] |             #[track_caller] | ||||||
|             fn box_into_sim_value(self: Box<Self>, ty: ($($Ty,)*)) -> SimValue<($($Ty,)*)> { |             fn into_sim_value(self) -> SimValue<Self::Type> { | ||||||
|                 Self::into_sim_value(*self, ty) |                 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! { | impl_tuples! { | ||||||
|     [] [ |     [] [ | ||||||
|         {#[num = 0, field = field_0, ty = ty0: Ty0] v0: T0} |         {#[num = 0, field = field_0, ty = ty0: Ty0, lhs = lhs0: Lhs0, rhs = rhs0: Rhs0] v0: T0} | ||||||
|         {#[num = 1, field = field_1, ty = ty1: Ty1] v1: T1} |         {#[num = 1, field = field_1, ty = ty1: Ty1, lhs = lhs1: Lhs1, rhs = rhs1: Rhs1] v1: T1} | ||||||
|         {#[num = 2, field = field_2, ty = ty2: Ty2] v2: T2} |         {#[num = 2, field = field_2, ty = ty2: Ty2, lhs = lhs2: Lhs2, rhs = rhs2: Rhs2] v2: T2} | ||||||
|         {#[num = 3, field = field_3, ty = ty3: Ty3] v3: T3} |         {#[num = 3, field = field_3, ty = ty3: Ty3, lhs = lhs3: Lhs3, rhs = rhs3: Rhs3] v3: T3} | ||||||
|         {#[num = 4, field = field_4, ty = ty4: Ty4] v4: T4} |         {#[num = 4, field = field_4, ty = ty4: Ty4, lhs = lhs4: Lhs4, rhs = rhs4: Rhs4] v4: T4} | ||||||
|         {#[num = 5, field = field_5, ty = ty5: Ty5] v5: T5} |         {#[num = 5, field = field_5, ty = ty5: Ty5, lhs = lhs5: Lhs5, rhs = rhs5: Rhs5] v5: T5} | ||||||
|         {#[num = 6, field = field_6, ty = ty6: Ty6] v6: T6} |         {#[num = 6, field = field_6, ty = ty6: Ty6, lhs = lhs6: Lhs6, rhs = rhs6: Rhs6] v6: T6} | ||||||
|         {#[num = 7, field = field_7, ty = ty7: Ty7] v7: T7} |         {#[num = 7, field = field_7, ty = ty7: Ty7, lhs = lhs7: Lhs7, rhs = rhs7: Rhs7] v7: T7} | ||||||
|         {#[num = 8, field = field_8, ty = ty8: Ty8] v8: T8} |         {#[num = 8, field = field_8, ty = ty8: Ty8, lhs = lhs8: Lhs8, rhs = rhs8: Rhs8] v8: T8} | ||||||
|         {#[num = 9, field = field_9, ty = ty9: Ty9] v9: T9} |         {#[num = 9, field = field_9, ty = ty9: Ty9, lhs = lhs9: Lhs9, rhs = rhs9: Rhs9] v9: T9} | ||||||
|         {#[num = 10, field = field_10, ty = ty10: Ty10] v10: T10} |         {#[num = 10, field = field_10, ty = ty10: Ty10, lhs = lhs10: Lhs10, rhs = rhs10: Rhs10] v10: T10} | ||||||
|         {#[num = 11, field = field_11, ty = ty11: Ty11] v11: T11} |         {#[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> { | impl<T: ?Sized + Send + Sync + 'static> Type for PhantomData<T> { | ||||||
|     type BaseType = Bundle; |     type BaseType = Bundle; | ||||||
|     type MaskType = (); |     type MaskType = (); | ||||||
|  |     type SimValue = PhantomData<T>; | ||||||
|     type MatchVariant = PhantomData<T>; |     type MatchVariant = PhantomData<T>; | ||||||
|     type MatchActiveScope = (); |     type MatchActiveScope = (); | ||||||
|     type MatchVariantAndInactiveScope = MatchVariantWithoutScope<Self::MatchVariant>; |     type MatchVariantAndInactiveScope = MatchVariantWithoutScope<Self::MatchVariant>; | ||||||
|  | @ -557,6 +723,16 @@ impl<T: ?Sized + Send + Sync + 'static> Type for PhantomData<T> { | ||||||
|     fn source_location() -> SourceLocation { |     fn source_location() -> SourceLocation { | ||||||
|         SourceLocation::builtin() |         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>); | 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] |     #[track_caller] | ||||||
|     fn to_sim_value(&self, ty: Self) -> SimValue<Self> { |     fn to_sim_value(&self) -> SimValue<Self> { | ||||||
|         ToSimValue::into_sim_value(BitVec::new(), ty) |         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] |     #[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()); |         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] |     #[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); |         let ty = Bundle::from_canonical(ty); | ||||||
|         assert!(ty.fields().is_empty()); |         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", |         default_value = "firtool", | ||||||
|         env = "FIRTOOL", |         env = "FIRTOOL", | ||||||
|         value_hint = ValueHint::CommandName, |         value_hint = ValueHint::CommandName, | ||||||
|         value_parser = OsStringValueParser::new().try_map(which::which) |         value_parser = OsStringValueParser::new().try_map(which) | ||||||
|     )] |     )] | ||||||
|     pub firtool: PathBuf, |     pub firtool: PathBuf, | ||||||
|     #[arg(long)] |     #[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)] | #[derive(Parser, Clone)] | ||||||
| #[non_exhaustive] | #[non_exhaustive] | ||||||
| pub struct FormalArgs { | pub struct FormalArgs { | ||||||
|  | @ -438,7 +445,7 @@ pub struct FormalArgs { | ||||||
|         default_value = "sby", |         default_value = "sby", | ||||||
|         env = "SBY", |         env = "SBY", | ||||||
|         value_hint = ValueHint::CommandName, |         value_hint = ValueHint::CommandName, | ||||||
|         value_parser = OsStringValueParser::new().try_map(which::which) |         value_parser = OsStringValueParser::new().try_map(which) | ||||||
|     )] |     )] | ||||||
|     pub sby: PathBuf, |     pub sby: PathBuf, | ||||||
|     #[arg(long)] |     #[arg(long)] | ||||||
|  |  | ||||||
|  | @ -8,6 +8,7 @@ use crate::{ | ||||||
|     source_location::SourceLocation, |     source_location::SourceLocation, | ||||||
|     ty::{impl_match_variant_as_self, CanonicalType, StaticType, Type, TypeProperties}, |     ty::{impl_match_variant_as_self, CanonicalType, StaticType, Type, TypeProperties}, | ||||||
| }; | }; | ||||||
|  | use bitvec::slice::BitSlice; | ||||||
| 
 | 
 | ||||||
| #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, Default)] | #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, Default)] | ||||||
| pub struct Clock; | pub struct Clock; | ||||||
|  | @ -15,6 +16,7 @@ pub struct Clock; | ||||||
| impl Type for Clock { | impl Type for Clock { | ||||||
|     type BaseType = Clock; |     type BaseType = Clock; | ||||||
|     type MaskType = Bool; |     type MaskType = Bool; | ||||||
|  |     type SimValue = bool; | ||||||
| 
 | 
 | ||||||
|     impl_match_variant_as_self!(); |     impl_match_variant_as_self!(); | ||||||
| 
 | 
 | ||||||
|  | @ -36,6 +38,21 @@ impl Type for Clock { | ||||||
|         }; |         }; | ||||||
|         retval |         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 { | impl Clock { | ||||||
|  |  | ||||||
|  | @ -2,21 +2,30 @@ | ||||||
| // See Notices.txt for copyright information
 | // See Notices.txt for copyright information
 | ||||||
| 
 | 
 | ||||||
| use crate::{ | use crate::{ | ||||||
|     expr::{ops::VariantAccess, Expr, ToExpr}, |     expr::{ | ||||||
|  |         ops::{ExprPartialEq, VariantAccess}, | ||||||
|  |         Expr, ToExpr, | ||||||
|  |     }, | ||||||
|     hdl, |     hdl, | ||||||
|     int::Bool, |     int::{Bool, UIntValue}, | ||||||
|     intern::{Intern, Interned}, |     intern::{Intern, Interned}, | ||||||
|     module::{ |     module::{ | ||||||
|         connect, enum_match_variants_helper, incomplete_wire, wire, |         connect, enum_match_variants_helper, incomplete_wire, wire, | ||||||
|         EnumMatchVariantAndInactiveScopeImpl, EnumMatchVariantsIterImpl, Scope, |         EnumMatchVariantAndInactiveScopeImpl, EnumMatchVariantsIterImpl, Scope, | ||||||
|     }, |     }, | ||||||
|  |     sim::value::{SimValue, SimValuePartialEq}, | ||||||
|     source_location::SourceLocation, |     source_location::SourceLocation, | ||||||
|     ty::{CanonicalType, MatchVariantAndInactiveScope, StaticType, Type, TypeProperties}, |     ty::{ | ||||||
|  |         CanonicalType, MatchVariantAndInactiveScope, OpaqueSimValue, StaticType, Type, | ||||||
|  |         TypeProperties, | ||||||
|  |     }, | ||||||
|  |     util::HashMap, | ||||||
| }; | }; | ||||||
| use hashbrown::HashMap; | use bitvec::{order::Lsb0, slice::BitSlice, view::BitView}; | ||||||
| use std::{convert::Infallible, fmt, iter::FusedIterator}; | 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 struct EnumVariant { | ||||||
|     pub name: Interned<str>, |     pub name: Interned<str>, | ||||||
|     pub ty: Option<CanonicalType>, |     pub ty: Option<CanonicalType>, | ||||||
|  | @ -149,6 +158,12 @@ impl EnumTypePropertiesBuilder { | ||||||
|             variant_count: variant_count + 1, |             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 { |     pub const fn finish(self) -> TypeProperties { | ||||||
|         assert!( |         assert!( | ||||||
|             self.variant_count != 0, |             self.variant_count != 0, | ||||||
|  | @ -178,7 +193,8 @@ impl Default for EnumTypePropertiesBuilder { | ||||||
| impl Enum { | impl Enum { | ||||||
|     #[track_caller] |     #[track_caller] | ||||||
|     pub fn new(variants: Interned<[EnumVariant]>) -> Self { |     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(); |         let mut type_props_builder = EnumTypePropertiesBuilder::new(); | ||||||
|         for (index, EnumVariant { name, ty }) in variants.iter().enumerate() { |         for (index, EnumVariant { name, ty }) in variants.iter().enumerate() { | ||||||
|             if let Some(old_index) = name_indexes.insert(*name, index) { |             if let Some(old_index) = name_indexes.insert(*name, index) { | ||||||
|  | @ -245,6 +261,7 @@ pub trait EnumType: | ||||||
|     MatchVariantsIter = EnumMatchVariantsIter<Self>, |     MatchVariantsIter = EnumMatchVariantsIter<Self>, | ||||||
| > | > | ||||||
| { | { | ||||||
|  |     type SimBuilder: From<Self>; | ||||||
|     fn variants(&self) -> Interned<[EnumVariant]>; |     fn variants(&self) -> Interned<[EnumVariant]>; | ||||||
|     fn match_activate_scope( |     fn match_activate_scope( | ||||||
|         v: Self::MatchVariantAndInactiveScope, |         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 { | impl EnumType for Enum { | ||||||
|  |     type SimBuilder = NoBuilder; | ||||||
|     fn match_activate_scope( |     fn match_activate_scope( | ||||||
|         v: Self::MatchVariantAndInactiveScope, |         v: Self::MatchVariantAndInactiveScope, | ||||||
|     ) -> (Self::MatchVariant, Self::MatchActiveScope) { |     ) -> (Self::MatchVariant, Self::MatchActiveScope) { | ||||||
|  | @ -322,6 +350,7 @@ impl EnumType for Enum { | ||||||
| impl Type for Enum { | impl Type for Enum { | ||||||
|     type BaseType = Enum; |     type BaseType = Enum; | ||||||
|     type MaskType = Bool; |     type MaskType = Bool; | ||||||
|  |     type SimValue = OpaqueSimValue; | ||||||
|     type MatchVariant = Option<Expr<CanonicalType>>; |     type MatchVariant = Option<Expr<CanonicalType>>; | ||||||
|     type MatchActiveScope = Scope; |     type MatchActiveScope = Scope; | ||||||
|     type MatchVariantAndInactiveScope = EnumMatchVariantAndInactiveScope<Self>; |     type MatchVariantAndInactiveScope = EnumMatchVariantAndInactiveScope<Self>; | ||||||
|  | @ -352,6 +381,309 @@ impl Type for Enum { | ||||||
|     fn source_location() -> SourceLocation { |     fn source_location() -> SourceLocation { | ||||||
|         SourceLocation::builtin() |         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] | #[hdl] | ||||||
|  | @ -360,6 +692,79 @@ pub enum HdlOption<T: Type> { | ||||||
|     HdlSome(T), |     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)] | #[allow(non_snake_case)] | ||||||
| pub fn HdlNone<T: StaticType>() -> Expr<HdlOption<T>> { | pub fn HdlNone<T: StaticType>() -> Expr<HdlOption<T>> { | ||||||
|     HdlOption[T::TYPE].HdlNone() |     HdlOption[T::TYPE].HdlNone() | ||||||
|  |  | ||||||
|  | @ -16,6 +16,7 @@ use crate::{ | ||||||
|         transform::visit::{Fold, Folder, Visit, Visitor}, |         transform::visit::{Fold, Folder, Visit, Visitor}, | ||||||
|         Instance, ModuleIO, |         Instance, ModuleIO, | ||||||
|     }, |     }, | ||||||
|  |     phantom_const::PhantomConst, | ||||||
|     reg::Reg, |     reg::Reg, | ||||||
|     reset::{AsyncReset, Reset, ResetType, ResetTypeDispatch, SyncReset}, |     reset::{AsyncReset, Reset, ResetType, ResetTypeDispatch, SyncReset}, | ||||||
|     ty::{CanonicalType, StaticType, Type, TypeWithDeref}, |     ty::{CanonicalType, StaticType, Type, TypeWithDeref}, | ||||||
|  | @ -109,6 +110,7 @@ expr_enum! { | ||||||
|         UIntLiteral(Interned<UIntValue>), |         UIntLiteral(Interned<UIntValue>), | ||||||
|         SIntLiteral(Interned<SIntValue>), |         SIntLiteral(Interned<SIntValue>), | ||||||
|         BoolLiteral(bool), |         BoolLiteral(bool), | ||||||
|  |         PhantomConst(PhantomConst), | ||||||
|         BundleLiteral(ops::BundleLiteral), |         BundleLiteral(ops::BundleLiteral), | ||||||
|         ArrayLiteral(ops::ArrayLiteral<CanonicalType, DynSize>), |         ArrayLiteral(ops::ArrayLiteral<CanonicalType, DynSize>), | ||||||
|         EnumLiteral(ops::EnumLiteral), |         EnumLiteral(ops::EnumLiteral), | ||||||
|  | @ -272,6 +274,17 @@ pub struct Expr<T: Type> { | ||||||
| 
 | 
 | ||||||
| impl<T: Type + fmt::Debug> fmt::Debug for Expr<T> { | impl<T: Type + fmt::Debug> fmt::Debug for Expr<T> { | ||||||
|     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |     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) |         self.__enum.fmt(f) | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | @ -698,6 +711,7 @@ impl<T: ToExpr + ?Sized> CastToBits for T { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| pub trait CastBitsTo { | pub trait CastBitsTo { | ||||||
|  |     #[track_caller] | ||||||
|     fn cast_bits_to<T: Type>(&self, ty: T) -> Expr<T>; |     fn cast_bits_to<T: Type>(&self, ty: T) -> Expr<T>; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -755,3 +769,27 @@ pub fn repeat<T: Type, L: SizeType>( | ||||||
|     ) |     ) | ||||||
|     .to_expr() |     .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, |             GetTarget, Target, TargetPathArrayElement, TargetPathBundleField, | ||||||
|             TargetPathDynArrayElement, TargetPathElement, |             TargetPathDynArrayElement, TargetPathElement, | ||||||
|         }, |         }, | ||||||
|         CastTo, Expr, ExprEnum, Flow, HdlPartialEq, HdlPartialOrd, NotALiteralExpr, ReduceBits, |         CastBitsTo as _, CastTo, CastToBits as _, Expr, ExprEnum, Flow, HdlPartialEq, | ||||||
|         ToExpr, ToLiteralBits, |         HdlPartialOrd, NotALiteralExpr, ReduceBits, ToExpr, ToLiteralBits, | ||||||
|     }, |     }, | ||||||
|     int::{ |     int::{ | ||||||
|         Bool, BoolOrIntType, DynSize, IntType, KnownSize, SInt, SIntType, SIntValue, Size, UInt, |         Bool, BoolOrIntType, DynSize, IntType, KnownSize, SInt, SIntType, SIntValue, Size, UInt, | ||||||
|         UIntType, UIntValue, |         UIntType, UIntValue, | ||||||
|     }, |     }, | ||||||
|     intern::{Intern, Interned}, |     intern::{Intern, Interned}, | ||||||
|  |     phantom_const::{PhantomConst, PhantomConstValue}, | ||||||
|     reset::{ |     reset::{ | ||||||
|         AsyncReset, Reset, ResetType, ResetTypeDispatch, SyncReset, ToAsyncReset, ToReset, |         AsyncReset, Reset, ResetType, ResetTypeDispatch, SyncReset, ToAsyncReset, ToReset, | ||||||
|         ToSyncReset, |         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)] | #[derive(Copy, Clone, PartialEq, Eq, Hash)] | ||||||
| pub struct FieldAccess<FieldType: Type = CanonicalType> { | pub struct FieldAccess<FieldType: Type = CanonicalType> { | ||||||
|     base: Expr<Bundle>, |     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::{ | ||||||
|             Target, TargetBase, TargetPathArrayElement, TargetPathBundleField, TargetPathElement, |             Target, TargetBase, TargetPathArrayElement, TargetPathBundleField, TargetPathElement, | ||||||
|         }, |         }, | ||||||
|         Expr, ExprEnum, |         CastBitsTo, Expr, ExprEnum, | ||||||
|     }, |     }, | ||||||
|     formal::FormalKind, |     formal::FormalKind, | ||||||
|     int::{Bool, DynSize, IntType, SIntValue, UInt, UIntValue}, |     int::{Bool, DynSize, IntType, SIntValue, UInt, UIntValue}, | ||||||
|  | @ -36,12 +36,11 @@ use crate::{ | ||||||
|     ty::{CanonicalType, Type}, |     ty::{CanonicalType, Type}, | ||||||
|     util::{ |     util::{ | ||||||
|         const_str_array_is_strictly_ascending, BitSliceWriteWithBase, DebugAsRawString, |         const_str_array_is_strictly_ascending, BitSliceWriteWithBase, DebugAsRawString, | ||||||
|         GenericConstBool, |         GenericConstBool, HashMap, HashSet, | ||||||
|     }, |     }, | ||||||
| }; | }; | ||||||
| use bitvec::slice::BitSlice; | use bitvec::slice::BitSlice; | ||||||
| use clap::value_parser; | use clap::value_parser; | ||||||
| use hashbrown::{HashMap, HashSet}; |  | ||||||
| use num_traits::Signed; | use num_traits::Signed; | ||||||
| use serde::Serialize; | use serde::Serialize; | ||||||
| use std::{ | use std::{ | ||||||
|  | @ -447,6 +446,7 @@ impl TypeState { | ||||||
|             CanonicalType::AsyncReset(AsyncReset {}) => "AsyncReset".into(), |             CanonicalType::AsyncReset(AsyncReset {}) => "AsyncReset".into(), | ||||||
|             CanonicalType::SyncReset(SyncReset {}) => "UInt<1>".into(), |             CanonicalType::SyncReset(SyncReset {}) => "UInt<1>".into(), | ||||||
|             CanonicalType::Reset(Reset {}) => "Reset".into(), |             CanonicalType::Reset(Reset {}) => "Reset".into(), | ||||||
|  |             CanonicalType::PhantomConst(_) => "{}".into(), | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | @ -1152,6 +1152,7 @@ impl<'a> Exporter<'a> { | ||||||
|             | CanonicalType::Clock(_) |             | CanonicalType::Clock(_) | ||||||
|             | CanonicalType::AsyncReset(_) |             | CanonicalType::AsyncReset(_) | ||||||
|             | CanonicalType::Reset(_) => format!("asUInt({value_str})"), |             | CanonicalType::Reset(_) => format!("asUInt({value_str})"), | ||||||
|  |             CanonicalType::PhantomConst(_) => "UInt<0>(0)".into(), | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|     fn expr_cast_bits_to_bundle( |     fn expr_cast_bits_to_bundle( | ||||||
|  | @ -1357,6 +1358,12 @@ impl<'a> Exporter<'a> { | ||||||
|             CanonicalType::AsyncReset(_) => format!("asAsyncReset({value_str})"), |             CanonicalType::AsyncReset(_) => format!("asAsyncReset({value_str})"), | ||||||
|             CanonicalType::SyncReset(_) => value_str, |             CanonicalType::SyncReset(_) => value_str, | ||||||
|             CanonicalType::Reset(_) => unreachable!("Reset is not bit castable to"), |             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>( |     fn expr_unary<T: Type>( | ||||||
|  | @ -1395,6 +1402,11 @@ impl<'a> Exporter<'a> { | ||||||
|             ExprEnum::UIntLiteral(literal) => self.uint_literal(&literal), |             ExprEnum::UIntLiteral(literal) => self.uint_literal(&literal), | ||||||
|             ExprEnum::SIntLiteral(literal) => self.sint_literal(&literal), |             ExprEnum::SIntLiteral(literal) => self.sint_literal(&literal), | ||||||
|             ExprEnum::BoolLiteral(literal) => self.bool_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) => { |             ExprEnum::ArrayLiteral(array_literal) => { | ||||||
|                 self.array_literal_expr(array_literal, definitions, const_ty) |                 self.array_literal_expr(array_literal, definitions, const_ty) | ||||||
|             } |             } | ||||||
|  | @ -2245,6 +2257,7 @@ impl<'a> Exporter<'a> { | ||||||
|             ModuleBody::Extern(ExternModuleBody { |             ModuleBody::Extern(ExternModuleBody { | ||||||
|                 verilog_name, |                 verilog_name, | ||||||
|                 parameters, |                 parameters, | ||||||
|  |                 simulation: _, | ||||||
|             }) => { |             }) => { | ||||||
|                 let verilog_name = Ident(verilog_name); |                 let verilog_name = Ident(verilog_name); | ||||||
|                 writeln!(body, "{indent}defname = {verilog_name}").unwrap(); |                 writeln!(body, "{indent}defname = {verilog_name}").unwrap(); | ||||||
|  | @ -2608,7 +2621,7 @@ fn export_impl( | ||||||
|             indent_depth: &indent_depth, |             indent_depth: &indent_depth, | ||||||
|             indent: "    ", |             indent: "    ", | ||||||
|         }, |         }, | ||||||
|         seen_modules: HashSet::new(), |         seen_modules: HashSet::default(), | ||||||
|         unwritten_modules: VecDeque::new(), |         unwritten_modules: VecDeque::new(), | ||||||
|         global_ns, |         global_ns, | ||||||
|         module: ModuleState::default(), |         module: ModuleState::default(), | ||||||
|  |  | ||||||
|  | @ -2,27 +2,52 @@ | ||||||
| // See Notices.txt for copyright information
 | // See Notices.txt for copyright information
 | ||||||
| 
 | 
 | ||||||
| use crate::{ | use crate::{ | ||||||
|  |     array::ArrayType, | ||||||
|     expr::{ |     expr::{ | ||||||
|         target::{GetTarget, Target}, |         target::{GetTarget, Target}, | ||||||
|         Expr, NotALiteralExpr, ToExpr, ToLiteralBits, |         Expr, NotALiteralExpr, ToExpr, ToLiteralBits, | ||||||
|     }, |     }, | ||||||
|  |     hdl, | ||||||
|     intern::{Intern, Interned, Memoize}, |     intern::{Intern, Interned, Memoize}, | ||||||
|  |     sim::value::{SimValue, ToSimValueWithType}, | ||||||
|     source_location::SourceLocation, |     source_location::SourceLocation, | ||||||
|     ty::{impl_match_variant_as_self, CanonicalType, StaticType, Type, TypeProperties}, |     ty::{impl_match_variant_as_self, CanonicalType, StaticType, Type, TypeProperties}, | ||||||
|     util::{interned_bit, ConstBool, ConstUsize, GenericConstBool, GenericConstUsize}, |     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_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::{ | use std::{ | ||||||
|     borrow::{BorrowMut, Cow}, |     borrow::{BorrowMut, Cow}, | ||||||
|     fmt, |     fmt, | ||||||
|     marker::PhantomData, |     marker::PhantomData, | ||||||
|     num::NonZero, |     num::NonZero, | ||||||
|     ops::{Bound, Index, Not, Range, RangeBounds, RangeInclusive}, |     ops::{Bound, Index, Not, Range, RangeBounds, RangeInclusive}, | ||||||
|  |     str::FromStr, | ||||||
|     sync::Arc, |     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 { | mod sealed { | ||||||
|     pub trait BoolOrIntTypeSealed {} |     pub trait BoolOrIntTypeSealed {} | ||||||
|     pub trait SizeSealed {} |     pub trait SizeSealed {} | ||||||
|  | @ -49,6 +74,16 @@ pub trait KnownSize: | ||||||
|         + IntoIterator<Item = Expr<Element>> |         + IntoIterator<Item = Expr<Element>> | ||||||
|         + TryFrom<Vec<Expr<Element>>> |         + TryFrom<Vec<Expr<Element>>> | ||||||
|         + Into<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 { | macro_rules! known_widths { | ||||||
|  | @ -60,6 +95,7 @@ macro_rules! known_widths { | ||||||
|         }> { |         }> { | ||||||
|             const SIZE: Self = Self; |             const SIZE: Self = Self; | ||||||
|             type ArrayMatch<Element: Type> = [Expr<Element>; Self::VALUE]; |             type ArrayMatch<Element: Type> = [Expr<Element>; Self::VALUE]; | ||||||
|  |             type ArraySimValue<Element: Type> = [SimValue<Element>; Self::VALUE]; | ||||||
|         } |         } | ||||||
|     }; |     }; | ||||||
|     ([2 $($rest:tt)*] $($bits:literal)+) => { |     ([2 $($rest:tt)*] $($bits:literal)+) => { | ||||||
|  | @ -72,6 +108,7 @@ macro_rules! known_widths { | ||||||
|         impl KnownSize for ConstUsize<{2 $(* $rest)*}> { |         impl KnownSize for ConstUsize<{2 $(* $rest)*}> { | ||||||
|             const SIZE: Self = Self; |             const SIZE: Self = Self; | ||||||
|             type ArrayMatch<Element: Type> = [Expr<Element>; Self::VALUE]; |             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]); | known_widths!([2 2 2 2 2 2 2 2 2]); | ||||||
| 
 | 
 | ||||||
| pub trait SizeType: | 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>; |     type Size: Size<SizeType = Self>; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| pub trait Size: | pub trait Size: | ||||||
|     sealed::SizeSealed + Copy + Ord + std::hash::Hash + std::fmt::Debug + Send + Sync + 'static |     sealed::SizeSealed | ||||||
|  |     + Copy | ||||||
|  |     + Ord | ||||||
|  |     + std::hash::Hash | ||||||
|  |     + std::fmt::Debug | ||||||
|  |     + Send | ||||||
|  |     + Sync | ||||||
|  |     + 'static | ||||||
|  |     + Serialize | ||||||
|  |     + DeserializeOwned | ||||||
| { | { | ||||||
|     type ArrayMatch<Element: Type>: AsRef<[Expr<Element>]> |     type ArrayMatch<Element: Type>: AsRef<[Expr<Element>]> | ||||||
|         + AsMut<[Expr<Element>]> |         + AsMut<[Expr<Element>]> | ||||||
|  | @ -100,6 +155,16 @@ pub trait Size: | ||||||
|         + IntoIterator<Item = Expr<Element>> |         + IntoIterator<Item = Expr<Element>> | ||||||
|         + TryFrom<Vec<Expr<Element>>> |         + TryFrom<Vec<Expr<Element>>> | ||||||
|         + Into<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>; |     const KNOWN_VALUE: Option<usize>; | ||||||
|     type SizeType: SizeType<Size = Self> |     type SizeType: SizeType<Size = Self> | ||||||
|         + Copy |         + Copy | ||||||
|  | @ -125,6 +190,7 @@ impl SizeType for usize { | ||||||
| 
 | 
 | ||||||
| impl Size for DynSize { | impl Size for DynSize { | ||||||
|     type ArrayMatch<Element: Type> = Box<[Expr<Element>]>; |     type ArrayMatch<Element: Type> = Box<[Expr<Element>]>; | ||||||
|  |     type ArraySimValue<Element: Type> = Box<[SimValue<Element>]>; | ||||||
|     const KNOWN_VALUE: Option<usize> = None; |     const KNOWN_VALUE: Option<usize> = None; | ||||||
|     type SizeType = usize; |     type SizeType = usize; | ||||||
| 
 | 
 | ||||||
|  | @ -147,6 +213,7 @@ impl<T: KnownSize> SizeType for T { | ||||||
| 
 | 
 | ||||||
| impl<T: KnownSize> Size for T { | impl<T: KnownSize> Size for T { | ||||||
|     type ArrayMatch<Element: Type> = <T as KnownSize>::ArrayMatch<Element>; |     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); |     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 { | macro_rules! impl_int { | ||||||
|     ($pretty_name:ident, $name:ident, $generic_name:ident, $value:ident, $SIGNED:literal) => { |     ($pretty_name:ident, $name:ident, $generic_name:ident, $value:ident, $SIGNED:literal) => { | ||||||
|         #[derive(Copy, Clone, PartialEq, Eq, Hash)] |         #[derive(Copy, Clone, PartialEq, Eq, Hash)] | ||||||
|  | @ -188,19 +554,14 @@ macro_rules! impl_int { | ||||||
|         pub const $name: $generic_name = $generic_name; |         pub const $name: $generic_name = $generic_name; | ||||||
| 
 | 
 | ||||||
|         impl<Width: Size> $name<Width> { |         impl<Width: Size> $name<Width> { | ||||||
|             pub fn new(width: Width::SizeType) -> Self { |             pub const fn new(width: Width::SizeType) -> Self { | ||||||
|                 Self { width } |                 Self { width } | ||||||
|             } |             } | ||||||
|             pub fn width(self) -> usize { |             pub fn width(self) -> usize { | ||||||
|                 Width::as_usize(self.width) |                 Width::as_usize(self.width) | ||||||
|             } |             } | ||||||
|             pub fn type_properties(self) -> TypeProperties { |             pub fn type_properties(self) -> TypeProperties { | ||||||
|                 TypeProperties { |                 self.as_dyn_int().type_properties_dyn() | ||||||
|                     is_passive: true, |  | ||||||
|                     is_storable: true, |  | ||||||
|                     is_castable_from_bits: true, |  | ||||||
|                     bit_width: self.width(), |  | ||||||
|                 } |  | ||||||
|             } |             } | ||||||
|             pub fn bits_from_bigint_wrapping(self, v: &BigInt) -> BitVec { |             pub fn bits_from_bigint_wrapping(self, v: &BigInt) -> BitVec { | ||||||
|                 BoolOrIntType::bits_from_bigint_wrapping(self, v) |                 BoolOrIntType::bits_from_bigint_wrapping(self, v) | ||||||
|  | @ -263,6 +624,12 @@ macro_rules! impl_int { | ||||||
|                 } |                 } | ||||||
|                 Expr::from_dyn_int(MemoizeBitsToExpr.get_cow(bits)) |                 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> { |         impl<Width: Size> IntType for $name<Width> { | ||||||
|  | @ -270,12 +637,20 @@ macro_rules! impl_int { | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         impl $name { |         impl $name { | ||||||
|             pub fn new_dyn(width: usize) -> Self { |             pub const fn new_dyn(width: usize) -> Self { | ||||||
|                 Self { width } |                 Self { width } | ||||||
|             } |             } | ||||||
|             pub fn bits_to_bigint(bits: &BitSlice) -> BigInt { |             pub fn bits_to_bigint(bits: &BitSlice) -> BigInt { | ||||||
|                 <Self as BoolOrIntType>::bits_to_bigint(bits) |                 <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> { |         impl<Width: KnownSize> $name<Width> { | ||||||
|  | @ -287,6 +662,7 @@ macro_rules! impl_int { | ||||||
|         impl<Width: Size> Type for $name<Width> { |         impl<Width: Size> Type for $name<Width> { | ||||||
|             type BaseType = $pretty_name; |             type BaseType = $pretty_name; | ||||||
|             type MaskType = Bool; |             type MaskType = Bool; | ||||||
|  |             type SimValue = $value<Width>; | ||||||
|             impl_match_variant_as_self!(); |             impl_match_variant_as_self!(); | ||||||
|             fn mask_type(&self) -> Self::MaskType { |             fn mask_type(&self) -> Self::MaskType { | ||||||
|                 Bool |                 Bool | ||||||
|  | @ -297,7 +673,7 @@ macro_rules! impl_int { | ||||||
|             #[track_caller] |             #[track_caller] | ||||||
|             fn from_canonical(canonical_type: CanonicalType) -> Self { |             fn from_canonical(canonical_type: CanonicalType) -> Self { | ||||||
|                 let CanonicalType::$pretty_name(retval) = canonical_type else { |                 let CanonicalType::$pretty_name(retval) = canonical_type else { | ||||||
|                     panic!("expected {}", stringify!($name)); |                     panic!("expected {}", stringify!($pretty_name)); | ||||||
|                 }; |                 }; | ||||||
|                 $name { |                 $name { | ||||||
|                     width: Width::from_usize(retval.width), |                     width: Width::from_usize(retval.width), | ||||||
|  | @ -306,20 +682,78 @@ macro_rules! impl_int { | ||||||
|             fn source_location() -> SourceLocation { |             fn source_location() -> SourceLocation { | ||||||
|                 SourceLocation::builtin() |                 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> { |         impl<Width: KnownSize> StaticType for $name<Width> { | ||||||
|             const TYPE: Self = Self { width: Width::SIZE }; |             const TYPE: Self = Self { width: Width::SIZE }; | ||||||
|             const MASK_TYPE: Self::MaskType = Bool; |             const MASK_TYPE: Self::MaskType = Bool; | ||||||
|             const TYPE_PROPERTIES: TypeProperties = TypeProperties { |             const TYPE_PROPERTIES: TypeProperties = $name { | ||||||
|                 is_passive: true, |                 width: Width::VALUE, | ||||||
|                 is_storable: true, |             } | ||||||
|                 is_castable_from_bits: true, |             .type_properties_dyn(); | ||||||
|                 bit_width: Width::VALUE, |  | ||||||
|             }; |  | ||||||
|             const MASK_TYPE_PROPERTIES: TypeProperties = Bool::TYPE_PROPERTIES; |             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)] |         #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, Default)] | ||||||
|         pub struct $generic_name; |         pub struct $generic_name; | ||||||
| 
 | 
 | ||||||
|  | @ -337,7 +771,7 @@ macro_rules! impl_int { | ||||||
|             _phantom: PhantomData<Width>, |             _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 { |             fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||||||
|                 let value = self.to_bigint(); |                 let value = self.to_bigint(); | ||||||
|                 let (sign, magnitude) = value.into_parts(); |                 let (sign, magnitude) = value.into_parts(); | ||||||
|  | @ -351,15 +785,38 @@ macro_rules! impl_int { | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         impl<Width: Size> PartialOrd for $value<Width> { |         impl<Width: Size> fmt::Debug for $value<Width> { | ||||||
|             fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> { |             fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||||||
|                 Some(self.cmp(other)) |                 fmt::Display::fmt(self, f) | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         impl<Width: Size> Ord for $value<Width> { |         impl<Width: Size> std::str::FromStr for $value<Width> { | ||||||
|             fn cmp(&self, other: &Self) -> std::cmp::Ordering { |             type Err = ParseIntValueError; | ||||||
|                 self.to_bigint().cmp(&other.to_bigint()) | 
 | ||||||
|  |             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> { |             pub fn bits(&self) -> &Arc<BitVec> { | ||||||
|                 &self.bits |                 &self.bits | ||||||
|             } |             } | ||||||
|  |             pub fn bits_mut(&mut self) -> &mut BitSlice { | ||||||
|  |                 Arc::<BitVec>::make_mut(&mut self.bits) | ||||||
|  |             } | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         impl<Width: Size> ToLiteralBits for $value<Width> { |         impl<Width: Size> ToLiteralBits for $value<Width> { | ||||||
|  | @ -455,6 +915,10 @@ impl UInt { | ||||||
|         let v: BigUint = v.into(); |         let v: BigUint = v.into(); | ||||||
|         Self::new(v.bits().try_into().expect("too big")) |         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
 |     /// gets the smallest `UInt` that fits `r` losslessly, panics if `r` is empty
 | ||||||
|     #[track_caller] |     #[track_caller] | ||||||
|     pub fn range(r: Range<impl Into<BigUint>>) -> Self { |     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
 |     /// gets the smallest `UInt` that fits `r` losslessly, panics if `r` is empty
 | ||||||
|     #[track_caller] |     #[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 { |     pub fn range_inclusive(r: RangeInclusive<impl Into<BigUint>>) -> Self { | ||||||
|         let (start, end) = r.into_inner(); |         let (start, end) = r.into_inner(); | ||||||
|         let start: BigUint = start.into(); |         let start: BigUint = start.into(); | ||||||
|  | @ -474,6 +944,16 @@ impl UInt { | ||||||
|         // so must not take more bits than `end`
 |         // so must not take more bits than `end`
 | ||||||
|         Self::for_value(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 { | impl SInt { | ||||||
|  | @ -580,14 +1060,17 @@ pub trait BoolOrIntType: Type + sealed::BoolOrIntTypeSealed { | ||||||
|     type Width: Size; |     type Width: Size; | ||||||
|     type Signed: GenericConstBool; |     type Signed: GenericConstBool; | ||||||
|     type Value: Clone |     type Value: Clone | ||||||
|         + Ord |         + PartialOrd | ||||||
|  |         + Eq | ||||||
|         + std::hash::Hash |         + std::hash::Hash | ||||||
|         + fmt::Debug |         + fmt::Debug | ||||||
|  |         + fmt::Display | ||||||
|         + Send |         + Send | ||||||
|         + Sync |         + Sync | ||||||
|         + 'static |         + 'static | ||||||
|         + ToExpr<Type = Self> |         + ToExpr<Type = Self> | ||||||
|         + Into<BigInt>; |         + Into<BigInt> | ||||||
|  |         + std::str::FromStr; | ||||||
|     fn width(self) -> usize; |     fn width(self) -> usize; | ||||||
|     fn new(width: <Self::Width as Size>::SizeType) -> Self; |     fn new(width: <Self::Width as Size>::SizeType) -> Self; | ||||||
|     fn new_static() -> Self |     fn new_static() -> Self | ||||||
|  | @ -621,6 +1104,12 @@ pub trait BoolOrIntType: Type + sealed::BoolOrIntTypeSealed { | ||||||
|         let bitslice = &BitSlice::<u8, Lsb0>::from_slice(&bytes)[..width]; |         let bitslice = &BitSlice::<u8, Lsb0>::from_slice(&bytes)[..width]; | ||||||
|         bits.clone_from_bitslice(bitslice); |         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 { |     fn bits_to_bigint(bits: &BitSlice) -> BigInt { | ||||||
|         let sign_byte = if Self::Signed::VALUE && bits.last().as_deref().copied().unwrap_or(false) { |         let sign_byte = if Self::Signed::VALUE && bits.last().as_deref().copied().unwrap_or(false) { | ||||||
|             0xFF |             0xFF | ||||||
|  | @ -654,9 +1143,12 @@ pub trait BoolOrIntType: Type + sealed::BoolOrIntTypeSealed { | ||||||
|             bytes, bit_width, |             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>; |     type Dyn: IntType<Dyn = Self::Dyn, Signed = Self::Signed, Width = DynSize>; | ||||||
|     fn as_dyn_int(self) -> Self::Dyn { |     fn as_dyn_int(self) -> Self::Dyn { | ||||||
|         Self::new_dyn(self.width()) |         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; | pub struct Bool; | ||||||
| 
 | 
 | ||||||
| impl sealed::BoolOrIntTypeSealed for Bool {} | impl sealed::BoolOrIntTypeSealed for Bool {} | ||||||
|  | @ -728,6 +1220,10 @@ impl BoolOrIntType for Bool { | ||||||
|         assert_eq!(bits.len(), 1); |         assert_eq!(bits.len(), 1); | ||||||
|         bits[0] |         bits[0] | ||||||
|     } |     } | ||||||
|  | 
 | ||||||
|  |     fn from_str_without_ty(self, s: &str) -> Result<Self::Value, <Self::Value as FromStr>::Err> { | ||||||
|  |         FromStr::from_str(s) | ||||||
|  |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl Bool { | impl Bool { | ||||||
|  | @ -742,6 +1238,7 @@ impl Bool { | ||||||
| impl Type for Bool { | impl Type for Bool { | ||||||
|     type BaseType = Bool; |     type BaseType = Bool; | ||||||
|     type MaskType = Bool; |     type MaskType = Bool; | ||||||
|  |     type SimValue = bool; | ||||||
|     impl_match_variant_as_self!(); |     impl_match_variant_as_self!(); | ||||||
|     fn mask_type(&self) -> Self::MaskType { |     fn mask_type(&self) -> Self::MaskType { | ||||||
|         Bool |         Bool | ||||||
|  | @ -759,6 +1256,18 @@ impl Type for Bool { | ||||||
|     fn source_location() -> SourceLocation { |     fn source_location() -> SourceLocation { | ||||||
|         SourceLocation::builtin() |         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 { | impl StaticType for Bool { | ||||||
|  | @ -783,6 +1292,13 @@ impl ToLiteralBits for bool { | ||||||
| mod tests { | mod tests { | ||||||
|     use super::*; |     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] |     #[test] | ||||||
|     fn test_uint_for_value() { |     fn test_uint_for_value() { | ||||||
|         assert_eq!(UInt::for_value(0u8).width, 0); |         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(3).width, 3); | ||||||
|         assert_eq!(SInt::for_value(4).width, 4); |         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
 | // SPDX-License-Identifier: LGPL-3.0-or-later
 | ||||||
| // See Notices.txt for copyright information
 | // See Notices.txt for copyright information
 | ||||||
| #![allow(clippy::type_complexity)] | #![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 bitvec::{ptr::BitPtr, slice::BitSlice, vec::BitVec}; | ||||||
| use hashbrown::{hash_map::RawEntryMut, HashMap, HashTable}; | use hashbrown::HashTable; | ||||||
| use serde::{Deserialize, Serialize}; | use serde::{Deserialize, Serialize}; | ||||||
| use std::{ | use std::{ | ||||||
|     any::{Any, TypeId}, |     any::{Any, TypeId}, | ||||||
|  | @ -17,7 +17,7 @@ use std::{ | ||||||
|     sync::{Mutex, RwLock}, |     sync::{Mutex, RwLock}, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| pub mod type_map; | mod type_map; | ||||||
| 
 | 
 | ||||||
| pub trait LazyInternedTrait<T: ?Sized + Send + Sync + 'static>: Send + Sync + Any { | pub trait LazyInternedTrait<T: ?Sized + Send + Sync + 'static>: Send + Sync + Any { | ||||||
|     fn get(&self) -> Interned<T>; |     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> { | 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> { | 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> { | impl<T: ?Sized + 'static + Send + Sync> Default for Interner<T> { | ||||||
|     fn default() -> Self { |     fn default() -> Self { | ||||||
|         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, |         alloc: F, | ||||||
|         value: Cow<'_, T>, |         value: Cow<'_, T>, | ||||||
|     ) -> Interned<T> { |     ) -> Interned<T> { | ||||||
|         let mut map = self.map.lock().unwrap(); |         let mut state = self.state.lock().unwrap(); | ||||||
|         let hasher = map.hasher().clone(); |         let InternerState { table, hasher } = &mut *state; | ||||||
|         let hash = hasher.hash_one(&*value); |         let inner = *table | ||||||
|         let inner = match map.raw_entry_mut().from_hash(hash, |k| **k == *value) { |             .entry( | ||||||
|             RawEntryMut::Occupied(entry) => *entry.key(), |                 hasher.hash_one(&*value), | ||||||
|             RawEntryMut::Vacant(entry) => { |                 |k| **k == *value, | ||||||
|                 *entry |                 |k| hasher.hash_one(&**k), | ||||||
|                     .insert_with_hasher(hash, alloc(value), (), |k| hasher.hash_one(&**k)) |             ) | ||||||
|                     .0 |             .or_insert_with(|| alloc(value)) | ||||||
|             } |             .get(); | ||||||
|         }; |  | ||||||
|         Interned { inner } |         Interned { inner } | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | @ -742,7 +749,7 @@ pub trait MemoizeGeneric: 'static + Send + Sync + Hash + Eq + Copy { | ||||||
|     fn get_cow(self, input: Self::InputCow<'_>) -> Self::Output { |     fn get_cow(self, input: Self::InputCow<'_>) -> Self::Output { | ||||||
|         static TYPE_ID_MAP: TypeIdMap = TypeIdMap::new(); |         static TYPE_ID_MAP: TypeIdMap = TypeIdMap::new(); | ||||||
|         let map: &RwLock<( |         let map: &RwLock<( | ||||||
|             hashbrown::hash_map::DefaultHashBuilder, |             DefaultBuildHasher, | ||||||
|             HashTable<(Self, Self::InputOwned, Self::Output)>, |             HashTable<(Self, Self::InputOwned, Self::Output)>, | ||||||
|         )> = TYPE_ID_MAP.get_or_insert_default(); |         )> = TYPE_ID_MAP.get_or_insert_default(); | ||||||
|         fn hash_eq_key<'a, 'b, T: MemoizeGeneric>( |         fn hash_eq_key<'a, 'b, T: MemoizeGeneric>( | ||||||
|  |  | ||||||
|  | @ -1,10 +1,8 @@ | ||||||
| // SPDX-License-Identifier: LGPL-3.0-or-later
 | // SPDX-License-Identifier: LGPL-3.0-or-later
 | ||||||
| // See Notices.txt for copyright information
 | // See Notices.txt for copyright information
 | ||||||
| use hashbrown::HashMap; |  | ||||||
| use std::{ | use std::{ | ||||||
|     any::{Any, TypeId}, |     any::{Any, TypeId}, | ||||||
|     hash::{BuildHasher, Hasher}, |     hash::{BuildHasher, Hasher}, | ||||||
|     ptr::NonNull, |  | ||||||
|     sync::RwLock, |     sync::RwLock, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | @ -75,59 +73,36 @@ impl BuildHasher for TypeIdBuildHasher { | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| struct Value(NonNull<dyn Any + Send + Sync>); | pub(crate) struct TypeIdMap( | ||||||
| 
 |     RwLock<hashbrown::HashMap<TypeId, &'static (dyn Any + Send + Sync), TypeIdBuildHasher>>, | ||||||
| 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>>); |  | ||||||
| 
 | 
 | ||||||
| impl TypeIdMap { | impl TypeIdMap { | ||||||
|     pub const fn new() -> Self { |     pub(crate) const fn new() -> Self { | ||||||
|         Self(RwLock::new(HashMap::with_hasher(TypeIdBuildHasher))) |         Self(RwLock::new(hashbrown::HashMap::with_hasher( | ||||||
|  |             TypeIdBuildHasher, | ||||||
|  |         ))) | ||||||
|     } |     } | ||||||
|     #[cold] |     #[cold] | ||||||
|     unsafe fn insert_slow( |     fn insert_slow( | ||||||
|         &self, |         &self, | ||||||
|         type_id: TypeId, |         type_id: TypeId, | ||||||
|         make: fn() -> Box<dyn Any + Sync + Send>, |         make: fn() -> Box<dyn Any + Sync + Send>, | ||||||
|     ) -> &(dyn Any + Sync + Send) { |     ) -> &'static (dyn Any + Sync + Send) { | ||||||
|         let value = Value::new(make()); |         let value = Box::leak(make()); | ||||||
|         let mut write_guard = self.0.write().unwrap(); |         let mut write_guard = self.0.write().unwrap(); | ||||||
|         unsafe { |         *write_guard.entry(type_id).or_insert(value) | ||||||
|             write_guard |  | ||||||
|                 .entry(type_id) |  | ||||||
|                 .or_insert(value) |  | ||||||
|                 .get_transmute_lifetime() |  | ||||||
|         } |  | ||||||
|     } |     } | ||||||
|     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 type_id = TypeId::of::<T>(); | ||||||
|         let read_guard = self.0.read().unwrap(); |         let read_guard = self.0.read().unwrap(); | ||||||
|         let retval = read_guard |         let retval = read_guard.get(&type_id).map(|v| *v); | ||||||
|             .get(&type_id) |  | ||||||
|             .map(|v| unsafe { Value::get_transmute_lifetime(v) }); |  | ||||||
|         drop(read_guard); |         drop(read_guard); | ||||||
|         let retval = match retval { |         let retval = match retval { | ||||||
|             Some(retval) => 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)] | #[doc(inline)] | ||||||
| pub use fayalite_proc_macros::hdl; | pub use fayalite_proc_macros::hdl; | ||||||
| 
 | 
 | ||||||
|  | pub use bitvec; | ||||||
|  | 
 | ||||||
| /// struct used as a placeholder when applying defaults
 | /// struct used as a placeholder when applying defaults
 | ||||||
| #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Default)] | #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Default)] | ||||||
| pub struct __; | pub struct __; | ||||||
|  | @ -96,6 +98,7 @@ pub mod int; | ||||||
| pub mod intern; | pub mod intern; | ||||||
| pub mod memory; | pub mod memory; | ||||||
| pub mod module; | pub mod module; | ||||||
|  | pub mod phantom_const; | ||||||
| pub mod prelude; | pub mod prelude; | ||||||
| pub mod reg; | pub mod reg; | ||||||
| pub mod reset; | pub mod reset; | ||||||
|  |  | ||||||
|  | @ -470,7 +470,7 @@ pub enum ReadUnderWrite { | ||||||
|     Undefined, |     Undefined, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #[derive(Copy, Clone, PartialEq, Eq, Hash)] | #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] | ||||||
| struct MemImpl<Element: Type, Len: Size, P> { | struct MemImpl<Element: Type, Len: Size, P> { | ||||||
|     scoped_name: ScopedNameId, |     scoped_name: ScopedNameId, | ||||||
|     source_location: SourceLocation, |     source_location: SourceLocation, | ||||||
|  | @ -1082,6 +1082,7 @@ pub fn splat_mask<T: Type>(ty: T, value: Expr<Bool>) -> Expr<AsMask<T>> { | ||||||
|             ) |             ) | ||||||
|             .to_expr(), |             .to_expr(), | ||||||
|         )), |         )), | ||||||
|  |         CanonicalType::PhantomConst(_) => Expr::from_canonical(Expr::canonical(().to_expr())), | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -21,18 +21,20 @@ use crate::{ | ||||||
|     memory::{Mem, MemBuilder, MemBuilderTarget, PortName}, |     memory::{Mem, MemBuilder, MemBuilderTarget, PortName}, | ||||||
|     reg::Reg, |     reg::Reg, | ||||||
|     reset::{AsyncReset, Reset, ResetType, ResetTypeDispatch, SyncReset}, |     reset::{AsyncReset, Reset, ResetType, ResetTypeDispatch, SyncReset}, | ||||||
|  |     sim::{ExternModuleSimGenerator, ExternModuleSimulation}, | ||||||
|     source_location::SourceLocation, |     source_location::SourceLocation, | ||||||
|     ty::{CanonicalType, Type}, |     ty::{CanonicalType, Type}, | ||||||
|     util::ScopedRef, |     util::{HashMap, HashSet, ScopedRef}, | ||||||
|     wire::{IncompleteWire, Wire}, |     wire::{IncompleteWire, Wire}, | ||||||
| }; | }; | ||||||
| use hashbrown::{hash_map::Entry, HashMap, HashSet}; | use hashbrown::hash_map::Entry; | ||||||
| use num_bigint::BigInt; | use num_bigint::BigInt; | ||||||
| use std::{ | use std::{ | ||||||
|     cell::RefCell, |     cell::RefCell, | ||||||
|     collections::VecDeque, |     collections::VecDeque, | ||||||
|     convert::Infallible, |     convert::Infallible, | ||||||
|     fmt, |     fmt, | ||||||
|  |     future::IntoFuture, | ||||||
|     hash::{Hash, Hasher}, |     hash::{Hash, Hasher}, | ||||||
|     iter::FusedIterator, |     iter::FusedIterator, | ||||||
|     marker::PhantomData, |     marker::PhantomData, | ||||||
|  | @ -1081,6 +1083,7 @@ pub struct ExternModuleBody< | ||||||
| > { | > { | ||||||
|     pub verilog_name: Interned<str>, |     pub verilog_name: Interned<str>, | ||||||
|     pub parameters: P, |     pub parameters: P, | ||||||
|  |     pub simulation: Option<ExternModuleSimulation>, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl From<ExternModuleBody<Vec<ExternModuleParameter>>> for ExternModuleBody { | impl From<ExternModuleBody<Vec<ExternModuleParameter>>> for ExternModuleBody { | ||||||
|  | @ -1088,11 +1091,13 @@ impl From<ExternModuleBody<Vec<ExternModuleParameter>>> for ExternModuleBody { | ||||||
|         let ExternModuleBody { |         let ExternModuleBody { | ||||||
|             verilog_name, |             verilog_name, | ||||||
|             parameters, |             parameters, | ||||||
|  |             simulation, | ||||||
|         } = value; |         } = value; | ||||||
|         let parameters = Intern::intern_owned(parameters); |         let parameters = Intern::intern_owned(parameters); | ||||||
|         Self { |         Self { | ||||||
|             verilog_name, |             verilog_name, | ||||||
|             parameters, |             parameters, | ||||||
|  |             simulation, | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | @ -1283,10 +1288,12 @@ impl<T: BundleType> fmt::Debug for DebugModuleBody<T> { | ||||||
|             ModuleBody::Extern(ExternModuleBody { |             ModuleBody::Extern(ExternModuleBody { | ||||||
|                 verilog_name, |                 verilog_name, | ||||||
|                 parameters, |                 parameters, | ||||||
|  |                 simulation, | ||||||
|             }) => { |             }) => { | ||||||
|                 debug_struct |                 debug_struct | ||||||
|                     .field("verilog_name", verilog_name) |                     .field("verilog_name", verilog_name) | ||||||
|                     .field("parameters", parameters); |                     .field("parameters", parameters) | ||||||
|  |                     .field("simulation", simulation); | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|         debug_struct.finish_non_exhaustive() |         debug_struct.finish_non_exhaustive() | ||||||
|  | @ -1490,6 +1497,9 @@ impl TargetState { | ||||||
|                         }) |                         }) | ||||||
|                         .collect(), |                         .collect(), | ||||||
|                 }, |                 }, | ||||||
|  |                 CanonicalType::PhantomConst(_) => TargetStateInner::Decomposed { | ||||||
|  |                     subtargets: HashMap::default(), | ||||||
|  |                 }, | ||||||
|                 CanonicalType::Array(ty) => TargetStateInner::Decomposed { |                 CanonicalType::Array(ty) => TargetStateInner::Decomposed { | ||||||
|                     subtargets: (0..ty.len()) |                     subtargets: (0..ty.len()) | ||||||
|                         .map(|index| { |                         .map(|index| { | ||||||
|  | @ -1758,6 +1768,7 @@ impl AssertValidityState { | ||||||
|             ModuleBody::Extern(ExternModuleBody { |             ModuleBody::Extern(ExternModuleBody { | ||||||
|                 verilog_name: _, |                 verilog_name: _, | ||||||
|                 parameters: _, |                 parameters: _, | ||||||
|  |                 simulation: _, | ||||||
|             }) => {} |             }) => {} | ||||||
|             ModuleBody::Normal(NormalModuleBody { body }) => { |             ModuleBody::Normal(NormalModuleBody { body }) => { | ||||||
|                 let body = self.make_block_index(body); |                 let body = self.make_block_index(body); | ||||||
|  | @ -1853,7 +1864,7 @@ impl<T: BundleType> Module<T> { | ||||||
|         AssertValidityState { |         AssertValidityState { | ||||||
|             module: self.canonical(), |             module: self.canonical(), | ||||||
|             blocks: vec![], |             blocks: vec![], | ||||||
|             target_states: HashMap::with_capacity(64), |             target_states: HashMap::with_capacity_and_hasher(64, Default::default()), | ||||||
|         } |         } | ||||||
|         .assert_validity(); |         .assert_validity(); | ||||||
|     } |     } | ||||||
|  | @ -2105,6 +2116,7 @@ impl ModuleBuilder { | ||||||
|             ModuleKind::Extern => ModuleBody::Extern(ExternModuleBody { |             ModuleKind::Extern => ModuleBody::Extern(ExternModuleBody { | ||||||
|                 verilog_name: name.0, |                 verilog_name: name.0, | ||||||
|                 parameters: vec![], |                 parameters: vec![], | ||||||
|  |                 simulation: None, | ||||||
|             }), |             }), | ||||||
|             ModuleKind::Normal => ModuleBody::Normal(NormalModuleBody { |             ModuleKind::Normal => ModuleBody::Normal(NormalModuleBody { | ||||||
|                 body: BuilderModuleBody { |                 body: BuilderModuleBody { | ||||||
|  | @ -2113,8 +2125,8 @@ impl ModuleBuilder { | ||||||
|                         incomplete_declarations: vec![], |                         incomplete_declarations: vec![], | ||||||
|                         stmts: vec![], |                         stmts: vec![], | ||||||
|                     }], |                     }], | ||||||
|                     annotations_map: HashMap::new(), |                     annotations_map: HashMap::default(), | ||||||
|                     memory_map: HashMap::new(), |                     memory_map: HashMap::default(), | ||||||
|                 }, |                 }, | ||||||
|             }), |             }), | ||||||
|         }; |         }; | ||||||
|  | @ -2124,7 +2136,7 @@ impl ModuleBuilder { | ||||||
|             impl_: RefCell::new(ModuleBuilderImpl { |             impl_: RefCell::new(ModuleBuilderImpl { | ||||||
|                 body, |                 body, | ||||||
|                 io: vec![], |                 io: vec![], | ||||||
|                 io_indexes: HashMap::new(), |                 io_indexes: HashMap::default(), | ||||||
|                 module_annotations: vec![], |                 module_annotations: vec![], | ||||||
|             }), |             }), | ||||||
|         }; |         }; | ||||||
|  | @ -2171,6 +2183,7 @@ impl ModuleBuilder { | ||||||
|             .builder_extern_body() |             .builder_extern_body() | ||||||
|             .verilog_name = name.intern(); |             .verilog_name = name.intern(); | ||||||
|     } |     } | ||||||
|  |     #[track_caller] | ||||||
|     pub fn parameter(&self, name: impl AsRef<str>, value: ExternModuleParameterValue) { |     pub fn parameter(&self, name: impl AsRef<str>, value: ExternModuleParameterValue) { | ||||||
|         let name = name.as_ref(); |         let name = name.as_ref(); | ||||||
|         self.impl_ |         self.impl_ | ||||||
|  | @ -2183,6 +2196,7 @@ impl ModuleBuilder { | ||||||
|                 value, |                 value, | ||||||
|             }); |             }); | ||||||
|     } |     } | ||||||
|  |     #[track_caller] | ||||||
|     pub fn parameter_int(&self, name: impl AsRef<str>, value: impl Into<BigInt>) { |     pub fn parameter_int(&self, name: impl AsRef<str>, value: impl Into<BigInt>) { | ||||||
|         let name = name.as_ref(); |         let name = name.as_ref(); | ||||||
|         let value = value.into(); |         let value = value.into(); | ||||||
|  | @ -2196,6 +2210,7 @@ impl ModuleBuilder { | ||||||
|                 value: ExternModuleParameterValue::Integer(value), |                 value: ExternModuleParameterValue::Integer(value), | ||||||
|             }); |             }); | ||||||
|     } |     } | ||||||
|  |     #[track_caller] | ||||||
|     pub fn parameter_str(&self, name: impl AsRef<str>, value: impl AsRef<str>) { |     pub fn parameter_str(&self, name: impl AsRef<str>, value: impl AsRef<str>) { | ||||||
|         let name = name.as_ref(); |         let name = name.as_ref(); | ||||||
|         let value = value.as_ref(); |         let value = value.as_ref(); | ||||||
|  | @ -2209,6 +2224,7 @@ impl ModuleBuilder { | ||||||
|                 value: ExternModuleParameterValue::String(value.intern()), |                 value: ExternModuleParameterValue::String(value.intern()), | ||||||
|             }); |             }); | ||||||
|     } |     } | ||||||
|  |     #[track_caller] | ||||||
|     pub fn parameter_raw_verilog(&self, name: impl AsRef<str>, raw_verilog: impl AsRef<str>) { |     pub fn parameter_raw_verilog(&self, name: impl AsRef<str>, raw_verilog: impl AsRef<str>) { | ||||||
|         let name = name.as_ref(); |         let name = name.as_ref(); | ||||||
|         let raw_verilog = raw_verilog.as_ref(); |         let raw_verilog = raw_verilog.as_ref(); | ||||||
|  | @ -2222,6 +2238,26 @@ impl ModuleBuilder { | ||||||
|                 value: ExternModuleParameterValue::RawVerilog(raw_verilog.intern()), |                 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] | #[track_caller] | ||||||
|  |  | ||||||
|  | @ -24,8 +24,9 @@ use crate::{ | ||||||
|     }, |     }, | ||||||
|     prelude::*, |     prelude::*, | ||||||
|     reset::{ResetType, ResetTypeDispatch}, |     reset::{ResetType, ResetTypeDispatch}, | ||||||
|  |     util::{HashMap, HashSet}, | ||||||
| }; | }; | ||||||
| use hashbrown::{hash_map::Entry, HashMap, HashSet}; | use hashbrown::hash_map::Entry; | ||||||
| use num_bigint::BigInt; | use num_bigint::BigInt; | ||||||
| use petgraph::unionfind::UnionFind; | use petgraph::unionfind::UnionFind; | ||||||
| use std::{fmt, marker::PhantomData}; | use std::{fmt, marker::PhantomData}; | ||||||
|  | @ -155,6 +156,7 @@ impl ResetsLayout { | ||||||
|                     CanonicalType::SyncReset(_) => ResetsLayout::SyncReset, |                     CanonicalType::SyncReset(_) => ResetsLayout::SyncReset, | ||||||
|                     CanonicalType::Reset(_) => ResetsLayout::Reset, |                     CanonicalType::Reset(_) => ResetsLayout::Reset, | ||||||
|                     CanonicalType::Clock(_) => ResetsLayout::NoResets, |                     CanonicalType::Clock(_) => ResetsLayout::NoResets, | ||||||
|  |                     CanonicalType::PhantomConst(_) => ResetsLayout::NoResets, | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|  | @ -407,7 +409,8 @@ impl Resets { | ||||||
|             | CanonicalType::Bool(_) |             | CanonicalType::Bool(_) | ||||||
|             | CanonicalType::AsyncReset(_) |             | CanonicalType::AsyncReset(_) | ||||||
|             | CanonicalType::SyncReset(_) |             | CanonicalType::SyncReset(_) | ||||||
|             | CanonicalType::Clock(_) => Ok(self.ty), |             | CanonicalType::Clock(_) | ||||||
|  |             | CanonicalType::PhantomConst(_) => Ok(self.ty), | ||||||
|             CanonicalType::Array(ty) => Ok(CanonicalType::Array(Array::new_dyn( |             CanonicalType::Array(ty) => Ok(CanonicalType::Array(Array::new_dyn( | ||||||
|                 self.array_elements().substituted_type( |                 self.array_elements().substituted_type( | ||||||
|                     reset_graph, |                     reset_graph, | ||||||
|  | @ -998,7 +1001,8 @@ fn cast_bit_op<P: Pass, T: Type, A: Type>( | ||||||
|                             CanonicalType::Array(_) |                             CanonicalType::Array(_) | ||||||
|                             | CanonicalType::Enum(_) |                             | CanonicalType::Enum(_) | ||||||
|                             | CanonicalType::Bundle(_) |                             | CanonicalType::Bundle(_) | ||||||
|                             | CanonicalType::Reset(_) => unreachable!(), |                             | CanonicalType::Reset(_) | ||||||
|  |                             | CanonicalType::PhantomConst(_) => unreachable!(), | ||||||
|                             $(CanonicalType::$Variant(ty) => Expr::expr_enum($arg.cast_to(ty)),)* |                             $(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::Enum(_) | ||||||
|                             | CanonicalType::Bundle(_) |                             | CanonicalType::Bundle(_) | ||||||
|                             | CanonicalType::Reset(_) => unreachable!(), |                             | CanonicalType::Reset(_) => unreachable!(), | ||||||
|  |                             CanonicalType::PhantomConst(_) => Expr::expr_enum(arg), | ||||||
|                             $(CanonicalType::$Variant(_) => { |                             $(CanonicalType::$Variant(_) => { | ||||||
|                                 let arg = Expr::<$Variant>::from_canonical(arg); |                                 let arg = Expr::<$Variant>::from_canonical(arg); | ||||||
|                                 match_expr_ty!(arg, UInt, SInt, Bool, AsyncReset, SyncReset, Clock) |                                 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::UIntLiteral(expr) => Ok(expr.run_pass(pass_args)?.map(ExprEnum::from)), | ||||||
|             ExprEnum::SIntLiteral(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::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::BundleLiteral(expr) => Ok(expr.run_pass(pass_args)?.map(ExprEnum::from)), | ||||||
|             ExprEnum::ArrayLiteral(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)), |             ExprEnum::EnumLiteral(expr) => Ok(expr.run_pass(pass_args)?.map(ExprEnum::from)), | ||||||
|  | @ -1670,7 +1676,8 @@ impl RunPassDispatch for AnyReg { | ||||||
|                 | CanonicalType::Enum(_) |                 | CanonicalType::Enum(_) | ||||||
|                 | CanonicalType::Bundle(_) |                 | CanonicalType::Bundle(_) | ||||||
|                 | CanonicalType::Reset(_) |                 | 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!([] UInt); | ||||||
| impl_run_pass_copy!([] usize); | impl_run_pass_copy!([] usize); | ||||||
| impl_run_pass_copy!([] FormalKind); | impl_run_pass_copy!([] FormalKind); | ||||||
|  | impl_run_pass_copy!([] PhantomConst); | ||||||
| 
 | 
 | ||||||
| macro_rules! impl_run_pass_for_struct { | macro_rules! impl_run_pass_for_struct { | ||||||
|     ( |     ( | ||||||
|  | @ -2244,9 +2252,9 @@ pub fn deduce_resets( | ||||||
|     fallback_to_sync_reset: bool, |     fallback_to_sync_reset: bool, | ||||||
| ) -> Result<Interned<Module<Bundle>>, DeduceResetsError> { | ) -> Result<Interned<Module<Bundle>>, DeduceResetsError> { | ||||||
|     let mut state = State { |     let mut state = State { | ||||||
|         modules_added_to_graph: HashSet::new(), |         modules_added_to_graph: HashSet::default(), | ||||||
|         substituted_modules: HashMap::new(), |         substituted_modules: HashMap::default(), | ||||||
|         expr_resets: HashMap::new(), |         expr_resets: HashMap::default(), | ||||||
|         reset_graph: ResetGraph::default(), |         reset_graph: ResetGraph::default(), | ||||||
|         fallback_to_sync_reset, |         fallback_to_sync_reset, | ||||||
|     }; |     }; | ||||||
|  |  | ||||||
|  | @ -18,10 +18,10 @@ use crate::{ | ||||||
|     }, |     }, | ||||||
|     source_location::SourceLocation, |     source_location::SourceLocation, | ||||||
|     ty::{CanonicalType, Type}, |     ty::{CanonicalType, Type}, | ||||||
|  |     util::HashMap, | ||||||
|     wire::Wire, |     wire::Wire, | ||||||
| }; | }; | ||||||
| use core::fmt; | use core::fmt; | ||||||
| use hashbrown::HashMap; |  | ||||||
| 
 | 
 | ||||||
| #[derive(Debug)] | #[derive(Debug)] | ||||||
| pub enum SimplifyEnumsError { | pub enum SimplifyEnumsError { | ||||||
|  | @ -69,7 +69,8 @@ fn contains_any_enum_types(ty: CanonicalType) -> bool { | ||||||
|                 | CanonicalType::AsyncReset(_) |                 | CanonicalType::AsyncReset(_) | ||||||
|                 | CanonicalType::SyncReset(_) |                 | CanonicalType::SyncReset(_) | ||||||
|                 | CanonicalType::Reset(_) |                 | CanonicalType::Reset(_) | ||||||
|                 | CanonicalType::Clock(_) => false, |                 | CanonicalType::Clock(_) | ||||||
|  |                 | CanonicalType::PhantomConst(_) => false, | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  | @ -512,7 +513,8 @@ impl State { | ||||||
|             | CanonicalType::AsyncReset(_) |             | CanonicalType::AsyncReset(_) | ||||||
|             | CanonicalType::SyncReset(_) |             | CanonicalType::SyncReset(_) | ||||||
|             | CanonicalType::Reset(_) |             | CanonicalType::Reset(_) | ||||||
|             | CanonicalType::Clock(_) => unreachable!(), |             | CanonicalType::Clock(_) | ||||||
|  |             | CanonicalType::PhantomConst(_) => unreachable!(), | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | @ -577,7 +579,8 @@ fn connect_port( | ||||||
|         | (CanonicalType::Clock(_), _) |         | (CanonicalType::Clock(_), _) | ||||||
|         | (CanonicalType::AsyncReset(_), _) |         | (CanonicalType::AsyncReset(_), _) | ||||||
|         | (CanonicalType::SyncReset(_), _) |         | (CanonicalType::SyncReset(_), _) | ||||||
|         | (CanonicalType::Reset(_), _) => unreachable!( |         | (CanonicalType::Reset(_), _) | ||||||
|  |         | (CanonicalType::PhantomConst(_), _) => unreachable!( | ||||||
|             "trying to connect memory ports:\n{:?}\n{:?}", |             "trying to connect memory ports:\n{:?}\n{:?}", | ||||||
|             Expr::ty(lhs), |             Expr::ty(lhs), | ||||||
|             Expr::ty(rhs), |             Expr::ty(rhs), | ||||||
|  | @ -665,6 +668,7 @@ impl Folder for State { | ||||||
|             ExprEnum::UIntLiteral(_) |             ExprEnum::UIntLiteral(_) | ||||||
|             | ExprEnum::SIntLiteral(_) |             | ExprEnum::SIntLiteral(_) | ||||||
|             | ExprEnum::BoolLiteral(_) |             | ExprEnum::BoolLiteral(_) | ||||||
|  |             | ExprEnum::PhantomConst(_) | ||||||
|             | ExprEnum::BundleLiteral(_) |             | ExprEnum::BundleLiteral(_) | ||||||
|             | ExprEnum::ArrayLiteral(_) |             | ExprEnum::ArrayLiteral(_) | ||||||
|             | ExprEnum::Uninit(_) |             | ExprEnum::Uninit(_) | ||||||
|  | @ -923,7 +927,8 @@ impl Folder for State { | ||||||
|             | CanonicalType::Clock(_) |             | CanonicalType::Clock(_) | ||||||
|             | CanonicalType::AsyncReset(_) |             | CanonicalType::AsyncReset(_) | ||||||
|             | CanonicalType::SyncReset(_) |             | 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, |     kind: SimplifyEnumsKind, | ||||||
| ) -> Result<Interned<Module<Bundle>>, SimplifyEnumsError> { | ) -> Result<Interned<Module<Bundle>>, SimplifyEnumsError> { | ||||||
|     module.fold(&mut State { |     module.fold(&mut State { | ||||||
|         enum_types: HashMap::new(), |         enum_types: HashMap::default(), | ||||||
|         replacement_mem_ports: HashMap::new(), |         replacement_mem_ports: HashMap::default(), | ||||||
|         kind, |         kind, | ||||||
|         module_state_stack: vec![], |         module_state_stack: vec![], | ||||||
|     }) |     }) | ||||||
|  |  | ||||||
|  | @ -14,11 +14,10 @@ use crate::{ | ||||||
|     }, |     }, | ||||||
|     source_location::SourceLocation, |     source_location::SourceLocation, | ||||||
|     ty::{CanonicalType, Type}, |     ty::{CanonicalType, Type}, | ||||||
|     util::MakeMutSlice, |     util::{HashMap, MakeMutSlice}, | ||||||
|     wire::Wire, |     wire::Wire, | ||||||
| }; | }; | ||||||
| use bitvec::{slice::BitSlice, vec::BitVec}; | use bitvec::{slice::BitSlice, vec::BitVec}; | ||||||
| use hashbrown::HashMap; |  | ||||||
| use std::{ | use std::{ | ||||||
|     convert::Infallible, |     convert::Infallible, | ||||||
|     fmt::Write, |     fmt::Write, | ||||||
|  | @ -62,6 +61,7 @@ enum MemSplit { | ||||||
|     Bundle { |     Bundle { | ||||||
|         fields: Rc<[MemSplit]>, |         fields: Rc<[MemSplit]>, | ||||||
|     }, |     }, | ||||||
|  |     PhantomConst, | ||||||
|     Single { |     Single { | ||||||
|         output_mem: Option<Mem>, |         output_mem: Option<Mem>, | ||||||
|         element_type: SingleType, |         element_type: SingleType, | ||||||
|  | @ -76,6 +76,7 @@ impl MemSplit { | ||||||
|     fn mark_changed_element_type(self) -> Self { |     fn mark_changed_element_type(self) -> Self { | ||||||
|         match self { |         match self { | ||||||
|             MemSplit::Bundle { fields: _ } => self, |             MemSplit::Bundle { fields: _ } => self, | ||||||
|  |             MemSplit::PhantomConst => self, | ||||||
|             MemSplit::Single { |             MemSplit::Single { | ||||||
|                 output_mem, |                 output_mem, | ||||||
|                 element_type, |                 element_type, | ||||||
|  | @ -97,6 +98,7 @@ impl MemSplit { | ||||||
|                     .map(|field| Self::new(field.ty).mark_changed_element_type()) |                     .map(|field| Self::new(field.ty).mark_changed_element_type()) | ||||||
|                     .collect(), |                     .collect(), | ||||||
|             }, |             }, | ||||||
|  |             CanonicalType::PhantomConst(_) => MemSplit::PhantomConst, | ||||||
|             CanonicalType::Array(ty) => { |             CanonicalType::Array(ty) => { | ||||||
|                 let element = MemSplit::new(ty.element()); |                 let element = MemSplit::new(ty.element()); | ||||||
|                 if let Self::Single { |                 if let Self::Single { | ||||||
|  | @ -339,6 +341,7 @@ impl SplitMemState<'_, '_> { | ||||||
|                     self.split_state_stack.pop(); |                     self.split_state_stack.pop(); | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|  |             MemSplit::PhantomConst => {} | ||||||
|             MemSplit::Single { |             MemSplit::Single { | ||||||
|                 output_mem, |                 output_mem, | ||||||
|                 element_type: single_type, |                 element_type: single_type, | ||||||
|  | @ -538,7 +541,12 @@ impl ModuleState { | ||||||
|             }; |             }; | ||||||
|         loop { |         loop { | ||||||
|             match input_element_type { |             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(_) |                 CanonicalType::Enum(_) | ||||||
|                     if input_array_types |                     if input_array_types | ||||||
|                         .first() |                         .first() | ||||||
|  | @ -743,7 +751,8 @@ impl ModuleState { | ||||||
|                 .. |                 .. | ||||||
|             } |             } | ||||||
|             | MemSplit::Bundle { .. } |             | MemSplit::Bundle { .. } | ||||||
|             | MemSplit::Array { .. } => { |             | MemSplit::Array { .. } | ||||||
|  |             | MemSplit::PhantomConst => { | ||||||
|                 let mut replacement_ports = Vec::with_capacity(input_mem.ports().len()); |                 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_rdata = Vec::with_capacity(input_mem.ports().len()); | ||||||
|                 let mut wire_port_wdata = 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, |             module, | ||||||
|             ModuleState { |             ModuleState { | ||||||
|                 output_module: None, |                 output_module: None, | ||||||
|                 memories: HashMap::new(), |                 memories: HashMap::default(), | ||||||
|             }, |             }, | ||||||
|         ); |         ); | ||||||
|         let mut this = PushedState::push_module(self, module); |         let mut this = PushedState::push_module(self, module); | ||||||
|  |  | ||||||
|  | @ -28,8 +28,10 @@ use crate::{ | ||||||
|         NormalModuleBody, ScopedNameId, Stmt, StmtConnect, StmtDeclaration, StmtFormal, StmtIf, |         NormalModuleBody, ScopedNameId, Stmt, StmtConnect, StmtDeclaration, StmtFormal, StmtIf, | ||||||
|         StmtInstance, StmtMatch, StmtReg, StmtWire, |         StmtInstance, StmtMatch, StmtReg, StmtWire, | ||||||
|     }, |     }, | ||||||
|  |     phantom_const::PhantomConst, | ||||||
|     reg::Reg, |     reg::Reg, | ||||||
|     reset::{AsyncReset, Reset, ResetType, SyncReset}, |     reset::{AsyncReset, Reset, ResetType, SyncReset}, | ||||||
|  |     sim::ExternModuleSimulation, | ||||||
|     source_location::SourceLocation, |     source_location::SourceLocation, | ||||||
|     ty::{CanonicalType, Type}, |     ty::{CanonicalType, Type}, | ||||||
|     wire::Wire, |     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_cover_with_enable, MakeFormalExpr, | ||||||
|     }, |     }, | ||||||
|     hdl, hdl_module, |     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}, |     memory::{Mem, MemBuilder, ReadUnderWrite}, | ||||||
|     module::{ |     module::{ | ||||||
|         annotate, connect, connect_any, incomplete_wire, instance, memory, memory_array, |         annotate, connect, connect_any, incomplete_wire, instance, memory, memory_array, | ||||||
|         memory_with_init, reg_builder, wire, Instance, Module, ModuleBuilder, |         memory_with_init, reg_builder, wire, Instance, Module, ModuleBuilder, | ||||||
|     }, |     }, | ||||||
|  |     phantom_const::PhantomConst, | ||||||
|     reg::Reg, |     reg::Reg, | ||||||
|     reset::{AsyncReset, Reset, SyncReset, ToAsyncReset, ToReset, ToSyncReset}, |     reset::{AsyncReset, Reset, SyncReset, ToAsyncReset, ToReset, ToSyncReset}, | ||||||
|  |     sim::{ | ||||||
|  |         time::{SimDuration, SimInstant}, | ||||||
|  |         value::{SimValue, ToSimValue, ToSimValueWithType}, | ||||||
|  |         ExternModuleSimulationState, Simulation, | ||||||
|  |     }, | ||||||
|     source_location::SourceLocation, |     source_location::SourceLocation, | ||||||
|     ty::{AsMask, CanonicalType, Type}, |     ty::{AsMask, CanonicalType, Type}, | ||||||
|     util::{ConstUsize, GenericConstUsize}, |     util::{ConstUsize, GenericConstUsize}, | ||||||
|     wire::Wire, |     wire::Wire, | ||||||
|     __, |     __, | ||||||
| }; | }; | ||||||
|  | pub use bitvec::{slice::BitSlice, vec::BitVec}; | ||||||
|  |  | ||||||
|  | @ -7,6 +7,7 @@ use crate::{ | ||||||
|     source_location::SourceLocation, |     source_location::SourceLocation, | ||||||
|     ty::{impl_match_variant_as_self, CanonicalType, StaticType, Type, TypeProperties}, |     ty::{impl_match_variant_as_self, CanonicalType, StaticType, Type, TypeProperties}, | ||||||
| }; | }; | ||||||
|  | use bitvec::slice::BitSlice; | ||||||
| 
 | 
 | ||||||
| mod sealed { | mod sealed { | ||||||
|     pub trait ResetTypeSealed {} |     pub trait ResetTypeSealed {} | ||||||
|  | @ -45,6 +46,7 @@ macro_rules! reset_type { | ||||||
|         impl Type for $name { |         impl Type for $name { | ||||||
|             type BaseType = $name; |             type BaseType = $name; | ||||||
|             type MaskType = Bool; |             type MaskType = Bool; | ||||||
|  |             type SimValue = bool; | ||||||
| 
 | 
 | ||||||
|             impl_match_variant_as_self!(); |             impl_match_variant_as_self!(); | ||||||
| 
 | 
 | ||||||
|  | @ -66,6 +68,21 @@ macro_rules! reset_type { | ||||||
|                 }; |                 }; | ||||||
|                 retval |                 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 { |         impl $name { | ||||||
|  |  | ||||||
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							|  | @ -7,10 +7,9 @@ use crate::{ | ||||||
|     intern::{Intern, Interned, Memoize}, |     intern::{Intern, Interned, Memoize}, | ||||||
|     source_location::SourceLocation, |     source_location::SourceLocation, | ||||||
|     ty::CanonicalType, |     ty::CanonicalType, | ||||||
|     util::get_many_mut, |     util::{get_many_mut, HashMap, HashSet}, | ||||||
| }; | }; | ||||||
| use bitvec::{boxed::BitBox, slice::BitSlice}; | use bitvec::{boxed::BitBox, slice::BitSlice}; | ||||||
| use hashbrown::{HashMap, HashSet}; |  | ||||||
| use num_bigint::BigInt; | use num_bigint::BigInt; | ||||||
| use num_traits::{One, Signed, ToPrimitive, Zero}; | use num_traits::{One, Signed, ToPrimitive, Zero}; | ||||||
| use std::{ | 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, |         TraceModuleIO, TraceReg, TraceSInt, TraceScalar, TraceScalarId, TraceScope, TraceSyncReset, | ||||||
|         TraceUInt, TraceWire, TraceWriter, TraceWriterDecls, |         TraceUInt, TraceWire, TraceWriter, TraceWriterDecls, | ||||||
|     }, |     }, | ||||||
|  |     util::HashMap, | ||||||
| }; | }; | ||||||
| use bitvec::{order::Lsb0, slice::BitSlice}; | use bitvec::{order::Lsb0, slice::BitSlice}; | ||||||
| use hashbrown::{hash_map::Entry, HashMap}; | use hashbrown::hash_map::Entry; | ||||||
| use std::{ | use std::{ | ||||||
|     fmt::{self, Write as _}, |     fmt::{self, Write as _}, | ||||||
|     io, mem, |     io, mem, | ||||||
|  |  | ||||||
|  | @ -2,9 +2,8 @@ | ||||||
| // See Notices.txt for copyright information
 | // See Notices.txt for copyright information
 | ||||||
| use crate::{ | use crate::{ | ||||||
|     intern::{Intern, Interned}, |     intern::{Intern, Interned}, | ||||||
|     util::DebugAsDisplay, |     util::{DebugAsDisplay, HashMap}, | ||||||
| }; | }; | ||||||
| use hashbrown::HashMap; |  | ||||||
| use std::{cell::RefCell, fmt, num::NonZeroUsize, panic, path::Path}; | use std::{cell::RefCell, fmt, num::NonZeroUsize, panic, path::Path}; | ||||||
| 
 | 
 | ||||||
| #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] | #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] | ||||||
|  | @ -97,7 +96,7 @@ impl NormalizeFilesForTestsState { | ||||||
|     fn new() -> Self { |     fn new() -> Self { | ||||||
|         Self { |         Self { | ||||||
|             test_position: panic::Location::caller(), |             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) |                         map.entry_ref(file) | ||||||
|                             .or_insert_with(|| NormalizedFileForTestState { |                             .or_insert_with(|| NormalizedFileForTestState { | ||||||
|                                 file_name_id: NonZeroUsize::new(len + 1).unwrap(), |                                 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_str = m.generate_file_name(file_state.file_name_id); | ||||||
|                     file = &file_str; |                     file = &file_str; | ||||||
|  |  | ||||||
|  | @ -3,9 +3,9 @@ | ||||||
| use crate::{ | use crate::{ | ||||||
|     cli::{FormalArgs, FormalMode, FormalOutput, RunPhase}, |     cli::{FormalArgs, FormalMode, FormalOutput, RunPhase}, | ||||||
|     firrtl::ExportOptions, |     firrtl::ExportOptions, | ||||||
|  |     util::HashMap, | ||||||
| }; | }; | ||||||
| use clap::Parser; | use clap::Parser; | ||||||
| use hashbrown::HashMap; |  | ||||||
| use serde::Deserialize; | use serde::Deserialize; | ||||||
| use std::{ | use std::{ | ||||||
|     fmt::Write, |     fmt::Write, | ||||||
|  | @ -87,7 +87,7 @@ fn get_assert_formal_target_path(test_name: &dyn std::fmt::Display) -> PathBuf { | ||||||
|     let index = *DIRS |     let index = *DIRS | ||||||
|         .lock() |         .lock() | ||||||
|         .unwrap() |         .unwrap() | ||||||
|         .get_or_insert_with(HashMap::new) |         .get_or_insert_with(HashMap::default) | ||||||
|         .entry_ref(&dir) |         .entry_ref(&dir) | ||||||
|         .and_modify(|v| *v += 1) |         .and_modify(|v| *v += 1) | ||||||
|         .or_insert(0); |         .or_insert(0); | ||||||
|  |  | ||||||
|  | @ -7,12 +7,19 @@ use crate::{ | ||||||
|     clock::Clock, |     clock::Clock, | ||||||
|     enum_::Enum, |     enum_::Enum, | ||||||
|     expr::Expr, |     expr::Expr, | ||||||
|     int::{Bool, SInt, UInt}, |     int::{Bool, SInt, UInt, UIntValue}, | ||||||
|     intern::{Intern, Interned}, |     intern::{Intern, Interned}, | ||||||
|  |     phantom_const::PhantomConst, | ||||||
|     reset::{AsyncReset, Reset, SyncReset}, |     reset::{AsyncReset, Reset, SyncReset}, | ||||||
|  |     sim::value::{SimValue, ToSimValueWithType}, | ||||||
|     source_location::SourceLocation, |     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)] | #[derive(Copy, Clone, Hash, PartialEq, Eq, Debug)] | ||||||
| #[non_exhaustive] | #[non_exhaustive] | ||||||
|  | @ -35,6 +42,7 @@ pub enum CanonicalType { | ||||||
|     SyncReset(SyncReset), |     SyncReset(SyncReset), | ||||||
|     Reset(Reset), |     Reset(Reset), | ||||||
|     Clock(Clock), |     Clock(Clock), | ||||||
|  |     PhantomConst(PhantomConst), | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl fmt::Debug for CanonicalType { | impl fmt::Debug for CanonicalType { | ||||||
|  | @ -50,10 +58,29 @@ impl fmt::Debug for CanonicalType { | ||||||
|             Self::SyncReset(v) => v.fmt(f), |             Self::SyncReset(v) => v.fmt(f), | ||||||
|             Self::Reset(v) => v.fmt(f), |             Self::Reset(v) => v.fmt(f), | ||||||
|             Self::Clock(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 { | impl CanonicalType { | ||||||
|     pub fn type_properties(self) -> TypeProperties { |     pub fn type_properties(self) -> TypeProperties { | ||||||
|         match self { |         match self { | ||||||
|  | @ -67,6 +94,7 @@ impl CanonicalType { | ||||||
|             CanonicalType::SyncReset(v) => v.type_properties(), |             CanonicalType::SyncReset(v) => v.type_properties(), | ||||||
|             CanonicalType::Reset(v) => v.type_properties(), |             CanonicalType::Reset(v) => v.type_properties(), | ||||||
|             CanonicalType::Clock(v) => v.type_properties(), |             CanonicalType::Clock(v) => v.type_properties(), | ||||||
|  |             CanonicalType::PhantomConst(v) => v.type_properties(), | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|     pub fn is_passive(self) -> bool { |     pub fn is_passive(self) -> bool { | ||||||
|  | @ -143,8 +171,17 @@ impl CanonicalType { | ||||||
|                 }; |                 }; | ||||||
|                 lhs.can_connect(rhs) |                 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 { | pub trait MatchVariantAndInactiveScope: Sized { | ||||||
|  | @ -166,7 +203,7 @@ impl<T: 'static + Send + Sync> MatchVariantAndInactiveScope for MatchVariantWith | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| pub trait FillInDefaultedGenerics { | pub trait FillInDefaultedGenerics { | ||||||
|     type Type: Type; |     type Type; | ||||||
|     fn fill_in_defaulted_generics(self) -> Self::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 { | mod sealed { | ||||||
|     pub trait TypeOrDefaultSealed {} |     pub trait TypeOrDefaultSealed {} | ||||||
|     pub trait BaseTypeSealed {} |     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!(UInt); | ||||||
| impl_base_type!(SInt); | impl_base_type!(SInt); | ||||||
| impl_base_type!(Bool); | impl_base_type!(Bool); | ||||||
|  | @ -205,6 +286,15 @@ impl_base_type!(AsyncReset); | ||||||
| impl_base_type!(SyncReset); | impl_base_type!(SyncReset); | ||||||
| impl_base_type!(Reset); | impl_base_type!(Reset); | ||||||
| impl_base_type!(Clock); | impl_base_type!(Clock); | ||||||
|  | impl_base_type!(PhantomConst); | ||||||
|  | 
 | ||||||
|  | impl_base_type_serde!(Bool, "a Bool"); | ||||||
|  | impl_base_type_serde!(Enum, "an Enum"); | ||||||
|  | impl_base_type_serde!(Bundle, "a Bundle"); | ||||||
|  | impl_base_type_serde!(AsyncReset, "an AsyncReset"); | ||||||
|  | impl_base_type_serde!(SyncReset, "a SyncReset"); | ||||||
|  | impl_base_type_serde!(Reset, "a Reset"); | ||||||
|  | impl_base_type_serde!(Clock, "a Clock"); | ||||||
| 
 | 
 | ||||||
| impl sealed::BaseTypeSealed for CanonicalType {} | impl sealed::BaseTypeSealed for CanonicalType {} | ||||||
| 
 | 
 | ||||||
|  | @ -240,6 +330,7 @@ pub trait Type: | ||||||
| { | { | ||||||
|     type BaseType: BaseType; |     type BaseType: BaseType; | ||||||
|     type MaskType: Type<MaskType = Self::MaskType>; |     type MaskType: Type<MaskType = Self::MaskType>; | ||||||
|  |     type SimValue: fmt::Debug + Clone + 'static + ToSimValueWithType<Self>; | ||||||
|     type MatchVariant: 'static + Send + Sync; |     type MatchVariant: 'static + Send + Sync; | ||||||
|     type MatchActiveScope; |     type MatchActiveScope; | ||||||
|     type MatchVariantAndInactiveScope: MatchVariantAndInactiveScope< |     type MatchVariantAndInactiveScope: MatchVariantAndInactiveScope< | ||||||
|  | @ -257,9 +348,22 @@ pub trait Type: | ||||||
|     fn canonical(&self) -> CanonicalType; |     fn canonical(&self) -> CanonicalType; | ||||||
|     fn from_canonical(canonical_type: CanonicalType) -> Self; |     fn from_canonical(canonical_type: CanonicalType) -> Self; | ||||||
|     fn source_location() -> SourceLocation; |     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 { | macro_rules! impl_match_variant_as_self { | ||||||
|     () => { |     () => { | ||||||
|  | @ -286,6 +390,7 @@ pub trait TypeWithDeref: Type { | ||||||
| impl Type for CanonicalType { | impl Type for CanonicalType { | ||||||
|     type BaseType = CanonicalType; |     type BaseType = CanonicalType; | ||||||
|     type MaskType = CanonicalType; |     type MaskType = CanonicalType; | ||||||
|  |     type SimValue = OpaqueSimValue; | ||||||
|     impl_match_variant_as_self!(); |     impl_match_variant_as_self!(); | ||||||
|     fn mask_type(&self) -> Self::MaskType { |     fn mask_type(&self) -> Self::MaskType { | ||||||
|         match self { |         match self { | ||||||
|  | @ -299,6 +404,7 @@ impl Type for CanonicalType { | ||||||
|             CanonicalType::SyncReset(v) => v.mask_type().canonical(), |             CanonicalType::SyncReset(v) => v.mask_type().canonical(), | ||||||
|             CanonicalType::Reset(v) => v.mask_type().canonical(), |             CanonicalType::Reset(v) => v.mask_type().canonical(), | ||||||
|             CanonicalType::Clock(v) => v.mask_type().canonical(), |             CanonicalType::Clock(v) => v.mask_type().canonical(), | ||||||
|  |             CanonicalType::PhantomConst(v) => v.mask_type().canonical(), | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|     fn canonical(&self) -> CanonicalType { |     fn canonical(&self) -> CanonicalType { | ||||||
|  | @ -310,9 +416,60 @@ impl Type for CanonicalType { | ||||||
|     fn source_location() -> SourceLocation { |     fn source_location() -> SourceLocation { | ||||||
|         SourceLocation::builtin() |         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 TYPE: Self; | ||||||
|     const MASK_TYPE: Self::MaskType; |     const MASK_TYPE: Self::MaskType; | ||||||
|     const TYPE_PROPERTIES: TypeProperties; |     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
 | // SPDX-License-Identifier: LGPL-3.0-or-later
 | ||||||
| // See Notices.txt for copyright information
 | // See Notices.txt for copyright information
 | ||||||
| 
 | 
 | ||||||
|  | pub(crate) mod alternating_cell; | ||||||
| mod const_bool; | mod const_bool; | ||||||
| mod const_cmp; | mod const_cmp; | ||||||
| mod const_usize; | mod const_usize; | ||||||
| mod misc; | mod misc; | ||||||
| mod scoped_ref; | mod scoped_ref; | ||||||
| pub(crate) mod streaming_read_utf8; | 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)] | #[doc(inline)] | ||||||
| pub use const_bool::{ConstBool, ConstBoolDispatch, ConstBoolDispatchTag, GenericConstBool}; | pub use const_bool::{ConstBool, ConstBoolDispatch, ConstBoolDispatchTag, GenericConstBool}; | ||||||
|  | @ -29,4 +40,5 @@ pub use misc::{ | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| pub mod job_server; | pub mod job_server; | ||||||
|  | pub mod prefix_sum; | ||||||
| pub mod ready_valid; | 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
 | // SPDX-License-Identifier: LGPL-3.0-or-later
 | ||||||
| // See Notices.txt for copyright information
 | // 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}; | use std::{fmt::Debug, hash::Hash, mem::ManuallyDrop, ptr}; | ||||||
| 
 | 
 | ||||||
| mod sealed { | mod sealed { | ||||||
|  | @ -9,7 +13,17 @@ mod sealed { | ||||||
| /// # Safety
 | /// # Safety
 | ||||||
| /// the only implementation is `ConstBool<Self::VALUE>`
 | /// the only implementation is `ConstBool<Self::VALUE>`
 | ||||||
| pub unsafe trait GenericConstBool: | 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; |     const VALUE: bool; | ||||||
| } | } | ||||||
|  | @ -30,6 +44,32 @@ unsafe impl<const VALUE: bool> GenericConstBool for ConstBool<VALUE> { | ||||||
|     const VALUE: bool = 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 { | pub trait ConstBoolDispatchTag { | ||||||
|     type Type<Select: GenericConstBool>; |     type Type<Select: GenericConstBool>; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -1,5 +1,9 @@ | ||||||
| // SPDX-License-Identifier: LGPL-3.0-or-later
 | // SPDX-License-Identifier: LGPL-3.0-or-later
 | ||||||
| // See Notices.txt for copyright information
 | // See Notices.txt for copyright information
 | ||||||
|  | use serde::{ | ||||||
|  |     de::{DeserializeOwned, Error, Unexpected}, | ||||||
|  |     Deserialize, Deserializer, Serialize, Serializer, | ||||||
|  | }; | ||||||
| use std::{fmt::Debug, hash::Hash}; | use std::{fmt::Debug, hash::Hash}; | ||||||
| 
 | 
 | ||||||
| mod sealed { | mod sealed { | ||||||
|  | @ -8,7 +12,17 @@ mod sealed { | ||||||
| 
 | 
 | ||||||
| /// the only implementation is `ConstUsize<Self::VALUE>`
 | /// the only implementation is `ConstUsize<Self::VALUE>`
 | ||||||
| pub trait GenericConstUsize: | 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; |     const VALUE: usize; | ||||||
| } | } | ||||||
|  | @ -27,3 +41,29 @@ impl<const VALUE: usize> sealed::Sealed for ConstUsize<VALUE> {} | ||||||
| impl<const VALUE: usize> GenericConstUsize for ConstUsize<VALUE> { | impl<const VALUE: usize> GenericConstUsize for ConstUsize<VALUE> { | ||||||
|     const VALUE: usize = 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, |     bundle::BundleType, | ||||||
|     enum_::EnumType, |     enum_::EnumType, | ||||||
|     int::{BoolOrIntType, IntType}, |     int::{BoolOrIntType, IntType}, | ||||||
|  |     phantom_const::PhantomConst, | ||||||
|     prelude::*, |     prelude::*, | ||||||
|     ty::StaticType, |     ty::StaticType, | ||||||
| }; | }; | ||||||
| use std::marker::PhantomData; | use std::marker::PhantomData; | ||||||
| 
 | 
 | ||||||
|  | #[hdl(outline_generated)] | ||||||
|  | pub struct MyConstSize<V: Size> { | ||||||
|  |     pub size: PhantomConst<UIntType<V>>, | ||||||
|  | } | ||||||
|  | 
 | ||||||
| #[hdl(outline_generated)] | #[hdl(outline_generated)] | ||||||
| pub struct S<T, Len: Size, T2> { | pub struct S<T, Len: Size, T2> { | ||||||
|     pub a: T, |     pub a: T, | ||||||
|  |  | ||||||
|  | @ -1,8 +1,13 @@ | ||||||
| // SPDX-License-Identifier: LGPL-3.0-or-later
 | // SPDX-License-Identifier: LGPL-3.0-or-later
 | ||||||
| // See Notices.txt for copyright information
 | // See Notices.txt for copyright information
 | ||||||
| use fayalite::{ | use fayalite::{ | ||||||
|     assert_export_firrtl, firrtl::ExportOptions, intern::Intern, |     assert_export_firrtl, | ||||||
|     module::transform::simplify_enums::SimplifyEnumsKind, prelude::*, reset::ResetType, |     firrtl::ExportOptions, | ||||||
|  |     int::{UIntInRange, UIntInRangeInclusive}, | ||||||
|  |     intern::Intern, | ||||||
|  |     module::transform::simplify_enums::SimplifyEnumsKind, | ||||||
|  |     prelude::*, | ||||||
|  |     reset::ResetType, | ||||||
|     ty::StaticType, |     ty::StaticType, | ||||||
| }; | }; | ||||||
| use serde_json::json; | 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)] | #[hdl_module(outline_generated)] | ||||||
| pub fn check_skipped_generics<T, #[hdl(skip)] U, const N: usize, #[hdl(skip)] const M: usize>(v: U) | pub fn check_skipped_generics<T, #[hdl(skip)] U, const N: usize, #[hdl(skip)] const M: usize>(v: U) | ||||||
| where | where | ||||||
|     T: StaticType, |     T: StaticType + UnknownTrait, | ||||||
|     ConstUsize<N>: KnownSize, |     ConstUsize<N>: KnownSize, | ||||||
|     U: std::fmt::Display, |     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 struct TestStruct<T> { | ||||||
|     pub a: T, |     pub a: T, | ||||||
|     pub b: UInt<8>, |     pub b: UInt<8>, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #[hdl(outline_generated)] | #[hdl(outline_generated, cmp_eq)] | ||||||
| pub struct TestStruct2 { | pub struct TestStruct2 { | ||||||
|     pub v: UInt<8>, |     pub v: UInt<8>, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #[hdl(outline_generated)] | #[hdl(outline_generated, cmp_eq)] | ||||||
| pub struct TestStruct3 {} | pub struct TestStruct3 {} | ||||||
| 
 | 
 | ||||||
| #[hdl_module(outline_generated)] | #[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
 | // See Notices.txt for copyright information
 | ||||||
| 
 | 
 | ||||||
| use fayalite::{ | use fayalite::{ | ||||||
|     int::UIntValue, |     memory::{ReadStruct, ReadWriteStruct, WriteStruct}, | ||||||
|  |     module::{instance_with_loc, reg_builder_with_loc}, | ||||||
|     prelude::*, |     prelude::*, | ||||||
|     reset::ResetType, |     reset::ResetType, | ||||||
|     sim::{time::SimDuration, vcd::VcdWriterDecls, Simulation, ToSimValue}, |     sim::vcd::VcdWriterDecls, | ||||||
|     ty::StaticType, |  | ||||||
|     util::RcWriter, |     util::RcWriter, | ||||||
| }; | }; | ||||||
| use std::num::NonZeroUsize; | use std::num::NonZeroUsize; | ||||||
|  | @ -316,8 +316,13 @@ pub fn enums() { | ||||||
|     let which_out: UInt<2> = m.output(); |     let which_out: UInt<2> = m.output(); | ||||||
|     #[hdl] |     #[hdl] | ||||||
|     let data_out: UInt<4> = m.output(); |     let data_out: UInt<4> = m.output(); | ||||||
|  |     let b_out_ty = HdlOption[(UInt[1], Bool)]; | ||||||
|     #[hdl] |     #[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] |     #[hdl] | ||||||
|     struct MyStruct<T> { |     struct MyStruct<T> { | ||||||
|  | @ -357,7 +362,7 @@ pub fn enums() { | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     connect(b_out, HdlNone()); |     connect(b_out, b_out_ty.HdlNone()); | ||||||
| 
 | 
 | ||||||
|     #[hdl] |     #[hdl] | ||||||
|     match the_reg { |     match the_reg { | ||||||
|  | @ -368,7 +373,7 @@ pub fn enums() { | ||||||
|         MyEnum::B(v) => { |         MyEnum::B(v) => { | ||||||
|             connect(which_out, 1_hdl_u2); |             connect(which_out, 1_hdl_u2); | ||||||
|             connect_any(data_out, v.0 | (v.1.cast_to_static::<UInt<1>>() << 1)); |             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) => { |         MyEnum::C(v) => { | ||||||
|             connect(which_out, 2_hdl_u2); |             connect(which_out, 2_hdl_u2); | ||||||
|  | @ -384,132 +389,136 @@ fn test_enums() { | ||||||
|     let mut sim = Simulation::new(enums()); |     let mut sim = Simulation::new(enums()); | ||||||
|     let mut writer = RcWriter::default(); |     let mut writer = RcWriter::default(); | ||||||
|     sim.add_trace_writer(VcdWriterDecls::new(writer.clone())); |     sim.add_trace_writer(VcdWriterDecls::new(writer.clone())); | ||||||
|     sim.write_clock(sim.io().cd.clk, false); |     sim.write(sim.io().cd.clk, false); | ||||||
|     sim.write_reset(sim.io().cd.rst, true); |     sim.write(sim.io().cd.rst, true); | ||||||
|     sim.write_bool(sim.io().en, false); |     sim.write(sim.io().en, false); | ||||||
|     sim.write_bool_or_int(sim.io().which_in, 0_hdl_u2); |     sim.write(sim.io().which_in, 0_hdl_u2); | ||||||
|     sim.write_bool_or_int(sim.io().data_in, 0_hdl_u4); |     sim.write(sim.io().data_in, 0_hdl_u4); | ||||||
|     sim.advance_time(SimDuration::from_micros(1)); |     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.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)); |     sim.advance_time(SimDuration::from_nanos(900)); | ||||||
|     type BOutTy = HdlOption<(UInt<1>, Bool)>; |     #[hdl(cmp_eq)] | ||||||
|     #[derive(Debug)] |     struct IO<W: Size> { | ||||||
|     struct IO { |         en: Bool, | ||||||
|         en: bool, |         which_in: UInt<2>, | ||||||
|         which_in: u8, |         data_in: UInt<4>, | ||||||
|         data_in: u8, |         which_out: UInt<2>, | ||||||
|         which_out: u8, |         data_out: UInt<4>, | ||||||
|         data_out: u8, |         b_out: HdlOption<(UIntType<W>, Bool)>, | ||||||
|         b_out: Expr<BOutTy>, |         b2_out: HdlOption<(UInt<1>, Bool)>, | ||||||
|     } |  | ||||||
|     impl PartialEq for IO { |  | ||||||
|         fn eq(&self, other: &Self) -> bool { |  | ||||||
|             let Self { |  | ||||||
|                 en, |  | ||||||
|                 which_in, |  | ||||||
|                 data_in, |  | ||||||
|                 which_out, |  | ||||||
|                 data_out, |  | ||||||
|                 b_out, |  | ||||||
|             } = *self; |  | ||||||
|             en == other.en |  | ||||||
|                 && which_in == other.which_in |  | ||||||
|                 && data_in == other.data_in |  | ||||||
|                 && which_out == other.which_out |  | ||||||
|                 && data_out == other.data_out |  | ||||||
|                 && b_out.to_sim_value(BOutTy::TYPE) == other.b_out.to_sim_value(BOutTy::TYPE) |  | ||||||
|         } |  | ||||||
|     } |     } | ||||||
|  |     let io_ty = IO[1]; | ||||||
|     let io_cycles = [ |     let io_cycles = [ | ||||||
|         IO { |         #[hdl(sim)] | ||||||
|  |         IO::<_> { | ||||||
|             en: false, |             en: false, | ||||||
|             which_in: 0, |             which_in: 0_hdl_u2, | ||||||
|             data_in: 0, |             data_in: 0_hdl_u4, | ||||||
|             which_out: 0, |             which_out: 0_hdl_u2, | ||||||
|             data_out: 0, |             data_out: 0_hdl_u4, | ||||||
|             b_out: HdlNone(), |             b_out: #[hdl(sim)] | ||||||
|  |             (io_ty.b_out).HdlNone(), | ||||||
|  |             b2_out: #[hdl(sim)] | ||||||
|  |             HdlNone(), | ||||||
|         }, |         }, | ||||||
|         IO { |         #[hdl(sim)] | ||||||
|  |         IO::<_> { | ||||||
|             en: true, |             en: true, | ||||||
|             which_in: 1, |             which_in: 1_hdl_u2, | ||||||
|             data_in: 0, |             data_in: 0_hdl_u4, | ||||||
|             which_out: 0, |             which_out: 0_hdl_u2, | ||||||
|             data_out: 0, |             data_out: 0_hdl_u4, | ||||||
|             b_out: HdlNone(), |             b_out: #[hdl(sim)] | ||||||
|  |             (io_ty.b_out).HdlNone(), | ||||||
|  |             b2_out: #[hdl(sim)] | ||||||
|  |             HdlNone(), | ||||||
|         }, |         }, | ||||||
|         IO { |         #[hdl(sim)] | ||||||
|  |         IO::<_> { | ||||||
|             en: false, |             en: false, | ||||||
|             which_in: 0, |             which_in: 0_hdl_u2, | ||||||
|             data_in: 0, |             data_in: 0_hdl_u4, | ||||||
|             which_out: 1, |             which_out: 1_hdl_u2, | ||||||
|             data_out: 0, |             data_out: 0_hdl_u4, | ||||||
|             b_out: HdlSome((0_hdl_u1, false)), |             b_out: #[hdl(sim)] | ||||||
|  |             (io_ty.b_out).HdlSome((0u8.cast_to(UInt[1]), false)), | ||||||
|  |             b2_out: #[hdl(sim)] | ||||||
|  |             HdlSome((0_hdl_u1, false)), | ||||||
|         }, |         }, | ||||||
|         IO { |         #[hdl(sim)] | ||||||
|  |         IO::<_> { | ||||||
|             en: true, |             en: true, | ||||||
|             which_in: 1, |             which_in: 1_hdl_u2, | ||||||
|             data_in: 0xF, |             data_in: 0xF_hdl_u4, | ||||||
|             which_out: 1, |             which_out: 1_hdl_u2, | ||||||
|             data_out: 0, |             data_out: 0_hdl_u4, | ||||||
|             b_out: HdlSome((0_hdl_u1, false)), |             b_out: #[hdl(sim)] | ||||||
|  |             (io_ty.b_out).HdlSome((0u8.cast_to(UInt[1]), false)), | ||||||
|  |             b2_out: #[hdl(sim)] | ||||||
|  |             HdlSome((0_hdl_u1, false)), | ||||||
|         }, |         }, | ||||||
|         IO { |         #[hdl(sim)] | ||||||
|  |         IO::<_> { | ||||||
|             en: true, |             en: true, | ||||||
|             which_in: 1, |             which_in: 1_hdl_u2, | ||||||
|             data_in: 0xF, |             data_in: 0xF_hdl_u4, | ||||||
|             which_out: 1, |             which_out: 1_hdl_u2, | ||||||
|             data_out: 0x3, |             data_out: 0x3_hdl_u4, | ||||||
|             b_out: HdlSome((1_hdl_u1, true)), |             b_out: #[hdl(sim)] | ||||||
|  |             (io_ty.b_out).HdlSome((1u8.cast_to(UInt[1]), true)), | ||||||
|  |             b2_out: #[hdl(sim)] | ||||||
|  |             HdlSome((1_hdl_u1, true)), | ||||||
|         }, |         }, | ||||||
|         IO { |         #[hdl(sim)] | ||||||
|  |         IO::<_> { | ||||||
|             en: true, |             en: true, | ||||||
|             which_in: 2, |             which_in: 2_hdl_u2, | ||||||
|             data_in: 0xF, |             data_in: 0xF_hdl_u4, | ||||||
|             which_out: 1, |             which_out: 1_hdl_u2, | ||||||
|             data_out: 0x3, |             data_out: 0x3_hdl_u4, | ||||||
|             b_out: HdlSome((1_hdl_u1, true)), |             b_out: #[hdl(sim)] | ||||||
|  |             (io_ty.b_out).HdlSome((1u8.cast_to(UInt[1]), true)), | ||||||
|  |             b2_out: #[hdl(sim)] | ||||||
|  |             HdlSome((1_hdl_u1, true)), | ||||||
|         }, |         }, | ||||||
|         IO { |         #[hdl(sim)] | ||||||
|  |         IO::<_> { | ||||||
|             en: true, |             en: true, | ||||||
|             which_in: 2, |             which_in: 2_hdl_u2, | ||||||
|             data_in: 0xF, |             data_in: 0xF_hdl_u4, | ||||||
|             which_out: 2, |             which_out: 2_hdl_u2, | ||||||
|             data_out: 0xF, |             data_out: 0xF_hdl_u4, | ||||||
|             b_out: HdlNone(), |             b_out: #[hdl(sim)] | ||||||
|  |             (io_ty.b_out).HdlNone(), | ||||||
|  |             b2_out: #[hdl(sim)] | ||||||
|  |             HdlNone(), | ||||||
|         }, |         }, | ||||||
|     ]; |     ]; | ||||||
|     for ( |     for (cycle, expected) in io_cycles.into_iter().enumerate() { | ||||||
|         cycle, |         #[hdl(sim)] | ||||||
|         expected @ IO { |         let IO::<_> { | ||||||
|             en, |             en, | ||||||
|             which_in, |             which_in, | ||||||
|             data_in, |             data_in, | ||||||
|             which_out: _, |             which_out: _, | ||||||
|             data_out: _, |             data_out: _, | ||||||
|             b_out: _, |             b_out: _, | ||||||
|         }, |             b2_out: _, | ||||||
|     ) in io_cycles.into_iter().enumerate() |         } = expected; | ||||||
|     { |         sim.write(sim.io().en, &en); | ||||||
|         sim.write_bool(sim.io().en, en); |         sim.write(sim.io().which_in, &which_in); | ||||||
|         sim.write_bool_or_int(sim.io().which_in, which_in.cast_to_static()); |         sim.write(sim.io().data_in, &data_in); | ||||||
|         sim.write_bool_or_int(sim.io().data_in, data_in.cast_to_static()); |         let io = #[hdl(sim)] | ||||||
|         let io = IO { |         IO::<_> { | ||||||
|             en, |             en, | ||||||
|             which_in, |             which_in, | ||||||
|             data_in, |             data_in, | ||||||
|             which_out: sim |             which_out: sim.read(sim.io().which_out), | ||||||
|                 .read_bool_or_int(sim.io().which_out) |             data_out: sim.read(sim.io().data_out), | ||||||
|                 .to_bigint() |             b_out: sim.read(sim.io().b_out), | ||||||
|                 .try_into() |             b2_out: sim.read(sim.io().b2_out), | ||||||
|                 .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(), |  | ||||||
|         }; |         }; | ||||||
|         assert_eq!( |         assert_eq!( | ||||||
|             expected, |             expected, | ||||||
|  | @ -517,6 +526,12 @@ fn test_enums() { | ||||||
|             "vcd:\n{}\ncycle: {cycle}", |             "vcd:\n{}\ncycle: {cycle}", | ||||||
|             String::from_utf8(writer.take()).unwrap(), |             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.write_clock(sim.io().cd.clk, false); | ||||||
|         sim.advance_time(SimDuration::from_micros(1)); |         sim.advance_time(SimDuration::from_micros(1)); | ||||||
|         sim.write_clock(sim.io().cd.clk, true); |         sim.write_clock(sim.io().cd.clk, true); | ||||||
|  | @ -538,9 +553,9 @@ fn test_enums() { | ||||||
| #[hdl_module(outline_generated)] | #[hdl_module(outline_generated)] | ||||||
| pub fn memories() { | pub fn memories() { | ||||||
|     #[hdl] |     #[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] |     #[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] |     #[hdl] | ||||||
|     let mut mem = memory_with_init([(0x01u8, 0x23i8); 16]); |     let mut mem = memory_with_init([(0x01u8, 0x23i8); 16]); | ||||||
|     mem.read_latency(0); |     mem.read_latency(0); | ||||||
|  | @ -559,120 +574,131 @@ fn test_memories() { | ||||||
|     sim.add_trace_writer(VcdWriterDecls::new(writer.clone())); |     sim.add_trace_writer(VcdWriterDecls::new(writer.clone())); | ||||||
|     sim.write_clock(sim.io().r.clk, false); |     sim.write_clock(sim.io().r.clk, false); | ||||||
|     sim.write_clock(sim.io().w.clk, false); |     sim.write_clock(sim.io().w.clk, false); | ||||||
|     #[derive(Debug, PartialEq, Eq)] |     #[hdl(cmp_eq)] | ||||||
|     struct IO { |     struct IO { | ||||||
|         r_addr: u8, |         r_addr: UInt<4>, | ||||||
|         r_en: bool, |         r_en: Bool, | ||||||
|         r_data: (u8, i8), |         r_data: (UInt<8>, SInt<8>), | ||||||
|         w_addr: u8, |         w_addr: UInt<4>, | ||||||
|         w_en: bool, |         w_en: Bool, | ||||||
|         w_data: (u8, i8), |         w_data: (UInt<8>, SInt<8>), | ||||||
|         w_mask: (bool, bool), |         w_mask: (Bool, Bool), | ||||||
|     } |     } | ||||||
|     let io_cycles = [ |     let io_cycles = [ | ||||||
|  |         #[hdl(sim)] | ||||||
|         IO { |         IO { | ||||||
|             r_addr: 0, |             r_addr: 0_hdl_u4, | ||||||
|             r_en: false, |             r_en: false, | ||||||
|             r_data: (0, 0), |             r_data: (0u8, 0i8), | ||||||
|             w_addr: 0, |             w_addr: 0_hdl_u4, | ||||||
|             w_en: false, |             w_en: false, | ||||||
|             w_data: (0, 0), |             w_data: (0u8, 0i8), | ||||||
|             w_mask: (false, false), |             w_mask: (false, false), | ||||||
|         }, |         }, | ||||||
|  |         #[hdl(sim)] | ||||||
|         IO { |         IO { | ||||||
|             r_addr: 0, |             r_addr: 0_hdl_u4, | ||||||
|             r_en: true, |             r_en: true, | ||||||
|             r_data: (0x1, 0x23), |             r_data: (0x1u8, 0x23i8), | ||||||
|             w_addr: 0, |             w_addr: 0_hdl_u4, | ||||||
|             w_en: true, |             w_en: true, | ||||||
|             w_data: (0x10, 0x20), |             w_data: (0x10u8, 0x20i8), | ||||||
|             w_mask: (true, true), |             w_mask: (true, true), | ||||||
|         }, |         }, | ||||||
|  |         #[hdl(sim)] | ||||||
|         IO { |         IO { | ||||||
|             r_addr: 0, |             r_addr: 0_hdl_u4, | ||||||
|             r_en: true, |             r_en: true, | ||||||
|             r_data: (0x10, 0x20), |             r_data: (0x10u8, 0x20i8), | ||||||
|             w_addr: 0, |             w_addr: 0_hdl_u4, | ||||||
|             w_en: true, |             w_en: true, | ||||||
|             w_data: (0x30, 0x40), |             w_data: (0x30u8, 0x40i8), | ||||||
|             w_mask: (false, true), |             w_mask: (false, true), | ||||||
|         }, |         }, | ||||||
|  |         #[hdl(sim)] | ||||||
|         IO { |         IO { | ||||||
|             r_addr: 0, |             r_addr: 0_hdl_u4, | ||||||
|             r_en: true, |             r_en: true, | ||||||
|             r_data: (0x10, 0x40), |             r_data: (0x10u8, 0x40i8), | ||||||
|             w_addr: 0, |             w_addr: 0_hdl_u4, | ||||||
|             w_en: true, |             w_en: true, | ||||||
|             w_data: (0x50, 0x60), |             w_data: (0x50u8, 0x60i8), | ||||||
|             w_mask: (true, false), |             w_mask: (true, false), | ||||||
|         }, |         }, | ||||||
|  |         #[hdl(sim)] | ||||||
|         IO { |         IO { | ||||||
|             r_addr: 0, |             r_addr: 0_hdl_u4, | ||||||
|             r_en: true, |             r_en: true, | ||||||
|             r_data: (0x50, 0x40), |             r_data: (0x50u8, 0x40i8), | ||||||
|             w_addr: 0, |             w_addr: 0_hdl_u4, | ||||||
|             w_en: true, |             w_en: true, | ||||||
|             w_data: (0x70, -0x80), |             w_data: (0x70u8, -0x80i8), | ||||||
|             w_mask: (false, false), |             w_mask: (false, false), | ||||||
|         }, |         }, | ||||||
|  |         #[hdl(sim)] | ||||||
|         IO { |         IO { | ||||||
|             r_addr: 0, |             r_addr: 0_hdl_u4, | ||||||
|             r_en: true, |             r_en: true, | ||||||
|             r_data: (0x50, 0x40), |             r_data: (0x50u8, 0x40i8), | ||||||
|             w_addr: 0, |             w_addr: 0_hdl_u4, | ||||||
|             w_en: false, |             w_en: false, | ||||||
|             w_data: (0x90, 0xA0u8 as i8), |             w_data: (0x90u8, 0xA0u8 as i8), | ||||||
|             w_mask: (false, false), |             w_mask: (false, false), | ||||||
|         }, |         }, | ||||||
|  |         #[hdl(sim)] | ||||||
|         IO { |         IO { | ||||||
|             r_addr: 0, |             r_addr: 0_hdl_u4, | ||||||
|             r_en: true, |             r_en: true, | ||||||
|             r_data: (0x50, 0x40), |             r_data: (0x50u8, 0x40i8), | ||||||
|             w_addr: 1, |             w_addr: 1_hdl_u4, | ||||||
|             w_en: true, |             w_en: true, | ||||||
|             w_data: (0x90, 0xA0u8 as i8), |             w_data: (0x90u8, 0xA0u8 as i8), | ||||||
|             w_mask: (true, true), |             w_mask: (true, true), | ||||||
|         }, |         }, | ||||||
|  |         #[hdl(sim)] | ||||||
|         IO { |         IO { | ||||||
|             r_addr: 0, |             r_addr: 0_hdl_u4, | ||||||
|             r_en: true, |             r_en: true, | ||||||
|             r_data: (0x50, 0x40), |             r_data: (0x50u8, 0x40i8), | ||||||
|             w_addr: 2, |             w_addr: 2_hdl_u4, | ||||||
|             w_en: true, |             w_en: true, | ||||||
|             w_data: (0xB0, 0xC0u8 as i8), |             w_data: (0xB0u8, 0xC0u8 as i8), | ||||||
|             w_mask: (true, true), |             w_mask: (true, true), | ||||||
|         }, |         }, | ||||||
|  |         #[hdl(sim)] | ||||||
|         IO { |         IO { | ||||||
|             r_addr: 0, |             r_addr: 0_hdl_u4, | ||||||
|             r_en: true, |             r_en: true, | ||||||
|             r_data: (0x50, 0x40), |             r_data: (0x50u8, 0x40i8), | ||||||
|             w_addr: 2, |             w_addr: 2_hdl_u4, | ||||||
|             w_en: false, |             w_en: false, | ||||||
|             w_data: (0xD0, 0xE0u8 as i8), |             w_data: (0xD0u8, 0xE0u8 as i8), | ||||||
|             w_mask: (true, true), |             w_mask: (true, true), | ||||||
|         }, |         }, | ||||||
|  |         #[hdl(sim)] | ||||||
|         IO { |         IO { | ||||||
|             r_addr: 1, |             r_addr: 1_hdl_u4, | ||||||
|             r_en: true, |             r_en: true, | ||||||
|             r_data: (0x90, 0xA0u8 as i8), |             r_data: (0x90u8, 0xA0u8 as i8), | ||||||
|             w_addr: 2, |             w_addr: 2_hdl_u4, | ||||||
|             w_en: false, |             w_en: false, | ||||||
|             w_data: (0xD0, 0xE0u8 as i8), |             w_data: (0xD0u8, 0xE0u8 as i8), | ||||||
|             w_mask: (true, true), |             w_mask: (true, true), | ||||||
|         }, |         }, | ||||||
|  |         #[hdl(sim)] | ||||||
|         IO { |         IO { | ||||||
|             r_addr: 2, |             r_addr: 2_hdl_u4, | ||||||
|             r_en: true, |             r_en: true, | ||||||
|             r_data: (0xB0, 0xC0u8 as i8), |             r_data: (0xB0u8, 0xC0u8 as i8), | ||||||
|             w_addr: 2, |             w_addr: 2_hdl_u4, | ||||||
|             w_en: false, |             w_en: false, | ||||||
|             w_data: (0xD0, 0xE0u8 as i8), |             w_data: (0xD0u8, 0xE0u8 as i8), | ||||||
|             w_mask: (true, true), |             w_mask: (true, true), | ||||||
|         }, |         }, | ||||||
|     ]; |     ]; | ||||||
|     for ( |     for (cycle, expected) in io_cycles.into_iter().enumerate() { | ||||||
|         cycle, |         #[hdl(sim)] | ||||||
|         expected @ IO { |         let IO { | ||||||
|             r_addr, |             r_addr, | ||||||
|             r_en, |             r_en, | ||||||
|             r_data: _, |             r_data: _, | ||||||
|  | @ -680,30 +706,18 @@ fn test_memories() { | ||||||
|             w_en, |             w_en, | ||||||
|             w_data, |             w_data, | ||||||
|             w_mask, |             w_mask, | ||||||
|         }, |         } = expected; | ||||||
|     ) in io_cycles.into_iter().enumerate() |         sim.write(sim.io().r.addr, &r_addr); | ||||||
|     { |         sim.write(sim.io().r.en, &r_en); | ||||||
|         sim.write_bool_or_int(sim.io().r.addr, r_addr.cast_to_static()); |         sim.write(sim.io().w.addr, &w_addr); | ||||||
|         sim.write_bool(sim.io().r.en, r_en); |         sim.write(sim.io().w.en, &w_en); | ||||||
|         sim.write_bool_or_int(sim.io().w.addr, w_addr.cast_to_static()); |         sim.write(sim.io().w.data, &w_data); | ||||||
|         sim.write_bool(sim.io().w.en, w_en); |         sim.write(sim.io().w.mask, &w_mask); | ||||||
|         sim.write_bool_or_int(sim.io().w.data.0, w_data.0); |         let io = #[hdl(sim)] | ||||||
|         sim.write_bool_or_int(sim.io().w.data.1, w_data.1); |         IO { | ||||||
|         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 { |  | ||||||
|             r_addr, |             r_addr, | ||||||
|             r_en, |             r_en, | ||||||
|             r_data: ( |             r_data: sim.read(sim.io().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"), |  | ||||||
|             ), |  | ||||||
|             w_addr, |             w_addr, | ||||||
|             w_en, |             w_en, | ||||||
|             w_data, |             w_data, | ||||||
|  | @ -716,11 +730,11 @@ fn test_memories() { | ||||||
|             String::from_utf8(writer.take()).unwrap(), |             String::from_utf8(writer.take()).unwrap(), | ||||||
|         ); |         ); | ||||||
|         sim.advance_time(SimDuration::from_micros(1)); |         sim.advance_time(SimDuration::from_micros(1)); | ||||||
|         sim.write_clock(sim.io().r.clk, true); |         sim.write(sim.io().r.clk, true); | ||||||
|         sim.write_clock(sim.io().w.clk, true); |         sim.write(sim.io().w.clk, true); | ||||||
|         sim.advance_time(SimDuration::from_micros(1)); |         sim.advance_time(SimDuration::from_micros(1)); | ||||||
|         sim.write_clock(sim.io().r.clk, false); |         sim.write(sim.io().r.clk, false); | ||||||
|         sim.write_clock(sim.io().w.clk, false); |         sim.write(sim.io().w.clk, false); | ||||||
|     } |     } | ||||||
|     sim.flush_traces().unwrap(); |     sim.flush_traces().unwrap(); | ||||||
|     let vcd = String::from_utf8(writer.take()).unwrap(); |     let vcd = String::from_utf8(writer.take()).unwrap(); | ||||||
|  | @ -738,7 +752,7 @@ fn test_memories() { | ||||||
| #[hdl_module(outline_generated)] | #[hdl_module(outline_generated)] | ||||||
| pub fn memories2() { | pub fn memories2() { | ||||||
|     #[hdl] |     #[hdl] | ||||||
|     let rw: fayalite::memory::ReadWriteStruct<UInt<2>, ConstUsize<3>> = m.input(); |     let rw: ReadWriteStruct<UInt<2>, ConstUsize<3>> = m.input(); | ||||||
|     #[hdl] |     #[hdl] | ||||||
|     let mut mem = memory_with_init([HdlSome(true); 5]); |     let mut mem = memory_with_init([HdlSome(true); 5]); | ||||||
|     mem.read_latency(1); |     mem.read_latency(1); | ||||||
|  | @ -1011,9 +1025,9 @@ fn test_memories2() { | ||||||
| #[hdl_module(outline_generated)] | #[hdl_module(outline_generated)] | ||||||
| pub fn memories3() { | pub fn memories3() { | ||||||
|     #[hdl] |     #[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] |     #[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] |     #[hdl] | ||||||
|     let mut mem: MemBuilder<Array<UInt<8>, 8>> = memory(); |     let mut mem: MemBuilder<Array<UInt<8>, 8>> = memory(); | ||||||
|     mem.depth(8); |     mem.depth(8); | ||||||
|  | @ -1443,3 +1457,172 @@ fn test_conditional_assignment_last() { | ||||||
|         panic!(); |         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,45 +92,30 @@ Simulation { | ||||||
|             .. |             .. | ||||||
|         }, |         }, | ||||||
|     }, |     }, | ||||||
|     uninitialized_inputs: {}, |     main_module: SimulationModuleState { | ||||||
|     io_targets: { |         base_targets: [ | ||||||
|         Instance { |             Instance { | ||||||
|             name: <simulator>::conditional_assignment_last, |                 name: <simulator>::conditional_assignment_last, | ||||||
|             instantiated: Module { |                 instantiated: Module { | ||||||
|                 name: conditional_assignment_last, |                     name: conditional_assignment_last, | ||||||
|                 .. |                     .. | ||||||
|             }, |  | ||||||
|         }.i: CompiledValue { |  | ||||||
|             layout: CompiledTypeLayout { |  | ||||||
|                 ty: Bool, |  | ||||||
|                 layout: TypeLayout { |  | ||||||
|                     small_slots: StatePartLayout<SmallSlots> { |  | ||||||
|                         len: 0, |  | ||||||
|                         debug_data: [], |  | ||||||
|                         .. |  | ||||||
|                     }, |  | ||||||
|                     big_slots: StatePartLayout<BigSlots> { |  | ||||||
|                         len: 1, |  | ||||||
|                         debug_data: [ |  | ||||||
|                             SlotDebugData { |  | ||||||
|                                 name: "InstantiatedModule(conditional_assignment_last: conditional_assignment_last).conditional_assignment_last::i", |  | ||||||
|                                 ty: Bool, |  | ||||||
|                             }, |  | ||||||
|                         ], |  | ||||||
|                         .. |  | ||||||
|                     }, |  | ||||||
|                 }, |                 }, | ||||||
|                 body: Scalar, |             }.i, | ||||||
|             }, |         ], | ||||||
|             range: TypeIndexRange { |         uninitialized_ios: {}, | ||||||
|                 small_slots: StatePartIndexRange<SmallSlots> { start: 0, len: 0 }, |         io_targets: { | ||||||
|                 big_slots: StatePartIndexRange<BigSlots> { start: 0, len: 1 }, |             Instance { | ||||||
|             }, |                 name: <simulator>::conditional_assignment_last, | ||||||
|             write: None, |                 instantiated: Module { | ||||||
|  |                     name: conditional_assignment_last, | ||||||
|  |                     .. | ||||||
|  |                 }, | ||||||
|  |             }.i, | ||||||
|         }, |         }, | ||||||
|  |         did_initial_settle: true, | ||||||
|     }, |     }, | ||||||
|     made_initial_step: true, |     extern_modules: [], | ||||||
|     needs_settle: false, |     state_ready_to_run: false, | ||||||
|     trace_decls: TraceModule { |     trace_decls: TraceModule { | ||||||
|         name: "conditional_assignment_last", |         name: "conditional_assignment_last", | ||||||
|         children: [ |         children: [ | ||||||
|  |  | ||||||
|  | @ -68,45 +68,30 @@ Simulation { | ||||||
|             .. |             .. | ||||||
|         }, |         }, | ||||||
|     }, |     }, | ||||||
|     uninitialized_inputs: {}, |     main_module: SimulationModuleState { | ||||||
|     io_targets: { |         base_targets: [ | ||||||
|         Instance { |             Instance { | ||||||
|             name: <simulator>::connect_const, |                 name: <simulator>::connect_const, | ||||||
|             instantiated: Module { |                 instantiated: Module { | ||||||
|                 name: connect_const, |                     name: connect_const, | ||||||
|                 .. |                     .. | ||||||
|             }, |  | ||||||
|         }.o: CompiledValue { |  | ||||||
|             layout: CompiledTypeLayout { |  | ||||||
|                 ty: UInt<8>, |  | ||||||
|                 layout: TypeLayout { |  | ||||||
|                     small_slots: StatePartLayout<SmallSlots> { |  | ||||||
|                         len: 0, |  | ||||||
|                         debug_data: [], |  | ||||||
|                         .. |  | ||||||
|                     }, |  | ||||||
|                     big_slots: StatePartLayout<BigSlots> { |  | ||||||
|                         len: 1, |  | ||||||
|                         debug_data: [ |  | ||||||
|                             SlotDebugData { |  | ||||||
|                                 name: "InstantiatedModule(connect_const: connect_const).connect_const::o", |  | ||||||
|                                 ty: UInt<8>, |  | ||||||
|                             }, |  | ||||||
|                         ], |  | ||||||
|                         .. |  | ||||||
|                     }, |  | ||||||
|                 }, |                 }, | ||||||
|                 body: Scalar, |             }.o, | ||||||
|             }, |         ], | ||||||
|             range: TypeIndexRange { |         uninitialized_ios: {}, | ||||||
|                 small_slots: StatePartIndexRange<SmallSlots> { start: 0, len: 0 }, |         io_targets: { | ||||||
|                 big_slots: StatePartIndexRange<BigSlots> { start: 0, len: 1 }, |             Instance { | ||||||
|             }, |                 name: <simulator>::connect_const, | ||||||
|             write: None, |                 instantiated: Module { | ||||||
|  |                     name: connect_const, | ||||||
|  |                     .. | ||||||
|  |                 }, | ||||||
|  |             }.o, | ||||||
|         }, |         }, | ||||||
|  |         did_initial_settle: true, | ||||||
|     }, |     }, | ||||||
|     made_initial_step: true, |     extern_modules: [], | ||||||
|     needs_settle: false, |     state_ready_to_run: false, | ||||||
|     trace_decls: TraceModule { |     trace_decls: TraceModule { | ||||||
|         name: "connect_const", |         name: "connect_const", | ||||||
|         children: [ |         children: [ | ||||||
|  |  | ||||||
|  | @ -97,79 +97,44 @@ Simulation { | ||||||
|             .. |             .. | ||||||
|         }, |         }, | ||||||
|     }, |     }, | ||||||
|     uninitialized_inputs: {}, |     main_module: SimulationModuleState { | ||||||
|     io_targets: { |         base_targets: [ | ||||||
|         Instance { |             Instance { | ||||||
|             name: <simulator>::connect_const_reset, |                 name: <simulator>::connect_const_reset, | ||||||
|             instantiated: Module { |                 instantiated: Module { | ||||||
|                 name: connect_const_reset, |                     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, |             }.reset_out, | ||||||
|             }, |             Instance { | ||||||
|             range: TypeIndexRange { |                 name: <simulator>::connect_const_reset, | ||||||
|                 small_slots: StatePartIndexRange<SmallSlots> { start: 0, len: 0 }, |                 instantiated: Module { | ||||||
|                 big_slots: StatePartIndexRange<BigSlots> { start: 1, len: 1 }, |                     name: connect_const_reset, | ||||||
|             }, |                     .. | ||||||
|             write: None, |  | ||||||
|         }, |  | ||||||
|         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: [], |  | ||||||
|                         .. |  | ||||||
|                     }, |  | ||||||
|                     big_slots: StatePartLayout<BigSlots> { |  | ||||||
|                         len: 1, |  | ||||||
|                         debug_data: [ |  | ||||||
|                             SlotDebugData { |  | ||||||
|                                 name: "InstantiatedModule(connect_const_reset: connect_const_reset).connect_const_reset::reset_out", |  | ||||||
|                                 ty: AsyncReset, |  | ||||||
|                             }, |  | ||||||
|                         ], |  | ||||||
|                         .. |  | ||||||
|                     }, |  | ||||||
|                 }, |                 }, | ||||||
|                 body: Scalar, |             }.bit_out, | ||||||
|             }, |         ], | ||||||
|             range: TypeIndexRange { |         uninitialized_ios: {}, | ||||||
|                 small_slots: StatePartIndexRange<SmallSlots> { start: 0, len: 0 }, |         io_targets: { | ||||||
|                 big_slots: StatePartIndexRange<BigSlots> { start: 0, len: 1 }, |             Instance { | ||||||
|             }, |                 name: <simulator>::connect_const_reset, | ||||||
|             write: None, |                 instantiated: Module { | ||||||
|  |                     name: connect_const_reset, | ||||||
|  |                     .. | ||||||
|  |                 }, | ||||||
|  |             }.bit_out, | ||||||
|  |             Instance { | ||||||
|  |                 name: <simulator>::connect_const_reset, | ||||||
|  |                 instantiated: Module { | ||||||
|  |                     name: connect_const_reset, | ||||||
|  |                     .. | ||||||
|  |                 }, | ||||||
|  |             }.reset_out, | ||||||
|         }, |         }, | ||||||
|  |         did_initial_settle: true, | ||||||
|     }, |     }, | ||||||
|     made_initial_step: true, |     extern_modules: [], | ||||||
|     needs_settle: false, |     state_ready_to_run: false, | ||||||
|     trace_decls: TraceModule { |     trace_decls: TraceModule { | ||||||
|         name: "connect_const_reset", |         name: "connect_const_reset", | ||||||
|         children: [ |         children: [ | ||||||
|  |  | ||||||
|  | @ -203,213 +203,58 @@ Simulation { | ||||||
|             .. |             .. | ||||||
|         }, |         }, | ||||||
|     }, |     }, | ||||||
|     uninitialized_inputs: {}, |     main_module: SimulationModuleState { | ||||||
|     io_targets: { |         base_targets: [ | ||||||
|         Instance { |             Instance { | ||||||
|             name: <simulator>::counter, |                 name: <simulator>::counter, | ||||||
|             instantiated: Module { |                 instantiated: Module { | ||||||
|                 name: counter, |                     name: counter, | ||||||
|                 .. |                     .. | ||||||
|             }, |  | ||||||
|         }.cd: CompiledValue { |  | ||||||
|             layout: CompiledTypeLayout { |  | ||||||
|                 ty: Bundle { |  | ||||||
|                     /* offset = 0 */ |  | ||||||
|                     clk: Clock, |  | ||||||
|                     /* offset = 1 */ |  | ||||||
|                     rst: AsyncReset, |  | ||||||
|                 }, |                 }, | ||||||
|                 layout: TypeLayout { |             }.cd, | ||||||
|                     small_slots: StatePartLayout<SmallSlots> { |             Instance { | ||||||
|                         len: 0, |                 name: <simulator>::counter, | ||||||
|                         debug_data: [], |                 instantiated: Module { | ||||||
|                         .. |                     name: counter, | ||||||
|                     }, |                     .. | ||||||
|                     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 { |             }.count, | ||||||
|                     fields: [ |         ], | ||||||
|                         CompiledBundleField { |         uninitialized_ios: {}, | ||||||
|                             offset: TypeIndex { |         io_targets: { | ||||||
|                                 small_slots: StatePartIndex<SmallSlots>(0), |             Instance { | ||||||
|                                 big_slots: StatePartIndex<BigSlots>(0), |                 name: <simulator>::counter, | ||||||
|                             }, |                 instantiated: Module { | ||||||
|                             ty: CompiledTypeLayout { |                     name: counter, | ||||||
|                                 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, |  | ||||||
|                             }, |  | ||||||
|                         }, |  | ||||||
|                     ], |  | ||||||
|                 }, |                 }, | ||||||
|             }, |             }.cd, | ||||||
|             range: TypeIndexRange { |             Instance { | ||||||
|                 small_slots: StatePartIndexRange<SmallSlots> { start: 0, len: 0 }, |                 name: <simulator>::counter, | ||||||
|                 big_slots: StatePartIndexRange<BigSlots> { start: 0, len: 2 }, |                 instantiated: Module { | ||||||
|             }, |                     name: counter, | ||||||
|             write: None, |                     .. | ||||||
|         }, |                 }, | ||||||
|         Instance { |             }.cd.clk, | ||||||
|             name: <simulator>::counter, |             Instance { | ||||||
|             instantiated: Module { |                 name: <simulator>::counter, | ||||||
|                 name: counter, |                 instantiated: Module { | ||||||
|                 .. |                     name: counter, | ||||||
|             }, |                     .. | ||||||
|         }.cd.clk: CompiledValue { |                 }, | ||||||
|             layout: CompiledTypeLayout { |             }.cd.rst, | ||||||
|                 ty: Clock, |             Instance { | ||||||
|                 layout: TypeLayout { |                 name: <simulator>::counter, | ||||||
|                     small_slots: StatePartLayout<SmallSlots> { |                 instantiated: Module { | ||||||
|                         len: 0, |                     name: counter, | ||||||
|                         debug_data: [], |                     .. | ||||||
|                         .. |                 }, | ||||||
|                     }, |             }.count, | ||||||
|                     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, |  | ||||||
|         }, |  | ||||||
|         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, |  | ||||||
|         }, |  | ||||||
|         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: [], |  | ||||||
|                         .. |  | ||||||
|                     }, |  | ||||||
|                     big_slots: StatePartLayout<BigSlots> { |  | ||||||
|                         len: 1, |  | ||||||
|                         debug_data: [ |  | ||||||
|                             SlotDebugData { |  | ||||||
|                                 name: "InstantiatedModule(counter: counter).counter::count", |  | ||||||
|                                 ty: UInt<4>, |  | ||||||
|                             }, |  | ||||||
|                         ], |  | ||||||
|                         .. |  | ||||||
|                     }, |  | ||||||
|                 }, |  | ||||||
|                 body: Scalar, |  | ||||||
|             }, |  | ||||||
|             range: TypeIndexRange { |  | ||||||
|                 small_slots: StatePartIndexRange<SmallSlots> { start: 0, len: 0 }, |  | ||||||
|                 big_slots: StatePartIndexRange<BigSlots> { start: 2, len: 1 }, |  | ||||||
|             }, |  | ||||||
|             write: None, |  | ||||||
|         }, |         }, | ||||||
|  |         did_initial_settle: true, | ||||||
|     }, |     }, | ||||||
|     made_initial_step: true, |     extern_modules: [], | ||||||
|     needs_settle: false, |     state_ready_to_run: false, | ||||||
|     trace_decls: TraceModule { |     trace_decls: TraceModule { | ||||||
|         name: "counter", |         name: "counter", | ||||||
|         children: [ |         children: [ | ||||||
|  |  | ||||||
|  | @ -184,213 +184,58 @@ Simulation { | ||||||
|             .. |             .. | ||||||
|         }, |         }, | ||||||
|     }, |     }, | ||||||
|     uninitialized_inputs: {}, |     main_module: SimulationModuleState { | ||||||
|     io_targets: { |         base_targets: [ | ||||||
|         Instance { |             Instance { | ||||||
|             name: <simulator>::counter, |                 name: <simulator>::counter, | ||||||
|             instantiated: Module { |                 instantiated: Module { | ||||||
|                 name: counter, |                     name: counter, | ||||||
|                 .. |                     .. | ||||||
|             }, |  | ||||||
|         }.cd: CompiledValue { |  | ||||||
|             layout: CompiledTypeLayout { |  | ||||||
|                 ty: Bundle { |  | ||||||
|                     /* offset = 0 */ |  | ||||||
|                     clk: Clock, |  | ||||||
|                     /* offset = 1 */ |  | ||||||
|                     rst: SyncReset, |  | ||||||
|                 }, |                 }, | ||||||
|                 layout: TypeLayout { |             }.cd, | ||||||
|                     small_slots: StatePartLayout<SmallSlots> { |             Instance { | ||||||
|                         len: 0, |                 name: <simulator>::counter, | ||||||
|                         debug_data: [], |                 instantiated: Module { | ||||||
|                         .. |                     name: counter, | ||||||
|                     }, |                     .. | ||||||
|                     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 { |             }.count, | ||||||
|                     fields: [ |         ], | ||||||
|                         CompiledBundleField { |         uninitialized_ios: {}, | ||||||
|                             offset: TypeIndex { |         io_targets: { | ||||||
|                                 small_slots: StatePartIndex<SmallSlots>(0), |             Instance { | ||||||
|                                 big_slots: StatePartIndex<BigSlots>(0), |                 name: <simulator>::counter, | ||||||
|                             }, |                 instantiated: Module { | ||||||
|                             ty: CompiledTypeLayout { |                     name: counter, | ||||||
|                                 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, |  | ||||||
|                             }, |  | ||||||
|                         }, |  | ||||||
|                     ], |  | ||||||
|                 }, |                 }, | ||||||
|             }, |             }.cd, | ||||||
|             range: TypeIndexRange { |             Instance { | ||||||
|                 small_slots: StatePartIndexRange<SmallSlots> { start: 0, len: 0 }, |                 name: <simulator>::counter, | ||||||
|                 big_slots: StatePartIndexRange<BigSlots> { start: 0, len: 2 }, |                 instantiated: Module { | ||||||
|             }, |                     name: counter, | ||||||
|             write: None, |                     .. | ||||||
|         }, |                 }, | ||||||
|         Instance { |             }.cd.clk, | ||||||
|             name: <simulator>::counter, |             Instance { | ||||||
|             instantiated: Module { |                 name: <simulator>::counter, | ||||||
|                 name: counter, |                 instantiated: Module { | ||||||
|                 .. |                     name: counter, | ||||||
|             }, |                     .. | ||||||
|         }.cd.clk: CompiledValue { |                 }, | ||||||
|             layout: CompiledTypeLayout { |             }.cd.rst, | ||||||
|                 ty: Clock, |             Instance { | ||||||
|                 layout: TypeLayout { |                 name: <simulator>::counter, | ||||||
|                     small_slots: StatePartLayout<SmallSlots> { |                 instantiated: Module { | ||||||
|                         len: 0, |                     name: counter, | ||||||
|                         debug_data: [], |                     .. | ||||||
|                         .. |                 }, | ||||||
|                     }, |             }.count, | ||||||
|                     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, |  | ||||||
|         }, |  | ||||||
|         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, |  | ||||||
|         }, |  | ||||||
|         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: [], |  | ||||||
|                         .. |  | ||||||
|                     }, |  | ||||||
|                     big_slots: StatePartLayout<BigSlots> { |  | ||||||
|                         len: 1, |  | ||||||
|                         debug_data: [ |  | ||||||
|                             SlotDebugData { |  | ||||||
|                                 name: "InstantiatedModule(counter: counter).counter::count", |  | ||||||
|                                 ty: UInt<4>, |  | ||||||
|                             }, |  | ||||||
|                         ], |  | ||||||
|                         .. |  | ||||||
|                     }, |  | ||||||
|                 }, |  | ||||||
|                 body: Scalar, |  | ||||||
|             }, |  | ||||||
|             range: TypeIndexRange { |  | ||||||
|                 small_slots: StatePartIndexRange<SmallSlots> { start: 0, len: 0 }, |  | ||||||
|                 big_slots: StatePartIndexRange<BigSlots> { start: 2, len: 1 }, |  | ||||||
|             }, |  | ||||||
|             write: None, |  | ||||||
|         }, |         }, | ||||||
|  |         did_initial_settle: true, | ||||||
|     }, |     }, | ||||||
|     made_initial_step: true, |     extern_modules: [], | ||||||
|     needs_settle: false, |     state_ready_to_run: false, | ||||||
|     trace_decls: TraceModule { |     trace_decls: TraceModule { | ||||||
|         name: "counter", |         name: "counter", | ||||||
|         children: [ |         children: [ | ||||||
|  |  | ||||||
|  | @ -88,10 +88,14 @@ Simulation { | ||||||
|             .. |             .. | ||||||
|         }, |         }, | ||||||
|     }, |     }, | ||||||
|     uninitialized_inputs: {}, |     main_module: SimulationModuleState { | ||||||
|     io_targets: {}, |         base_targets: [], | ||||||
|     made_initial_step: true, |         uninitialized_ios: {}, | ||||||
|     needs_settle: false, |         io_targets: {}, | ||||||
|  |         did_initial_settle: true, | ||||||
|  |     }, | ||||||
|  |     extern_modules: [], | ||||||
|  |     state_ready_to_run: false, | ||||||
|     trace_decls: TraceModule { |     trace_decls: TraceModule { | ||||||
|         name: "duplicate_names", |         name: "duplicate_names", | ||||||
|         children: [ |         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 | $var wire 1 * \1 $end | ||||||
| $upscope $end | $upscope $end | ||||||
| $upscope $end | $upscope $end | ||||||
| $scope struct the_reg $end | $scope struct b2_out $end | ||||||
| $var string 1 + \$tag $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 | $scope struct B $end | ||||||
| $var reg 1 , \0 $end | $var reg 1 / \0 $end | ||||||
| $var reg 1 - \1 $end | $var reg 1 0 \1 $end | ||||||
| $upscope $end | $upscope $end | ||||||
| $scope struct C $end | $scope struct C $end | ||||||
| $scope struct a $end | $scope struct a $end | ||||||
| $var reg 1 . \[0] $end | $var reg 1 1 \[0] $end | ||||||
| $var reg 1 / \[1] $end | $var reg 1 2 \[1] $end | ||||||
| $upscope $end | $upscope $end | ||||||
| $var reg 2 0 b $end | $var reg 2 3 b $end | ||||||
| $upscope $end | $upscope $end | ||||||
| $upscope $end | $upscope $end | ||||||
| $upscope $end | $upscope $end | ||||||
|  | @ -43,12 +50,15 @@ b0 ' | ||||||
| sHdlNone\x20(0) ( | sHdlNone\x20(0) ( | ||||||
| 0) | 0) | ||||||
| 0* | 0* | ||||||
| sA\x20(0) + | sHdlNone\x20(0) + | ||||||
| 0, | 0, | ||||||
| 0- | 0- | ||||||
| 0. | sA\x20(0) . | ||||||
| 0/ | 0/ | ||||||
| b0 0 | 00 | ||||||
|  | 01 | ||||||
|  | 02 | ||||||
|  | b0 3 | ||||||
| $end | $end | ||||||
| #1000000 | #1000000 | ||||||
| 1! | 1! | ||||||
|  | @ -66,7 +76,8 @@ b1 $ | ||||||
| 1! | 1! | ||||||
| b1 & | b1 & | ||||||
| sHdlSome\x20(1) ( | sHdlSome\x20(1) ( | ||||||
| sB\x20(1) + | sHdlSome\x20(1) + | ||||||
|  | sB\x20(1) . | ||||||
| #6000000 | #6000000 | ||||||
| 0# | 0# | ||||||
| b0 $ | b0 $ | ||||||
|  | @ -85,8 +96,10 @@ b11 ' | ||||||
| 1* | 1* | ||||||
| 1, | 1, | ||||||
| 1- | 1- | ||||||
| 1. |  | ||||||
| 1/ | 1/ | ||||||
|  | 10 | ||||||
|  | 11 | ||||||
|  | 12 | ||||||
| #10000000 | #10000000 | ||||||
| 0! | 0! | ||||||
| #11000000 | #11000000 | ||||||
|  | @ -101,8 +114,11 @@ b1111 ' | ||||||
| sHdlNone\x20(0) ( | sHdlNone\x20(0) ( | ||||||
| 0) | 0) | ||||||
| 0* | 0* | ||||||
| sC\x20(2) + | sHdlNone\x20(0) + | ||||||
| b11 0 | 0, | ||||||
|  | 0- | ||||||
|  | sC\x20(2) . | ||||||
|  | b11 3 | ||||||
| #14000000 | #14000000 | ||||||
| 0! | 0! | ||||||
| #15000000 | #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,514 +598,79 @@ Simulation { | ||||||
|             .. |             .. | ||||||
|         }, |         }, | ||||||
|     }, |     }, | ||||||
|     uninitialized_inputs: {}, |     main_module: SimulationModuleState { | ||||||
|     io_targets: { |         base_targets: [ | ||||||
|         Instance { |             Instance { | ||||||
|             name: <simulator>::memories2, |                 name: <simulator>::memories2, | ||||||
|             instantiated: Module { |                 instantiated: Module { | ||||||
|                 name: memories2, |                     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 { |             }.rw, | ||||||
|                     small_slots: StatePartLayout<SmallSlots> { |         ], | ||||||
|                         len: 0, |         uninitialized_ios: {}, | ||||||
|                         debug_data: [], |         io_targets: { | ||||||
|                         .. |             Instance { | ||||||
|                     }, |                 name: <simulator>::memories2, | ||||||
|                     big_slots: StatePartLayout<BigSlots> { |                 instantiated: Module { | ||||||
|                         len: 7, |                     name: memories2, | ||||||
|                         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 { |             }.rw, | ||||||
|                     fields: [ |             Instance { | ||||||
|                         CompiledBundleField { |                 name: <simulator>::memories2, | ||||||
|                             offset: TypeIndex { |                 instantiated: Module { | ||||||
|                                 small_slots: StatePartIndex<SmallSlots>(0), |                     name: memories2, | ||||||
|                                 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, |  | ||||||
|                             }, |  | ||||||
|                         }, |  | ||||||
|                     ], |  | ||||||
|                 }, |                 }, | ||||||
|             }, |             }.rw.addr, | ||||||
|             range: TypeIndexRange { |             Instance { | ||||||
|                 small_slots: StatePartIndexRange<SmallSlots> { start: 0, len: 0 }, |                 name: <simulator>::memories2, | ||||||
|                 big_slots: StatePartIndexRange<BigSlots> { start: 0, len: 7 }, |                 instantiated: Module { | ||||||
|             }, |                     name: memories2, | ||||||
|             write: None, |                     .. | ||||||
|         }, |                 }, | ||||||
|         Instance { |             }.rw.clk, | ||||||
|             name: <simulator>::memories2, |             Instance { | ||||||
|             instantiated: Module { |                 name: <simulator>::memories2, | ||||||
|                 name: memories2, |                 instantiated: Module { | ||||||
|                 .. |                     name: memories2, | ||||||
|             }, |                     .. | ||||||
|         }.rw.addr: CompiledValue { |                 }, | ||||||
|             layout: CompiledTypeLayout { |             }.rw.en, | ||||||
|                 ty: UInt<3>, |             Instance { | ||||||
|                 layout: TypeLayout { |                 name: <simulator>::memories2, | ||||||
|                     small_slots: StatePartLayout<SmallSlots> { |                 instantiated: Module { | ||||||
|                         len: 0, |                     name: memories2, | ||||||
|                         debug_data: [], |                     .. | ||||||
|                         .. |                 }, | ||||||
|                     }, |             }.rw.rdata, | ||||||
|                     big_slots: StatePartLayout<BigSlots> { |             Instance { | ||||||
|                         len: 1, |                 name: <simulator>::memories2, | ||||||
|                         debug_data: [ |                 instantiated: Module { | ||||||
|                             SlotDebugData { |                     name: memories2, | ||||||
|                                 name: "", |                     .. | ||||||
|                                 ty: UInt<3>, |                 }, | ||||||
|                             }, |             }.rw.wdata, | ||||||
|                         ], |             Instance { | ||||||
|                         .. |                 name: <simulator>::memories2, | ||||||
|                     }, |                 instantiated: Module { | ||||||
|                 }, |                     name: memories2, | ||||||
|                 body: Scalar, |                     .. | ||||||
|             }, |                 }, | ||||||
|             range: TypeIndexRange { |             }.rw.wmask, | ||||||
|                 small_slots: StatePartIndexRange<SmallSlots> { start: 0, len: 0 }, |             Instance { | ||||||
|                 big_slots: StatePartIndexRange<BigSlots> { start: 0, len: 1 }, |                 name: <simulator>::memories2, | ||||||
|             }, |                 instantiated: Module { | ||||||
|             write: None, |                     name: memories2, | ||||||
|         }, |                     .. | ||||||
|         Instance { |                 }, | ||||||
|             name: <simulator>::memories2, |             }.rw.wmode, | ||||||
|             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, |  | ||||||
|         }, |  | ||||||
|         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, |  | ||||||
|         }, |  | ||||||
|         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, |  | ||||||
|         }, |  | ||||||
|         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, |  | ||||||
|         }, |  | ||||||
|         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, |  | ||||||
|         }, |  | ||||||
|         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: [], |  | ||||||
|                         .. |  | ||||||
|                     }, |  | ||||||
|                     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: 4, len: 1 }, |  | ||||||
|             }, |  | ||||||
|             write: None, |  | ||||||
|         }, |         }, | ||||||
|  |         did_initial_settle: true, | ||||||
|     }, |     }, | ||||||
|     made_initial_step: true, |     extern_modules: [], | ||||||
|     needs_settle: false, |     state_ready_to_run: false, | ||||||
|     trace_decls: TraceModule { |     trace_decls: TraceModule { | ||||||
|         name: "memories2", |         name: "memories2", | ||||||
|         children: [ |         children: [ | ||||||
|  |  | ||||||
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							|  | @ -216,313 +216,58 @@ Simulation { | ||||||
|             .. |             .. | ||||||
|         }, |         }, | ||||||
|     }, |     }, | ||||||
|     uninitialized_inputs: {}, |     main_module: SimulationModuleState { | ||||||
|     io_targets: { |         base_targets: [ | ||||||
|         Instance { |             Instance { | ||||||
|             name: <simulator>::mod1, |                 name: <simulator>::mod1, | ||||||
|             instantiated: Module { |                 instantiated: Module { | ||||||
|                 name: mod1, |                     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 { |             }.o, | ||||||
|                     small_slots: StatePartLayout<SmallSlots> { |         ], | ||||||
|                         len: 0, |         uninitialized_ios: {}, | ||||||
|                         debug_data: [], |         io_targets: { | ||||||
|                         .. |             Instance { | ||||||
|                     }, |                 name: <simulator>::mod1, | ||||||
|                     big_slots: StatePartLayout<BigSlots> { |                 instantiated: Module { | ||||||
|                         len: 4, |                     name: mod1, | ||||||
|                         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 { |             }.o, | ||||||
|                     fields: [ |             Instance { | ||||||
|                         CompiledBundleField { |                 name: <simulator>::mod1, | ||||||
|                             offset: TypeIndex { |                 instantiated: Module { | ||||||
|                                 small_slots: StatePartIndex<SmallSlots>(0), |                     name: mod1, | ||||||
|                                 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, |  | ||||||
|                             }, |  | ||||||
|                         }, |  | ||||||
|                     ], |  | ||||||
|                 }, |                 }, | ||||||
|             }, |             }.o.i, | ||||||
|             range: TypeIndexRange { |             Instance { | ||||||
|                 small_slots: StatePartIndexRange<SmallSlots> { start: 0, len: 0 }, |                 name: <simulator>::mod1, | ||||||
|                 big_slots: StatePartIndexRange<BigSlots> { start: 0, len: 4 }, |                 instantiated: Module { | ||||||
|             }, |                     name: mod1, | ||||||
|             write: None, |                     .. | ||||||
|         }, |                 }, | ||||||
|         Instance { |             }.o.i2, | ||||||
|             name: <simulator>::mod1, |             Instance { | ||||||
|             instantiated: Module { |                 name: <simulator>::mod1, | ||||||
|                 name: mod1, |                 instantiated: Module { | ||||||
|                 .. |                     name: mod1, | ||||||
|             }, |                     .. | ||||||
|         }.o.i: CompiledValue { |                 }, | ||||||
|             layout: CompiledTypeLayout { |             }.o.o, | ||||||
|                 ty: UInt<4>, |             Instance { | ||||||
|                 layout: TypeLayout { |                 name: <simulator>::mod1, | ||||||
|                     small_slots: StatePartLayout<SmallSlots> { |                 instantiated: Module { | ||||||
|                         len: 0, |                     name: mod1, | ||||||
|                         debug_data: [], |                     .. | ||||||
|                         .. |                 }, | ||||||
|                     }, |             }.o.o2, | ||||||
|                     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, |  | ||||||
|         }, |  | ||||||
|         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, |  | ||||||
|         }, |  | ||||||
|         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, |  | ||||||
|         }, |  | ||||||
|         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: [], |  | ||||||
|                         .. |  | ||||||
|                     }, |  | ||||||
|                     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: 3, len: 1 }, |  | ||||||
|             }, |  | ||||||
|             write: None, |  | ||||||
|         }, |         }, | ||||||
|  |         did_initial_settle: true, | ||||||
|     }, |     }, | ||||||
|     made_initial_step: true, |     extern_modules: [], | ||||||
|     needs_settle: false, |     state_ready_to_run: false, | ||||||
|     trace_decls: TraceModule { |     trace_decls: TraceModule { | ||||||
|         name: "mod1", |         name: "mod1", | ||||||
|         children: [ |         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,247 +265,72 @@ Simulation { | ||||||
|             .. |             .. | ||||||
|         }, |         }, | ||||||
|     }, |     }, | ||||||
|     uninitialized_inputs: {}, |     main_module: SimulationModuleState { | ||||||
|     io_targets: { |         base_targets: [ | ||||||
|         Instance { |             Instance { | ||||||
|             name: <simulator>::shift_register, |                 name: <simulator>::shift_register, | ||||||
|             instantiated: Module { |                 instantiated: Module { | ||||||
|                 name: shift_register, |                     name: shift_register, | ||||||
|                 .. |                     .. | ||||||
|             }, |  | ||||||
|         }.cd: CompiledValue { |  | ||||||
|             layout: CompiledTypeLayout { |  | ||||||
|                 ty: Bundle { |  | ||||||
|                     /* offset = 0 */ |  | ||||||
|                     clk: Clock, |  | ||||||
|                     /* offset = 1 */ |  | ||||||
|                     rst: SyncReset, |  | ||||||
|                 }, |                 }, | ||||||
|                 layout: TypeLayout { |             }.cd, | ||||||
|                     small_slots: StatePartLayout<SmallSlots> { |             Instance { | ||||||
|                         len: 0, |                 name: <simulator>::shift_register, | ||||||
|                         debug_data: [], |                 instantiated: Module { | ||||||
|                         .. |                     name: shift_register, | ||||||
|                     }, |                     .. | ||||||
|                     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 { |             }.d, | ||||||
|                     fields: [ |             Instance { | ||||||
|                         CompiledBundleField { |                 name: <simulator>::shift_register, | ||||||
|                             offset: TypeIndex { |                 instantiated: Module { | ||||||
|                                 small_slots: StatePartIndex<SmallSlots>(0), |                     name: shift_register, | ||||||
|                                 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, |  | ||||||
|                             }, |  | ||||||
|                         }, |  | ||||||
|                     ], |  | ||||||
|                 }, |                 }, | ||||||
|             }, |             }.q, | ||||||
|             range: TypeIndexRange { |         ], | ||||||
|                 small_slots: StatePartIndexRange<SmallSlots> { start: 0, len: 0 }, |         uninitialized_ios: {}, | ||||||
|                 big_slots: StatePartIndexRange<BigSlots> { start: 0, len: 2 }, |         io_targets: { | ||||||
|             }, |             Instance { | ||||||
|             write: None, |                 name: <simulator>::shift_register, | ||||||
|         }, |                 instantiated: Module { | ||||||
|         Instance { |                     name: shift_register, | ||||||
|             name: <simulator>::shift_register, |                     .. | ||||||
|             instantiated: Module { |                 }, | ||||||
|                 name: shift_register, |             }.cd, | ||||||
|                 .. |             Instance { | ||||||
|             }, |                 name: <simulator>::shift_register, | ||||||
|         }.cd.clk: CompiledValue { |                 instantiated: Module { | ||||||
|             layout: CompiledTypeLayout { |                     name: shift_register, | ||||||
|                 ty: Clock, |                     .. | ||||||
|                 layout: TypeLayout { |                 }, | ||||||
|                     small_slots: StatePartLayout<SmallSlots> { |             }.cd.clk, | ||||||
|                         len: 0, |             Instance { | ||||||
|                         debug_data: [], |                 name: <simulator>::shift_register, | ||||||
|                         .. |                 instantiated: Module { | ||||||
|                     }, |                     name: shift_register, | ||||||
|                     big_slots: StatePartLayout<BigSlots> { |                     .. | ||||||
|                         len: 1, |                 }, | ||||||
|                         debug_data: [ |             }.cd.rst, | ||||||
|                             SlotDebugData { |             Instance { | ||||||
|                                 name: "", |                 name: <simulator>::shift_register, | ||||||
|                                 ty: Clock, |                 instantiated: Module { | ||||||
|                             }, |                     name: shift_register, | ||||||
|                         ], |                     .. | ||||||
|                         .. |                 }, | ||||||
|                     }, |             }.d, | ||||||
|                 }, |             Instance { | ||||||
|                 body: Scalar, |                 name: <simulator>::shift_register, | ||||||
|             }, |                 instantiated: Module { | ||||||
|             range: TypeIndexRange { |                     name: shift_register, | ||||||
|                 small_slots: StatePartIndexRange<SmallSlots> { start: 0, len: 0 }, |                     .. | ||||||
|                 big_slots: StatePartIndexRange<BigSlots> { start: 0, len: 1 }, |                 }, | ||||||
|             }, |             }.q, | ||||||
|             write: None, |  | ||||||
|         }, |  | ||||||
|         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, |  | ||||||
|         }, |  | ||||||
|         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, |  | ||||||
|         }, |  | ||||||
|         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: [], |  | ||||||
|                         .. |  | ||||||
|                     }, |  | ||||||
|                     big_slots: StatePartLayout<BigSlots> { |  | ||||||
|                         len: 1, |  | ||||||
|                         debug_data: [ |  | ||||||
|                             SlotDebugData { |  | ||||||
|                                 name: "InstantiatedModule(shift_register: shift_register).shift_register::q", |  | ||||||
|                                 ty: Bool, |  | ||||||
|                             }, |  | ||||||
|                         ], |  | ||||||
|                         .. |  | ||||||
|                     }, |  | ||||||
|                 }, |  | ||||||
|                 body: Scalar, |  | ||||||
|             }, |  | ||||||
|             range: TypeIndexRange { |  | ||||||
|                 small_slots: StatePartIndexRange<SmallSlots> { start: 0, len: 0 }, |  | ||||||
|                 big_slots: StatePartIndexRange<BigSlots> { start: 3, len: 1 }, |  | ||||||
|             }, |  | ||||||
|             write: None, |  | ||||||
|         }, |         }, | ||||||
|  |         did_initial_settle: true, | ||||||
|     }, |     }, | ||||||
|     made_initial_step: true, |     extern_modules: [], | ||||||
|     needs_settle: false, |     state_ready_to_run: false, | ||||||
|     trace_decls: TraceModule { |     trace_decls: TraceModule { | ||||||
|         name: "shift_register", |         name: "shift_register", | ||||||
|         children: [ |         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", |                 "AsyncReset": "Visible", | ||||||
|                 "SyncReset": "Visible", |                 "SyncReset": "Visible", | ||||||
|                 "Reset": "Visible", |                 "Reset": "Visible", | ||||||
|                 "Clock": "Visible" |                 "Clock": "Visible", | ||||||
|  |                 "PhantomConst": "Visible" | ||||||
|             } |             } | ||||||
|         }, |         }, | ||||||
|         "Bundle": { |         "Bundle": { | ||||||
|  | @ -159,7 +160,8 @@ | ||||||
|             "data": { |             "data": { | ||||||
|                 "$kind": "Struct", |                 "$kind": "Struct", | ||||||
|                 "verilog_name": "Visible", |                 "verilog_name": "Visible", | ||||||
|                 "parameters": "Visible" |                 "parameters": "Visible", | ||||||
|  |                 "simulation": "Visible" | ||||||
|             } |             } | ||||||
|         }, |         }, | ||||||
|         "ExternModuleParameter": { |         "ExternModuleParameter": { | ||||||
|  | @ -1262,6 +1264,17 @@ | ||||||
|                 "ArrayElement": "Visible", |                 "ArrayElement": "Visible", | ||||||
|                 "DynArrayElement": "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