diff --git a/.forgejo/workflows/deps.yml b/.forgejo/workflows/deps.yml index ffaca53..b29723c 100644 --- a/.forgejo/workflows/deps.yml +++ b/.forgejo/workflows/deps.yml @@ -12,10 +12,10 @@ jobs: outputs: cache-primary-key: ${{ steps.restore-deps.outputs.cache-primary-key }} steps: - - uses: https://code.forgejo.org/actions/checkout@v3 + - uses: https://git.libre-chip.org/mirrors/checkout@v3 with: fetch-depth: 0 - - uses: https://code.forgejo.org/actions/cache/restore@v3 + - uses: https://git.libre-chip.org/mirrors/cache/restore@v3 id: restore-deps with: path: deps @@ -58,19 +58,19 @@ jobs: - name: Get SymbiYosys if: steps.restore-deps.outputs.cache-hit != 'true' run: | - git clone --depth=1 --branch=yosys-0.45 https://github.com/YosysHQ/sby.git deps/sby + git clone --depth=1 --branch=yosys-0.45 https://git.libre-chip.org/mirrors/sby deps/sby - name: Build Z3 if: steps.restore-deps.outputs.cache-hit != 'true' run: | - git clone --depth=1 --recursive --branch=z3-4.13.3 https://github.com/Z3Prover/z3.git deps/z3 + git clone --depth=1 --recursive --branch=z3-4.13.3 https://git.libre-chip.org/mirrors/z3 deps/z3 (cd deps/z3; PYTHON=python3 ./configure --prefix=/usr/local) make -C deps/z3/build -j"$(nproc)" - name: Build Yosys if: steps.restore-deps.outputs.cache-hit != 'true' run: | - git clone --depth=1 --recursive --branch=0.45 https://github.com/YosysHQ/yosys.git deps/yosys + git clone --depth=1 --recursive --branch=0.45 https://git.libre-chip.org/mirrors/yosys deps/yosys make -C deps/yosys -j"$(nproc)" - - uses: https://code.forgejo.org/actions/cache/save@v3 + - uses: https://git.libre-chip.org/mirrors/cache/save@v3 if: steps.restore-deps.outputs.cache-hit != 'true' with: path: deps diff --git a/.forgejo/workflows/test.yml b/.forgejo/workflows/test.yml index e83c668..294ccaa 100644 --- a/.forgejo/workflows/test.yml +++ b/.forgejo/workflows/test.yml @@ -4,12 +4,13 @@ on: [push, pull_request] jobs: deps: + runs-on: debian-12 uses: ./.forgejo/workflows/deps.yml test: runs-on: debian-12 needs: deps steps: - - uses: https://code.forgejo.org/actions/checkout@v3 + - uses: https://git.libre-chip.org/mirrors/checkout@v3 with: fetch-depth: 0 - run: | @@ -38,10 +39,11 @@ jobs: z3 \ zlib1g-dev - run: | - 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.89.0 source "$HOME/.cargo/env" + rustup component add rust-src echo "$PATH" >> "$GITHUB_PATH" - - uses: https://code.forgejo.org/actions/cache/restore@v3 + - uses: https://git.libre-chip.org/mirrors/cache/restore@v3 with: path: deps key: ${{ needs.deps.outputs.cache-primary-key }} @@ -52,10 +54,11 @@ jobs: make -C deps/yosys install export PATH="$(realpath deps/firtool/bin):$PATH" echo "$PATH" >> "$GITHUB_PATH" - - uses: https://github.com/Swatinem/rust-cache@v2 + - uses: https://git.libre-chip.org/mirrors/rust-cache@v2 with: save-if: ${{ github.ref == 'refs/heads/master' }} - run: cargo test - run: cargo build --tests --features=unstable-doc - run: cargo test --doc --features=unstable-doc - run: cargo doc --features=unstable-doc + - run: FAYALITE_TEST_HASHER=always_zero cargo test --test=module --features=unstable-doc,unstable-test-hasher diff --git a/Cargo.lock b/Cargo.lock index 23cdc34..e0c32e9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,18 +2,6 @@ # It is not intended for manual editing. version = 3 -[[package]] -name = "ahash" -version = "0.8.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77c3a9648d43b9cd48db467b3f87fdd6e146bcc88ab0180006cef2179fe11d01" -dependencies = [ - "cfg-if", - "once_cell", - "version_check", - "zerocopy", -] - [[package]] name = "allocator-api2" version = "0.2.16" @@ -365,6 +353,12 @@ version = "0.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1d674e81391d1e1ab681a28d99df07927c6d4aa5b027d7da16ba32d1d21ecd99" +[[package]] +name = "foldhash" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" + [[package]] name = "funty" version = "2.0.0" @@ -400,12 +394,13 @@ checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" [[package]] name = "hashbrown" -version = "0.14.3" +version = "0.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" +checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" dependencies = [ - "ahash", "allocator-api2", + "equivalent", + "foldhash", ] [[package]] @@ -431,9 +426,9 @@ checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683" [[package]] name = "indexmap" -version = "2.5.0" +version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68b900aa2f7301e21c36462b170ee99994de34dff39a4a6a528e80e7376d07e5" +checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e" dependencies = [ "equivalent", "hashbrown", @@ -524,11 +519,14 @@ dependencies = [ [[package]] name = "petgraph" -version = "0.6.5" -source = "git+https://github.com/programmerjake/petgraph.git?rev=258ea8071209a924b73fe96f9f87a3b7b45cbc9f#258ea8071209a924b73fe96f9f87a3b7b45cbc9f" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a98c6720655620a521dcc722d0ad66cd8afd5d86e34a89ef691c50b7b24de06" dependencies = [ "fixedbitset", + "hashbrown", "indexmap", + "serde", ] [[package]] @@ -893,23 +891,3 @@ checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" dependencies = [ "tap", ] - -[[package]] -name = "zerocopy" -version = "0.7.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74d4d3961e53fa4c9a25a8637fc2bfaf2595b3d3ae34875568a5cf64787716be" -dependencies = [ - "zerocopy-derive", -] - -[[package]] -name = "zerocopy-derive" -version = "0.7.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] diff --git a/Cargo.toml b/Cargo.toml index 54de3a8..5a792c6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,11 +7,11 @@ members = ["crates/*"] [workspace.package] version = "0.3.0" license = "LGPL-3.0-or-later" -edition = "2021" +edition = "2024" repository = "https://git.libre-chip.org/libre-chip/fayalite" keywords = ["hdl", "hardware", "semiconductors", "firrtl", "fpga"] categories = ["simulation", "development-tools", "compilers"] -rust-version = "1.82.0" +rust-version = "1.89.0" [workspace.dependencies] fayalite-proc-macros = { version = "=0.3.0", path = "crates/fayalite-proc-macros" } @@ -23,14 +23,13 @@ blake3 = { version = "1.5.4", features = ["serde"] } clap = { version = "4.5.9", features = ["derive", "env", "string"] } ctor = "0.2.8" eyre = "0.6.12" -hashbrown = "0.14.3" +hashbrown = "0.15.2" indexmap = { version = "2.5.0", features = ["serde"] } jobslot = "0.2.19" num-bigint = "0.4.6" num-traits = "0.2.16" os_pipe = "1.2.1" -# TODO: switch back to crates.io once petgraph accepts PR #684 and releases a new version -petgraph = { git = "https://github.com/programmerjake/petgraph.git", rev = "258ea8071209a924b73fe96f9f87a3b7b45cbc9f" } +petgraph = "0.8.1" prettyplease = "0.2.20" proc-macro2 = "1.0.83" quote = "1.0.36" diff --git a/README.md b/README.md index 438550e..0b91833 100644 --- a/README.md +++ b/README.md @@ -7,3 +7,11 @@ See Notices.txt for copyright information Fayalite is a library for designing digital hardware -- a hardware description language (HDL) embedded in the Rust programming language. Fayalite's semantics are based on [FIRRTL] as interpreted by [LLVM CIRCT](https://circt.llvm.org/docs/Dialects/FIRRTL/FIRRTLAnnotations/). [FIRRTL]: https://github.com/chipsalliance/firrtl-spec + +# Funding + +## NLnet Grants + +* [Libre-Chip CPU with proof of No Spectre bugs](https://nlnet.nl/project/Libre-Chip-proof/) 2024-12-324 [(progress)](https://git.libre-chip.org/libre-chip/grant-tracking/src/branch/master/nlnet-2024-12-324/progress.md) + +This project was funded through the [NGI0 Commons Fund](https://nlnet.nl/commonsfund), a fund established by [NLnet](https://nlnet.nl/) with financial support from the European Commission's [Next Generation Internet](https://ngi.eu) programme, under the aegis of [DG Communications Networks, Content and Technology](https://commission.europa.eu/about-european-commission/departments-and-executive-agencies/communications-networks-content-and-technology_en) under grant agreement № [101135429](https://cordis.europa.eu/project/id/101135429). Additional funding is made available by the [Swiss State Secretariat for Education, Research and Innovation](https://www.sbfi.admin.ch/sbfi/en/home.html) (SERI). diff --git a/crates/fayalite-proc-macros-impl/src/fold.rs b/crates/fayalite-proc-macros-impl/src/fold.rs index 49cc8c1..22e7b82 100644 --- a/crates/fayalite-proc-macros-impl/src/fold.rs +++ b/crates/fayalite-proc-macros-impl/src/fold.rs @@ -220,6 +220,7 @@ forward_fold!(syn::ExprArray => fold_expr_array); forward_fold!(syn::ExprCall => fold_expr_call); forward_fold!(syn::ExprIf => fold_expr_if); forward_fold!(syn::ExprMatch => fold_expr_match); +forward_fold!(syn::ExprMethodCall => fold_expr_method_call); forward_fold!(syn::ExprPath => fold_expr_path); forward_fold!(syn::ExprRepeat => fold_expr_repeat); forward_fold!(syn::ExprStruct => fold_expr_struct); diff --git a/crates/fayalite-proc-macros-impl/src/hdl_bundle.rs b/crates/fayalite-proc-macros-impl/src/hdl_bundle.rs index 79326e2..538c2da 100644 --- a/crates/fayalite-proc-macros-impl/src/hdl_bundle.rs +++ b/crates/fayalite-proc-macros-impl/src/hdl_bundle.rs @@ -1,21 +1,22 @@ // SPDX-License-Identifier: LGPL-3.0-or-later // See Notices.txt for copyright information use crate::{ + Errors, HdlAttr, PairsIterExt, hdl_type_common::{ - common_derives, get_target, ItemOptions, MakeHdlTypeExpr, MaybeParsed, ParsedField, - ParsedFieldsNamed, ParsedGenerics, SplitForImpl, TypesParser, WrappedInConst, + ItemOptions, MakeHdlTypeExpr, MaybeParsed, ParsedField, ParsedFieldsNamed, ParsedGenerics, + SplitForImpl, TypesParser, WrappedInConst, common_derives, get_target, }, - kw, Errors, HdlAttr, PairsIterExt, + kw, }; use proc_macro2::TokenStream; -use quote::{format_ident, quote_spanned, ToTokens}; +use quote::{ToTokens, format_ident, quote_spanned}; use syn::{ - parse_quote, parse_quote_spanned, + AngleBracketedGenericArguments, Attribute, Field, FieldMutability, Fields, FieldsNamed, + GenericParam, Generics, Ident, ItemStruct, Path, Token, Type, Visibility, parse_quote, + parse_quote_spanned, punctuated::{Pair, Punctuated}, spanned::Spanned, token::Brace, - AngleBracketedGenericArguments, Attribute, Field, FieldMutability, Fields, FieldsNamed, - GenericParam, Generics, Ident, ItemStruct, Path, Token, Type, Visibility, }; #[derive(Clone, Debug)] @@ -30,7 +31,9 @@ pub(crate) struct ParsedBundle { pub(crate) field_flips: Vec>>, pub(crate) mask_type_ident: Ident, pub(crate) mask_type_match_variant_ident: Ident, + pub(crate) mask_type_sim_value_ident: Ident, pub(crate) match_variant_ident: Ident, + pub(crate) sim_value_ident: Ident, pub(crate) builder_ident: Ident, pub(crate) mask_type_builder_ident: Ident, } @@ -83,6 +86,7 @@ impl ParsedBundle { custom_bounds, no_static: _, no_runtime_generics: _, + cmp_eq: _, } = options.body; let mut fields = match fields { syn::Fields::Named(fields) => fields, @@ -124,7 +128,9 @@ impl ParsedBundle { field_flips, mask_type_ident: format_ident!("__{}__MaskType", ident), mask_type_match_variant_ident: format_ident!("__{}__MaskType__MatchVariant", ident), + mask_type_sim_value_ident: format_ident!("__{}__MaskType__SimValue", ident), match_variant_ident: format_ident!("__{}__MatchVariant", ident), + sim_value_ident: format_ident!("__{}__SimValue", ident), mask_type_builder_ident: format_ident!("__{}__MaskType__Builder", ident), builder_ident: format_ident!("__{}__Builder", ident), ident, @@ -426,7 +432,9 @@ impl ToTokens for ParsedBundle { field_flips, mask_type_ident, mask_type_match_variant_ident, + mask_type_sim_value_ident, match_variant_ident, + sim_value_ident, builder_ident, mask_type_builder_ident, } = self; @@ -437,6 +445,7 @@ impl ToTokens for ParsedBundle { custom_bounds: _, no_static, no_runtime_generics, + cmp_eq, } = &options.body; let target = get_target(target, ident); let mut item_attrs = attrs.clone(); @@ -521,7 +530,7 @@ impl ToTokens for ParsedBundle { semi_token: None, } .to_tokens(tokens); - let mut mask_type_match_variant_fields = mask_type_fields; + let mut mask_type_match_variant_fields = mask_type_fields.clone(); for Field { ty, .. } in &mut mask_type_match_variant_fields.named { *ty = parse_quote_spanned! {span=> ::fayalite::expr::Expr<#ty> @@ -563,6 +572,58 @@ impl ToTokens for ParsedBundle { semi_token: None, } .to_tokens(tokens); + let mut mask_type_sim_value_fields = mask_type_fields; + for Field { ty, .. } in &mut mask_type_sim_value_fields.named { + *ty = parse_quote_spanned! {span=> + ::fayalite::sim::value::SimValue<#ty> + }; + } + ItemStruct { + attrs: vec![ + parse_quote_spanned! {span=> + #[::fayalite::__std::prelude::v1::derive( + ::fayalite::__std::fmt::Debug, + ::fayalite::__std::clone::Clone, + )] + }, + parse_quote_spanned! {span=> + #[allow(non_camel_case_types, dead_code)] + }, + ], + vis: vis.clone(), + struct_token: *struct_token, + ident: mask_type_sim_value_ident.clone(), + generics: generics.into(), + fields: Fields::Named(mask_type_sim_value_fields), + semi_token: None, + } + .to_tokens(tokens); + let mut sim_value_fields = FieldsNamed::from(fields.clone()); + for Field { ty, .. } in &mut sim_value_fields.named { + *ty = parse_quote_spanned! {span=> + ::fayalite::sim::value::SimValue<#ty> + }; + } + ItemStruct { + attrs: vec![ + parse_quote_spanned! {span=> + #[::fayalite::__std::prelude::v1::derive( + ::fayalite::__std::fmt::Debug, + ::fayalite::__std::clone::Clone, + )] + }, + parse_quote_spanned! {span=> + #[allow(non_camel_case_types, dead_code)] + }, + ], + vis: vis.clone(), + struct_token: *struct_token, + ident: sim_value_ident.clone(), + generics: generics.into(), + fields: Fields::Named(sim_value_fields), + semi_token: None, + } + .to_tokens(tokens); let this_token = Ident::new("__this", span); let fields_token = Ident::new("__fields", span); let self_token = Token![self](span); @@ -613,6 +674,32 @@ impl ToTokens for ParsedBundle { } }, )); + let sim_value_from_opaque_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_opaque(), + } + })); + let sim_value_clone_from_opaque_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_opaque(&mut value.#ident); + } + })); + let sim_value_to_opaque_fields = Vec::from_iter(fields.named().into_iter().map(|field| { + let ident: &Ident = field.ident().as_ref().unwrap(); + quote_spanned! {span=> + v.field(&value.#ident); + } + })); + let to_sim_value_fields = Vec::from_iter(fields.named().into_iter().map(|field| { + let ident: &Ident = field.ident().as_ref().unwrap(); + quote_spanned! {span=> + #ident: ::fayalite::sim::value::SimValue::ty(&self.#ident), + } + })); let fields_len = fields.named().into_iter().len(); quote_spanned! {span=> #[automatically_derived] @@ -621,6 +708,7 @@ impl ToTokens for ParsedBundle { { type BaseType = ::fayalite::bundle::Bundle; type MaskType = #mask_type_ident #type_generics; + type SimValue = #mask_type_sim_value_ident #type_generics; type MatchVariant = #mask_type_match_variant_ident #type_generics; type MatchActiveScope = (); type MatchVariantAndInactiveScope = ::fayalite::ty::MatchVariantWithoutScope< @@ -658,6 +746,35 @@ impl ToTokens for ParsedBundle { fn source_location() -> ::fayalite::source_location::SourceLocation { ::fayalite::source_location::SourceLocation::caller() } + fn sim_value_from_opaque( + &self, + opaque: ::fayalite::ty::OpaqueSimValueSlice<'_>, + ) -> ::SimValue { + #![allow(unused_mut, unused_variables)] + let mut v = ::fayalite::bundle::BundleSimValueFromOpaque::new(*self, opaque); + #mask_type_sim_value_ident { + #(#sim_value_from_opaque_fields)* + } + } + fn sim_value_clone_from_opaque( + &self, + value: &mut ::SimValue, + opaque: ::fayalite::ty::OpaqueSimValueSlice<'_>, + ) { + #![allow(unused_mut, unused_variables)] + let mut v = ::fayalite::bundle::BundleSimValueFromOpaque::new(*self, opaque); + #(#sim_value_clone_from_opaque_fields)* + } + fn sim_value_to_opaque<'__w>( + &self, + value: &::SimValue, + writer: ::fayalite::ty::OpaqueSimValueWriter<'__w>, + ) -> ::fayalite::ty::OpaqueSimValueWritten<'__w> { + #![allow(unused_mut, unused_variables)] + let mut v = ::fayalite::bundle::BundleSimValueToOpaque::new(*self, writer); + #(#sim_value_to_opaque_fields)* + v.finish() + } } #[automatically_derived] impl #impl_generics ::fayalite::bundle::BundleType for #mask_type_ident #type_generics @@ -689,11 +806,57 @@ impl ToTokens for ParsedBundle { } } #[automatically_derived] + impl #impl_generics ::fayalite::sim::value::ToSimValue for #mask_type_sim_value_ident #type_generics + #where_clause + { + type Type = #mask_type_ident #type_generics; + + fn to_sim_value( + &self, + ) -> ::fayalite::sim::value::SimValue< + ::Type, + > { + let ty = #mask_type_ident { + #(#to_sim_value_fields)* + }; + ::fayalite::sim::value::SimValue::from_value(ty, ::fayalite::__std::clone::Clone::clone(self)) + } + fn into_sim_value( + self, + ) -> ::fayalite::sim::value::SimValue< + ::Type, + > { + let ty = #mask_type_ident { + #(#to_sim_value_fields)* + }; + ::fayalite::sim::value::SimValue::from_value(ty, self) + } + } + #[automatically_derived] + impl #impl_generics ::fayalite::sim::value::ToSimValueWithType<#mask_type_ident #type_generics> + for #mask_type_sim_value_ident #type_generics + #where_clause + { + fn to_sim_value_with_type( + &self, + ty: #mask_type_ident #type_generics, + ) -> ::fayalite::sim::value::SimValue<#mask_type_ident #type_generics> { + ::fayalite::sim::value::SimValue::from_value(ty, ::fayalite::__std::clone::Clone::clone(self)) + } + fn into_sim_value_with_type( + self, + ty: #mask_type_ident #type_generics, + ) -> ::fayalite::sim::value::SimValue<#mask_type_ident #type_generics> { + ::fayalite::sim::value::SimValue::from_value(ty, self) + } + } + #[automatically_derived] impl #impl_generics ::fayalite::ty::Type for #target #type_generics #where_clause { type BaseType = ::fayalite::bundle::Bundle; type MaskType = #mask_type_ident #type_generics; + type SimValue = #sim_value_ident #type_generics; type MatchVariant = #match_variant_ident #type_generics; type MatchActiveScope = (); type MatchVariantAndInactiveScope = ::fayalite::ty::MatchVariantWithoutScope< @@ -733,6 +896,35 @@ impl ToTokens for ParsedBundle { fn source_location() -> ::fayalite::source_location::SourceLocation { ::fayalite::source_location::SourceLocation::caller() } + fn sim_value_from_opaque( + &self, + opaque: ::fayalite::ty::OpaqueSimValueSlice<'_>, + ) -> ::SimValue { + #![allow(unused_mut, unused_variables)] + let mut v = ::fayalite::bundle::BundleSimValueFromOpaque::new(*self, opaque); + #sim_value_ident { + #(#sim_value_from_opaque_fields)* + } + } + fn sim_value_clone_from_opaque( + &self, + value: &mut ::SimValue, + opaque: ::fayalite::ty::OpaqueSimValueSlice<'_>, + ) { + #![allow(unused_mut, unused_variables)] + let mut v = ::fayalite::bundle::BundleSimValueFromOpaque::new(*self, opaque); + #(#sim_value_clone_from_opaque_fields)* + } + fn sim_value_to_opaque<'__w>( + &self, + value: &::SimValue, + writer: ::fayalite::ty::OpaqueSimValueWriter<'__w>, + ) -> ::fayalite::ty::OpaqueSimValueWritten<'__w> { + #![allow(unused_mut, unused_variables)] + let mut v = ::fayalite::bundle::BundleSimValueToOpaque::new(*self, writer); + #(#sim_value_to_opaque_fields)* + v.finish() + } } #[automatically_derived] impl #impl_generics ::fayalite::bundle::BundleType for #target #type_generics @@ -763,8 +955,144 @@ impl ToTokens for ParsedBundle { ::fayalite::intern::Interned::into_inner(::fayalite::intern::Intern::intern_sized(__retval)) } } + #[automatically_derived] + impl #impl_generics ::fayalite::sim::value::ToSimValue for #sim_value_ident #type_generics + #where_clause + { + type Type = #target #type_generics; + + fn to_sim_value( + &self, + ) -> ::fayalite::sim::value::SimValue< + ::Type, + > { + let ty = #target { + #(#to_sim_value_fields)* + }; + ::fayalite::sim::value::SimValue::from_value(ty, ::fayalite::__std::clone::Clone::clone(self)) + } + fn into_sim_value( + self, + ) -> ::fayalite::sim::value::SimValue< + ::Type, + > { + let ty = #target { + #(#to_sim_value_fields)* + }; + ::fayalite::sim::value::SimValue::from_value(ty, self) + } + } + #[automatically_derived] + impl #impl_generics ::fayalite::sim::value::ToSimValueWithType<#target #type_generics> + for #sim_value_ident #type_generics + #where_clause + { + fn to_sim_value_with_type( + &self, + ty: #target #type_generics, + ) -> ::fayalite::sim::value::SimValue<#target #type_generics> { + ::fayalite::sim::value::SimValue::from_value(ty, ::fayalite::__std::clone::Clone::clone(self)) + } + fn into_sim_value_with_type( + self, + ty: #target #type_generics, + ) -> ::fayalite::sim::value::SimValue<#target #type_generics> { + ::fayalite::sim::value::SimValue::from_value(ty, self) + } + } } .to_tokens(tokens); + if let Some((cmp_eq,)) = cmp_eq { + let mut expr_where_clause = + Generics::from(generics) + .where_clause + .unwrap_or_else(|| syn::WhereClause { + where_token: Token![where](span), + predicates: Punctuated::new(), + }); + let mut sim_value_where_clause = expr_where_clause.clone(); + let mut fields_sim_value_eq = vec![]; + let mut fields_cmp_eq = vec![]; + let mut fields_cmp_ne = vec![]; + for field in fields.named() { + let field_ident = field.ident(); + let field_ty = field.ty(); + 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 for #target #type_generics + #expr_where_clause + { + fn cmp_eq( + __lhs: ::fayalite::expr::Expr, + __rhs: ::fayalite::expr::Expr, + ) -> ::fayalite::expr::Expr<::fayalite::int::Bool> { + #cmp_eq_body + } + fn cmp_ne( + __lhs: ::fayalite::expr::Expr, + __rhs: ::fayalite::expr::Expr, + ) -> ::fayalite::expr::Expr<::fayalite::int::Bool> { + #cmp_ne_body + } + } + #[automatically_derived] + impl #impl_generics ::fayalite::sim::value::SimValuePartialEq for #target #type_generics + #sim_value_where_clause + { + fn sim_value_eq( + __lhs: &::fayalite::sim::value::SimValue, + __rhs: &::fayalite::sim::value::SimValue, + ) -> bool { + #sim_value_eq_body + } + } + } + .to_tokens(tokens); + } if let (None, MaybeParsed::Parsed(generics)) = (no_static, &self.generics) { let static_generics = generics.clone().for_static_type(); let (static_impl_generics, static_type_generics, static_where_clause) = @@ -800,6 +1128,14 @@ impl ToTokens for ParsedBundle { } })); quote_spanned! {span=> + #[automatically_derived] + impl #static_impl_generics ::fayalite::__std::default::Default for #mask_type_ident #static_type_generics + #static_where_clause + { + fn default() -> Self { + ::TYPE + } + } #[automatically_derived] impl #static_impl_generics ::fayalite::ty::StaticType for #mask_type_ident #static_type_generics #static_where_clause @@ -822,6 +1158,15 @@ impl ToTokens for ParsedBundle { }; } #[automatically_derived] + impl #static_impl_generics ::fayalite::__std::default::Default + for #target #static_type_generics + #static_where_clause + { + fn default() -> Self { + ::TYPE + } + } + #[automatically_derived] impl #static_impl_generics ::fayalite::ty::StaticType for #target #static_type_generics #static_where_clause { diff --git a/crates/fayalite-proc-macros-impl/src/hdl_enum.rs b/crates/fayalite-proc-macros-impl/src/hdl_enum.rs index 1d16177..e5cbe27 100644 --- a/crates/fayalite-proc-macros-impl/src/hdl_enum.rs +++ b/crates/fayalite-proc-macros-impl/src/hdl_enum.rs @@ -1,20 +1,20 @@ // SPDX-License-Identifier: LGPL-3.0-or-later // See Notices.txt for copyright information use crate::{ + Errors, HdlAttr, PairsIterExt, hdl_type_common::{ - common_derives, get_target, ItemOptions, MakeHdlTypeExpr, MaybeParsed, ParsedGenerics, - ParsedType, SplitForImpl, TypesParser, WrappedInConst, + ItemOptions, MakeHdlTypeExpr, MaybeParsed, ParsedGenerics, ParsedType, SplitForImpl, + TypesParser, WrappedInConst, common_derives, get_target, }, - kw, Errors, HdlAttr, PairsIterExt, + kw, }; use proc_macro2::TokenStream; -use quote::{format_ident, quote_spanned, ToTokens}; +use quote::{ToTokens, format_ident, quote_spanned}; use syn::{ - parse_quote_spanned, + Attribute, Field, FieldMutability, Fields, FieldsNamed, FieldsUnnamed, Generics, Ident, + ItemEnum, ItemStruct, Token, Type, Variant, Visibility, parse_quote_spanned, punctuated::{Pair, Punctuated}, token::{Brace, Paren}, - Attribute, Field, FieldMutability, Fields, FieldsNamed, FieldsUnnamed, Generics, Ident, - ItemEnum, ItemStruct, Token, Type, Variant, Visibility, }; crate::options! { @@ -129,6 +129,9 @@ pub(crate) struct ParsedEnum { pub(crate) brace_token: Brace, pub(crate) variants: Punctuated, pub(crate) match_variant_ident: Ident, + pub(crate) sim_value_ident: Ident, + pub(crate) sim_builder_ident: Ident, + pub(crate) sim_builder_ty_field_ident: Ident, } impl ParsedEnum { @@ -155,7 +158,11 @@ impl ParsedEnum { custom_bounds, no_static: _, no_runtime_generics: _, + cmp_eq, } = options.body; + if let Some((cmp_eq,)) = cmp_eq { + errors.error(cmp_eq, "#[hdl(cmp_eq)] is not yet implemented for enums"); + } attrs.retain(|attr| { if attr.path().is_ident("repr") { errors.error(attr, "#[repr] is not supported on #[hdl] enums"); @@ -186,6 +193,9 @@ impl ParsedEnum { brace_token, variants, match_variant_ident: format_ident!("__{}__MatchVariant", ident), + sim_value_ident: format_ident!("__{}__SimValue", ident), + sim_builder_ident: format_ident!("__{}__SimBuilder", ident), + sim_builder_ty_field_ident: format_ident!("__ty", span = ident.span()), ident, }) } @@ -203,6 +213,9 @@ impl ToTokens for ParsedEnum { brace_token, variants, match_variant_ident, + sim_value_ident, + sim_builder_ident, + sim_builder_ty_field_ident, } = self; let span = ident.span(); let ItemOptions { @@ -211,6 +224,7 @@ impl ToTokens for ParsedEnum { custom_bounds: _, no_static, no_runtime_generics, + cmp_eq: _, // TODO: implement cmp_eq for enums } = &options.body; let target = get_target(target, ident); let mut struct_attrs = attrs.clone(); @@ -404,6 +418,133 @@ impl ToTokens for ParsedEnum { )), } .to_tokens(tokens); + let mut struct_attrs = attrs.clone(); + struct_attrs.push(parse_quote_spanned! {span=> + #[allow(dead_code, non_camel_case_types)] + }); + ItemStruct { + attrs: struct_attrs, + vis: vis.clone(), + struct_token: Token![struct](enum_token.span), + ident: sim_builder_ident.clone(), + generics: generics.into(), + fields: FieldsNamed { + brace_token: *brace_token, + named: Punctuated::from_iter([Field { + attrs: vec![], + vis: Visibility::Inherited, + mutability: FieldMutability::None, + ident: Some(sim_builder_ty_field_ident.clone()), + colon_token: Some(Token![:](span)), + ty: parse_quote_spanned! {span=> + #target #type_generics + }, + }]), + } + .into(), + semi_token: None, + } + .to_tokens(tokens); + let mut enum_attrs = attrs.clone(); + enum_attrs.push(parse_quote_spanned! {span=> + #[::fayalite::__std::prelude::v1::derive( + ::fayalite::__std::fmt::Debug, + ::fayalite::__std::clone::Clone, + )] + }); + enum_attrs.push(parse_quote_spanned! {span=> + #[allow(dead_code, non_camel_case_types)] + }); + let sim_value_has_unknown_variant = !variants.len().is_power_of_two(); + let sim_value_unknown_variant_name = sim_value_has_unknown_variant.then(|| { + let mut name = String::new(); + let unknown = "Unknown"; + loop { + let orig_len = name.len(); + name.push_str(unknown); + if variants.iter().all(|v| v.ident != name) { + break Ident::new(&name, span); + } + name.truncate(orig_len); + name.push('_'); + } + }); + let sim_value_unknown_variant = + sim_value_unknown_variant_name + .as_ref() + .map(|unknown_variant_name| { + Pair::End(parse_quote_spanned! {span=> + #unknown_variant_name(::fayalite::enum_::UnknownVariantSimValue) + }) + }); + ItemEnum { + attrs: enum_attrs, + vis: vis.clone(), + enum_token: *enum_token, + ident: sim_value_ident.clone(), + generics: generics.into(), + brace_token: *brace_token, + variants: Punctuated::from_iter( + variants + .pairs() + .map_pair_value_ref( + |ParsedVariant { + attrs, + options: _, + ident, + field, + }| Variant { + attrs: attrs.clone(), + ident: ident.clone(), + fields: match field { + Some(ParsedVariantField { + paren_token, + attrs, + options: _, + ty, + comma_token, + }) => Fields::Unnamed(FieldsUnnamed { + paren_token: *paren_token, + unnamed: Punctuated::from_iter([ + Pair::new( + Field { + attrs: attrs.clone(), + vis: Visibility::Inherited, + mutability: FieldMutability::None, + ident: None, + colon_token: None, + ty: parse_quote_spanned! {span=> + ::fayalite::sim::value::SimValue<#ty> + }, + }, + Some(comma_token.unwrap_or(Token![,](ident.span()))), + ), + Pair::new( + Field { + attrs: vec![], + vis: Visibility::Inherited, + mutability: FieldMutability::None, + ident: None, + colon_token: None, + ty: parse_quote_spanned! {span=> + ::fayalite::enum_::EnumPaddingSimValue + }, + }, + None, + ), + ]), + }), + None => Fields::Unnamed(parse_quote_spanned! {span=> + (::fayalite::enum_::EnumPaddingSimValue) + }), + }, + discriminant: None, + }, + ) + .chain(sim_value_unknown_variant), + ), + } + .to_tokens(tokens); let self_token = Token![self](span); for (index, ParsedVariant { ident, field, .. }) in variants.iter().enumerate() { if let Some(ParsedVariantField { ty, .. }) = field { @@ -430,6 +571,25 @@ impl ToTokens for ParsedEnum { ) } } + #[automatically_derived] + impl #impl_generics #sim_builder_ident #type_generics + #where_clause + { + #[allow(non_snake_case, dead_code)] + #vis fn #ident<__V: ::fayalite::sim::value::ToSimValueWithType<#ty>>( + #self_token, + v: __V, + ) -> ::fayalite::sim::value::SimValue<#target #type_generics> { + let v = ::fayalite::sim::value::ToSimValueWithType::into_sim_value_with_type( + v, + #self_token.#sim_builder_ty_field_ident.#ident, + ); + ::fayalite::sim::value::SimValue::from_value( + #self_token.#sim_builder_ty_field_ident, + #sim_value_ident::#ident(v, ::fayalite::enum_::EnumPaddingSimValue::new()), + ) + } + } } } else { quote_spanned! {span=> @@ -448,6 +608,18 @@ impl ToTokens for ParsedEnum { ) } } + #[automatically_derived] + impl #impl_generics #sim_builder_ident #type_generics + #where_clause + { + #[allow(non_snake_case, dead_code)] + #vis fn #ident(#self_token) -> ::fayalite::sim::value::SimValue<#target #type_generics> { + ::fayalite::sim::value::SimValue::from_value( + #self_token.#sim_builder_ty_field_ident, + #sim_value_ident::#ident(::fayalite::enum_::EnumPaddingSimValue::new()), + ) + } + } } } .to_tokens(tokens); @@ -529,6 +701,142 @@ impl ToTokens for ParsedEnum { } }, )); + let sim_value_from_opaque_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_opaque()), + } + } else { + quote_spanned! {span=> + _ => ::fayalite::__std::unreachable!(), + } + }; + let sim_value_from_opaque_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_opaque(); + #sim_value_ident::#ident(field, padding) + } + } + } else { + quote_spanned! {span=> + #index => #sim_value_ident::#ident( + v.variant_no_field_from_opaque(), + ), + } + } + }, + ) + .chain([sim_value_from_opaque_unknown_match_arm]), + ); + let sim_value_clone_from_opaque_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_opaque(value); + } else { + *value = #sim_value_ident::#sim_value_unknown_variant_name( + v.unknown_variant_from_opaque(), + ); + }, + } + } else { + quote_spanned! {span=> + _ => ::fayalite::__std::unreachable!(), + } + }; + let sim_value_clone_from_opaque_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_opaque(field, padding); + } else { + let (field, padding) = v.variant_with_field_from_opaque(); + *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_opaque(padding); + } else { + *value = #sim_value_ident::#ident( + v.variant_no_field_from_opaque(), + ); + }, + } + } + }, + ) + .chain([sim_value_clone_from_opaque_unknown_match_arm]), + ); + let sim_value_to_opaque_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_opaque(#index, field, padding) + } + } + } else { + quote_spanned! {span=> + #sim_value_ident::#ident(padding) => { + v.variant_no_field_to_opaque(#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_opaque(value) + } + } + }, + )), + ); let variants_len = variants.len(); quote_spanned! {span=> #[automatically_derived] @@ -537,6 +845,7 @@ impl ToTokens for ParsedEnum { { type BaseType = ::fayalite::enum_::Enum; type MaskType = ::fayalite::int::Bool; + type SimValue = #sim_value_ident #type_generics; type MatchVariant = #match_variant_ident #type_generics; type MatchActiveScope = ::fayalite::module::Scope; type MatchVariantAndInactiveScope = ::fayalite::enum_::EnumMatchVariantAndInactiveScope; @@ -569,11 +878,41 @@ impl ToTokens for ParsedEnum { fn source_location() -> ::fayalite::source_location::SourceLocation { ::fayalite::source_location::SourceLocation::caller() } + fn sim_value_from_opaque( + &self, + opaque: ::fayalite::ty::OpaqueSimValueSlice<'_>, + ) -> ::SimValue { + let v = ::fayalite::enum_::EnumSimValueFromOpaque::new(*self, opaque); + match v.discriminant() { + #(#sim_value_from_opaque_match_arms)* + } + } + fn sim_value_clone_from_opaque( + &self, + value: &mut ::SimValue, + opaque: ::fayalite::ty::OpaqueSimValueSlice<'_>, + ) { + let v = ::fayalite::enum_::EnumSimValueFromOpaque::new(*self, opaque); + match v.discriminant() { + #(#sim_value_clone_from_opaque_match_arms)* + } + } + fn sim_value_to_opaque<'__w>( + &self, + value: &::SimValue, + writer: ::fayalite::ty::OpaqueSimValueWriter<'__w>, + ) -> ::fayalite::ty::OpaqueSimValueWritten<'__w> { + let v = ::fayalite::enum_::EnumSimValueToOpaque::new(*self, writer); + match value { + #(#sim_value_to_opaque_match_arms)* + } + } } #[automatically_derived] impl #impl_generics ::fayalite::enum_::EnumType for #target #type_generics #where_clause { + type SimBuilder = #sim_builder_ident #type_generics; fn match_activate_scope( v: ::MatchVariantAndInactiveScope, ) -> (::MatchVariant, ::MatchActiveScope) { @@ -592,6 +931,33 @@ impl ToTokens for ParsedEnum { ][..]) } } + #[automatically_derived] + impl #impl_generics ::fayalite::sim::value::ToSimValueWithType<#target #type_generics> + for #sim_value_ident #type_generics + #where_clause + { + fn to_sim_value_with_type( + &self, + ty: #target #type_generics, + ) -> ::fayalite::sim::value::SimValue<#target #type_generics> { + ::fayalite::sim::value::SimValue::from_value(ty, ::fayalite::__std::clone::Clone::clone(self)) + } + fn into_sim_value_with_type( + self, + ty: #target #type_generics, + ) -> ::fayalite::sim::value::SimValue<#target #type_generics> { + ::fayalite::sim::value::SimValue::from_value(ty, self) + } + } + #[automatically_derived] + impl #impl_generics ::fayalite::__std::convert::From<#target #type_generics> + for #sim_builder_ident #type_generics + #where_clause + { + fn from(#sim_builder_ty_field_ident: #target #type_generics) -> Self { + Self { #sim_builder_ty_field_ident } + } + } } .to_tokens(tokens); if let (None, MaybeParsed::Parsed(generics)) = (no_static, &self.generics) { @@ -629,6 +995,15 @@ impl ToTokens for ParsedEnum { } })); quote_spanned! {span=> + #[automatically_derived] + impl #static_impl_generics ::fayalite::__std::default::Default + for #target #static_type_generics + #static_where_clause + { + fn default() -> Self { + ::TYPE + } + } #[automatically_derived] impl #static_impl_generics ::fayalite::ty::StaticType for #target #static_type_generics @@ -647,6 +1022,34 @@ impl ToTokens for ParsedEnum { const MASK_TYPE_PROPERTIES: ::fayalite::ty::TypeProperties = <::fayalite::int::Bool as ::fayalite::ty::StaticType>::TYPE_PROPERTIES; } + #[automatically_derived] + impl #static_impl_generics ::fayalite::sim::value::ToSimValue + for #sim_value_ident #static_type_generics + #static_where_clause + { + type Type = #target #static_type_generics; + + fn to_sim_value( + &self, + ) -> ::fayalite::sim::value::SimValue< + ::Type, + > { + ::fayalite::sim::value::SimValue::from_value( + ::fayalite::ty::StaticType::TYPE, + ::fayalite::__std::clone::Clone::clone(self), + ) + } + fn into_sim_value( + self, + ) -> ::fayalite::sim::value::SimValue< + ::Type, + > { + ::fayalite::sim::value::SimValue::from_value( + ::fayalite::ty::StaticType::TYPE, + self, + ) + } + } } .to_tokens(tokens); } diff --git a/crates/fayalite-proc-macros-impl/src/hdl_type_alias.rs b/crates/fayalite-proc-macros-impl/src/hdl_type_alias.rs index e5d5f7b..d4a035b 100644 --- a/crates/fayalite-proc-macros-impl/src/hdl_type_alias.rs +++ b/crates/fayalite-proc-macros-impl/src/hdl_type_alias.rs @@ -1,15 +1,16 @@ // SPDX-License-Identifier: LGPL-3.0-or-later // See Notices.txt for copyright information use crate::{ + Errors, HdlAttr, hdl_type_common::{ - get_target, ItemOptions, MakeHdlTypeExpr, MaybeParsed, ParsedGenerics, ParsedType, - TypesParser, + ItemOptions, MakeHdlTypeExpr, MaybeParsed, ParsedGenerics, ParsedType, TypesParser, + get_target, }, - kw, Errors, HdlAttr, + kw, }; use proc_macro2::TokenStream; use quote::ToTokens; -use syn::{parse_quote_spanned, Attribute, Generics, Ident, ItemType, Token, Type, Visibility}; +use syn::{Attribute, Generics, Ident, ItemType, Token, Type, Visibility, parse_quote_spanned}; #[derive(Clone, Debug)] pub(crate) struct ParsedTypeAlias { @@ -49,10 +50,14 @@ impl ParsedTypeAlias { custom_bounds, no_static, no_runtime_generics: _, + cmp_eq, } = options.body; if let Some((no_static,)) = no_static { errors.error(no_static, "no_static is not valid on type aliases"); } + if let Some((cmp_eq,)) = cmp_eq { + errors.error(cmp_eq, "cmp_eq is not valid on type aliases"); + } let generics = if custom_bounds.is_some() { MaybeParsed::Unrecognized(generics) } else if let Some(generics) = errors.ok(ParsedGenerics::parse(&mut generics)) { @@ -95,6 +100,7 @@ impl ToTokens for ParsedTypeAlias { custom_bounds: _, no_static: _, no_runtime_generics, + cmp_eq: _, } = &options.body; let target = get_target(target, ident); let mut type_attrs = attrs.clone(); diff --git a/crates/fayalite-proc-macros-impl/src/hdl_type_common.rs b/crates/fayalite-proc-macros-impl/src/hdl_type_common.rs index 6193dc3..1206f11 100644 --- a/crates/fayalite-proc-macros-impl/src/hdl_type_common.rs +++ b/crates/fayalite-proc-macros-impl/src/hdl_type_common.rs @@ -1,21 +1,21 @@ // SPDX-License-Identifier: LGPL-3.0-or-later // See Notices.txt for copyright information -use crate::{fold::impl_fold, kw, Errors, HdlAttr, PairsIterExt}; +use crate::{Errors, HdlAttr, PairsIterExt, fold::impl_fold, kw}; use proc_macro2::{Span, TokenStream}; -use quote::{format_ident, quote_spanned, ToTokens}; +use quote::{ToTokens, format_ident, quote_spanned}; use std::{collections::HashMap, fmt, mem}; use syn::{ - parse::{Parse, ParseStream}, - parse_quote, parse_quote_spanned, - punctuated::{Pair, Punctuated}, - spanned::Spanned, - token::{Brace, Bracket, Paren}, AngleBracketedGenericArguments, Attribute, Block, ConstParam, Expr, ExprBlock, ExprGroup, ExprIndex, ExprParen, ExprPath, ExprTuple, Field, FieldMutability, Fields, FieldsNamed, FieldsUnnamed, GenericArgument, GenericParam, Generics, Ident, ImplGenerics, Index, ItemStruct, Path, PathArguments, PathSegment, PredicateType, QSelf, Stmt, Token, Turbofish, Type, TypeGenerics, TypeGroup, TypeParam, TypeParen, TypePath, TypeTuple, Visibility, WhereClause, WherePredicate, + parse::{Parse, ParseStream}, + parse_quote, parse_quote_spanned, + punctuated::{Pair, Punctuated}, + spanned::Spanned, + token::{Brace, Bracket, Paren}, }; crate::options! { @@ -26,6 +26,7 @@ crate::options! { CustomBounds(custom_bounds), NoStatic(no_static), NoRuntimeGenerics(no_runtime_generics), + CmpEq(cmp_eq), } } @@ -298,7 +299,7 @@ impl ParseTypes for ParsedExpr { return Ok(ParsedExpr::Delimited(ParsedExprDelimited { delim: ExprDelimiter::Group(*group_token), expr: parser.parse(expr)?, - })) + })); } Expr::Paren(ExprParen { attrs, @@ -308,7 +309,7 @@ impl ParseTypes for ParsedExpr { return Ok(ParsedExpr::Delimited(ParsedExprDelimited { delim: ExprDelimiter::Paren(*paren_token), expr: parser.parse(expr)?, - })) + })); } Expr::Path(ExprPath { attrs, @@ -1901,8 +1902,8 @@ pub(crate) mod known_items { use proc_macro2::{Ident, Span, TokenStream}; use quote::ToTokens; use syn::{ - parse::{Parse, ParseStream}, Path, PathArguments, PathSegment, Token, + parse::{Parse, ParseStream}, }; macro_rules! impl_known_item_body { @@ -2069,11 +2070,16 @@ macro_rules! impl_bounds { $( $Variant:ident, )* + $( + #[unknown] + $Unknown:ident, + )? } ) => { #[derive(Clone, Debug)] $vis enum $enum_type { $($Variant(known_items::$Variant),)* + $($Unknown(syn::TypeParamBound),)? } $(impl From for $enum_type { @@ -2086,28 +2092,54 @@ macro_rules! impl_bounds { fn to_tokens(&self, tokens: &mut TokenStream) { match self { $(Self::$Variant(v) => v.to_tokens(tokens),)* + $(Self::$Unknown(v) => v.to_tokens(tokens),)? } } } impl $enum_type { $vis fn parse_path(path: Path) -> Result { + #![allow(unreachable_code)] $(let path = match known_items::$Variant::parse_path(path) { Ok(v) => return Ok(Self::$Variant(v)), Err(path) => path, };)* + $(return Ok(Self::$Unknown(syn::TraitBound { + paren_token: None, + modifier: syn::TraitBoundModifier::None, + lifetimes: None, + path, + }.into()));)? Err(path) } + $vis fn parse_type_param_bound(mut type_param_bound: syn::TypeParamBound) -> Result { + #![allow(unreachable_code)] + if let syn::TypeParamBound::Trait(mut trait_bound) = type_param_bound { + if let syn::TraitBound { + paren_token: _, + modifier: syn::TraitBoundModifier::None, + lifetimes: None, + path: _, + } = trait_bound { + match Self::parse_path(trait_bound.path) { + Ok(retval) => return Ok(retval), + Err(path) => trait_bound.path = path, + } + } + type_param_bound = trait_bound.into(); + } + $(return Ok(Self::$Unknown(type_param_bound));)? + Err(type_param_bound) + } } impl Parse for $enum_type { fn parse(input: ParseStream) -> syn::Result { - Self::parse_path(Path::parse_mod_style(input)?).map_err(|path| { - syn::Error::new_spanned( - path, + Self::parse_type_param_bound(input.parse()?) + .map_err(|type_param_bound| syn::Error::new_spanned( + type_param_bound, format_args!("expected one of: {}", [$(stringify!($Variant)),*].join(", ")), - ) - }) + )) } } @@ -2115,6 +2147,7 @@ macro_rules! impl_bounds { #[allow(non_snake_case)] $vis struct $struct_type { $($vis $Variant: Option,)* + $($vis $Unknown: Vec,)? } impl ToTokens for $struct_type { @@ -2126,42 +2159,63 @@ macro_rules! impl_bounds { separator = Some(::default()); v.to_tokens(tokens); })* + $(for v in &self.$Unknown { + separator.to_tokens(tokens); + separator = Some(::default()); + v.to_tokens(tokens); + })* } } const _: () = { #[derive(Clone, Debug)] - $vis struct Iter($vis $struct_type); + #[allow(non_snake_case)] + $vis struct Iter { + $($Variant: Option,)* + $($Unknown: std::vec::IntoIter,)? + } impl IntoIterator for $struct_type { type Item = $enum_type; type IntoIter = Iter; fn into_iter(self) -> Self::IntoIter { - Iter(self) + Iter { + $($Variant: self.$Variant,)* + $($Unknown: self.$Unknown.into_iter(),)? + } } } impl Iterator for Iter { type Item = $enum_type; - fn next(&mut self) -> Option { $( - if let Some(value) = self.0.$Variant.take() { + if let Some(value) = self.$Variant.take() { return Some($enum_type::$Variant(value)); } )* + $( + if let Some(value) = self.$Unknown.next() { + return Some($enum_type::$Unknown(value)); + } + )? None } #[allow(unused_mut, unused_variables)] fn fold B>(mut self, mut init: B, mut f: F) -> B { $( - if let Some(value) = self.0.$Variant.take() { + if let Some(value) = self.$Variant.take() { init = f(init, $enum_type::$Variant(value)); } )* + $( + if let Some(value) = self.$Unknown.next() { + init = f(init, $enum_type::$Unknown(value)); + } + )? init } } @@ -2173,6 +2227,9 @@ macro_rules! impl_bounds { $($enum_type::$Variant(v) => { self.$Variant = Some(v); })* + $($enum_type::$Unknown(v) => { + self.$Unknown.push(v); + })? }); } } @@ -2191,6 +2248,7 @@ macro_rules! impl_bounds { $(if let Some(v) = v.$Variant { self.$Variant = Some(v); })* + $(self.$Unknown.extend(v.$Unknown);)* }); } } @@ -2244,6 +2302,8 @@ impl_bounds! { Size, StaticType, Type, + #[unknown] + Unknown, } } @@ -2257,6 +2317,8 @@ impl_bounds! { ResetType, StaticType, Type, + #[unknown] + Unknown, } } @@ -2270,6 +2332,7 @@ impl From for ParsedBound { ParsedTypeBound::ResetType(v) => ParsedBound::ResetType(v), ParsedTypeBound::StaticType(v) => ParsedBound::StaticType(v), ParsedTypeBound::Type(v) => ParsedBound::Type(v), + ParsedTypeBound::Unknown(v) => ParsedBound::Unknown(v), } } } @@ -2284,6 +2347,7 @@ impl From for ParsedBounds { ResetType, StaticType, Type, + Unknown, } = value; Self { BoolOrIntType, @@ -2295,6 +2359,7 @@ impl From for ParsedBounds { Size: None, StaticType, Type, + Unknown, } } } @@ -2330,6 +2395,7 @@ impl ParsedTypeBound { ParsedTypeBound::Type(known_items::Type(span)), ]), Self::Type(v) => ParsedTypeBounds::from_iter([ParsedTypeBound::from(v)]), + Self::Unknown(v) => ParsedTypeBounds::from_iter([ParsedTypeBound::Unknown(v)]), } } } @@ -2364,6 +2430,7 @@ impl From for ParsedBounds { Size, StaticType: None, Type: None, + Unknown: vec![], } } } @@ -2391,6 +2458,7 @@ impl ParsedBounds { fn categorize(self, errors: &mut Errors, span: Span) -> ParsedBoundsCategory { let mut type_bounds = None; let mut size_type_bounds = None; + let mut unknown_bounds = vec![]; self.into_iter().for_each(|bound| match bound.categorize() { ParsedBoundCategory::Type(bound) => { type_bounds @@ -2402,15 +2470,37 @@ impl ParsedBounds { .get_or_insert_with(ParsedSizeTypeBounds::default) .extend([bound]); } + ParsedBoundCategory::Unknown(bound) => unknown_bounds.push(bound), }); - match (type_bounds, size_type_bounds) { - (None, None) => ParsedBoundsCategory::Type(ParsedTypeBounds { + match (type_bounds, size_type_bounds, unknown_bounds.is_empty()) { + (None, None, true) => ParsedBoundsCategory::Type(ParsedTypeBounds { Type: Some(known_items::Type(span)), ..Default::default() }), - (None, Some(bounds)) => ParsedBoundsCategory::SizeType(bounds), - (Some(bounds), None) => ParsedBoundsCategory::Type(bounds), - (Some(type_bounds), Some(size_type_bounds)) => { + (None, None, false) => { + errors.error( + unknown_bounds.remove(0), + "unknown bounds: must use at least one known bound (such as `Type`) with any unknown bounds", + ); + ParsedBoundsCategory::Type(ParsedTypeBounds { + Unknown: unknown_bounds, + ..Default::default() + }) + } + (None, Some(bounds), true) => ParsedBoundsCategory::SizeType(bounds), + (None, Some(bounds), false) => { + // TODO: implement + errors.error( + unknown_bounds.remove(0), + "unknown bounds with `Size` bounds are not implemented", + ); + ParsedBoundsCategory::SizeType(bounds) + } + (Some(bounds), None, _) => ParsedBoundsCategory::Type(ParsedTypeBounds { + Unknown: unknown_bounds, + ..bounds + }), + (Some(type_bounds), Some(size_type_bounds), _) => { errors.error( size_type_bounds .Size @@ -2427,6 +2517,7 @@ impl ParsedBounds { pub(crate) enum ParsedBoundCategory { Type(ParsedTypeBound), SizeType(ParsedSizeTypeBound), + Unknown(syn::TypeParamBound), } impl ParsedBound { @@ -2441,12 +2532,14 @@ impl ParsedBound { Self::Size(v) => ParsedBoundCategory::SizeType(ParsedSizeTypeBound::Size(v)), Self::StaticType(v) => ParsedBoundCategory::Type(ParsedTypeBound::StaticType(v)), Self::Type(v) => ParsedBoundCategory::Type(ParsedTypeBound::Type(v)), + Self::Unknown(v) => ParsedBoundCategory::Unknown(v), } } fn implied_bounds(self) -> ParsedBounds { match self.categorize() { ParsedBoundCategory::Type(v) => v.implied_bounds().into(), ParsedBoundCategory::SizeType(v) => v.implied_bounds().into(), + ParsedBoundCategory::Unknown(v) => ParsedBounds::from_iter([ParsedBound::Unknown(v)]), } } } @@ -3325,7 +3418,7 @@ impl ParsedGenerics { | ParsedTypeBound::EnumType(_) | ParsedTypeBound::IntType(_) | ParsedTypeBound::ResetType(_) => { - errors.error(bound, "bound on mask type not implemented"); + errors.error(bound, "bounds on mask types are not implemented"); } ParsedTypeBound::StaticType(bound) => { if bounds.StaticType.is_none() { @@ -3337,6 +3430,12 @@ impl ParsedGenerics { } } ParsedTypeBound::Type(_) => {} + ParsedTypeBound::Unknown(_) => { + errors.error( + bound, + "unknown bounds on mask types are not implemented", + ); + } } } bounds.add_implied_bounds(); @@ -3662,7 +3761,10 @@ pub(crate) trait AsTurbofish { } impl AsTurbofish for TypeGenerics<'_> { - type Turbofish<'a> = Turbofish<'a> where Self: 'a; + type Turbofish<'a> + = Turbofish<'a> + where + Self: 'a; fn as_turbofish(&self) -> Self::Turbofish<'_> { TypeGenerics::as_turbofish(self) @@ -3670,7 +3772,8 @@ impl AsTurbofish for TypeGenerics<'_> { } impl AsTurbofish for ParsedGenericsTypeGenerics<'_> { - type Turbofish<'a> = ParsedGenericsTurbofish<'a> + type Turbofish<'a> + = ParsedGenericsTurbofish<'a> where Self: 'a; @@ -3721,15 +3824,18 @@ impl SplitForImpl for Generics { } impl SplitForImpl for ParsedGenerics { - type ImplGenerics<'a> = ParsedGenericsImplGenerics<'a> + type ImplGenerics<'a> + = ParsedGenericsImplGenerics<'a> where Self: 'a; - type TypeGenerics<'a> = ParsedGenericsTypeGenerics<'a> + type TypeGenerics<'a> + = ParsedGenericsTypeGenerics<'a> where Self: 'a; - type WhereClause<'a> = ParsedGenericsWhereClause<'a> + type WhereClause<'a> + = ParsedGenericsWhereClause<'a> where Self: 'a; @@ -3946,7 +4052,8 @@ impl ToTokens for MaybeParsed { } impl AsTurbofish for MaybeParsed { - type Turbofish<'a> = MaybeParsed, U::Turbofish<'a>> + type Turbofish<'a> + = MaybeParsed, U::Turbofish<'a>> where Self: 'a; @@ -3959,13 +4066,16 @@ impl AsTurbofish for MaybeParsed { } impl SplitForImpl for MaybeParsed { - type ImplGenerics<'a> = MaybeParsed, U::ImplGenerics<'a>> + type ImplGenerics<'a> + = MaybeParsed, U::ImplGenerics<'a>> where Self: 'a; - type TypeGenerics<'a> = MaybeParsed, U::TypeGenerics<'a>> + type TypeGenerics<'a> + = MaybeParsed, U::TypeGenerics<'a>> where Self: 'a; - type WhereClause<'a> = MaybeParsed, U::WhereClause<'a>> + type WhereClause<'a> + = MaybeParsed, U::WhereClause<'a>> where Self: 'a; diff --git a/crates/fayalite-proc-macros-impl/src/lib.rs b/crates/fayalite-proc-macros-impl/src/lib.rs index 6ba177b..def91eb 100644 --- a/crates/fayalite-proc-macros-impl/src/lib.rs +++ b/crates/fayalite-proc-macros-impl/src/lib.rs @@ -2,13 +2,13 @@ // See Notices.txt for copyright information #![cfg_attr(test, recursion_limit = "512")] use proc_macro2::{Span, TokenStream}; -use quote::{quote, ToTokens}; +use quote::{ToTokens, quote}; use std::{ - collections::{hash_map::Entry, HashMap}, + collections::{HashMap, hash_map::Entry}, io::{ErrorKind, Write}, }; use syn::{ - bracketed, + AttrStyle, Attribute, Error, Ident, Item, ItemFn, LitBool, LitStr, Meta, Token, bracketed, ext::IdentExt, parenthesized, parse::{Parse, ParseStream, Parser}, @@ -16,7 +16,6 @@ use syn::{ punctuated::{Pair, Punctuated}, spanned::Spanned, token::{Bracket, Paren}, - AttrStyle, Attribute, Error, Ident, Item, ItemFn, LitBool, LitStr, Meta, Token, }; mod fold; @@ -72,13 +71,14 @@ mod kw { custom_keyword!(cfg); custom_keyword!(cfg_attr); custom_keyword!(clock_domain); + custom_keyword!(cmp_eq); custom_keyword!(connect_inexact); custom_keyword!(custom_bounds); custom_keyword!(flip); custom_keyword!(hdl); custom_keyword!(hdl_module); - custom_keyword!(input); custom_keyword!(incomplete_wire); + custom_keyword!(input); custom_keyword!(instance); custom_keyword!(m); custom_keyword!(memory); @@ -92,6 +92,7 @@ mod kw { custom_keyword!(output); custom_keyword!(reg_builder); custom_keyword!(reset); + custom_keyword!(sim); custom_keyword!(skip); custom_keyword!(target); custom_keyword!(wire); diff --git a/crates/fayalite-proc-macros-impl/src/module.rs b/crates/fayalite-proc-macros-impl/src/module.rs index 0852f58..c7caa16 100644 --- a/crates/fayalite-proc-macros-impl/src/module.rs +++ b/crates/fayalite-proc-macros-impl/src/module.rs @@ -1,19 +1,20 @@ // SPDX-License-Identifier: LGPL-3.0-or-later // See Notices.txt for copyright information use crate::{ + Errors, HdlAttr, PairsIterExt, hdl_type_common::{ParsedGenerics, SplitForImpl}, kw, module::transform_body::{HdlLet, HdlLetKindIO}, - options, Errors, HdlAttr, PairsIterExt, + options, }; use proc_macro2::TokenStream; -use quote::{format_ident, quote, quote_spanned, ToTokens}; +use quote::{ToTokens, format_ident, quote, quote_spanned}; use std::collections::HashSet; use syn::{ - parse_quote, - visit::{visit_pat, Visit}, Attribute, Block, ConstParam, Error, FnArg, GenericParam, Generics, Ident, ItemFn, ItemStruct, LifetimeParam, ReturnType, Signature, TypeParam, Visibility, WhereClause, WherePredicate, + parse_quote, + visit::{Visit, visit_pat}, }; mod transform_body; @@ -377,7 +378,7 @@ impl ModuleFn { module_kind, vis, sig, - block, + mut block, struct_generics, the_struct, } = match self.0 { @@ -439,6 +440,12 @@ impl ModuleFn { body_sig .inputs .insert(0, parse_quote! { m: &::fayalite::module::ModuleBuilder }); + block.stmts.insert( + 0, + parse_quote! { + let _ = m; + }, + ); let body_fn = ItemFn { attrs: vec![], vis: Visibility::Inherited, diff --git a/crates/fayalite-proc-macros-impl/src/module/transform_body.rs b/crates/fayalite-proc-macros-impl/src/module/transform_body.rs index 6e99e87..6859f69 100644 --- a/crates/fayalite-proc-macros-impl/src/module/transform_body.rs +++ b/crates/fayalite-proc-macros-impl/src/module/transform_body.rs @@ -1,32 +1,40 @@ // SPDX-License-Identifier: LGPL-3.0-or-later // See Notices.txt for copyright information use crate::{ - fold::{impl_fold, DoFold}, + Errors, HdlAttr, + fold::{DoFold, impl_fold}, hdl_type_common::{ - known_items, ParseFailed, ParseTypes, ParsedGenerics, ParsedType, TypesParser, + ParseFailed, ParseTypes, ParsedGenerics, ParsedType, TypesParser, known_items, }, is_hdl_attr, kw, - module::{check_name_conflicts_with_module_builder, ModuleIO, ModuleIOKind, ModuleKind}, - options, Errors, HdlAttr, + module::{ModuleIO, ModuleIOKind, ModuleKind, check_name_conflicts_with_module_builder}, + options, }; use num_bigint::BigInt; use proc_macro2::{Span, TokenStream}; -use quote::{quote, quote_spanned, ToTokens}; +use quote::{ToTokens, quote, quote_spanned}; use std::{borrow::Borrow, convert::Infallible}; use syn::{ - fold::{fold_expr, fold_expr_lit, fold_expr_unary, fold_local, fold_stmt, Fold}, + Attribute, Block, Error, Expr, ExprIf, ExprLet, ExprLit, ExprRepeat, ExprUnary, + GenericArgument, Ident, Item, Lit, LitStr, Local, LocalInit, Pat, Token, Type, UnOp, + fold::{Fold, fold_expr, fold_expr_lit, fold_expr_unary, fold_local, fold_stmt}, parenthesized, - parse::{Nothing, Parse, ParseStream}, + parse::{Parse, ParseStream}, parse_quote, parse_quote_spanned, spanned::Spanned, token::Paren, - Attribute, Block, Error, Expr, ExprIf, ExprLet, ExprLit, ExprRepeat, ExprUnary, - GenericArgument, Ident, Item, Lit, LitStr, Local, LocalInit, Pat, Token, Type, UnOp, }; mod expand_aggregate_literals; mod expand_match; +options! { + #[options = ExprOptions] + pub(crate) enum ExprOption { + Sim(sim), + } +} + options! { pub(crate) enum LetFnKind { Input(input), @@ -952,7 +960,7 @@ with_debug_clone_and_fold! { #[allow(dead_code)] pub(crate) struct HdlLet { pub(crate) attrs: Vec, - pub(crate) hdl_attr: HdlAttr, + pub(crate) hdl_attr: HdlAttr, pub(crate) let_token: Token![let], pub(crate) mut_token: Option, pub(crate) name: Ident, @@ -1109,7 +1117,7 @@ fn parse_quote_let_pat>( } } -fn wrap_ty_with_expr(ty: impl ToTokens) -> Type { +pub(crate) fn wrap_ty_with_expr(ty: impl ToTokens) -> Type { parse_quote_spanned! {ty.span()=> ::fayalite::expr::Expr<#ty> } @@ -1173,7 +1181,7 @@ impl Visitor<'_> { Some(_) => {} } } - fn process_hdl_if(&mut self, hdl_attr: HdlAttr, expr_if: ExprIf) -> Expr { + fn process_hdl_if(&mut self, hdl_attr: HdlAttr, expr_if: ExprIf) -> Expr { let ExprIf { attrs, if_token, @@ -1181,10 +1189,10 @@ impl Visitor<'_> { then_branch, else_branch, } = expr_if; - self.require_normal_module_or_fn(if_token); - let else_expr = else_branch.unzip().1.map(|else_expr| match *else_expr { - Expr::If(expr_if) => self.process_hdl_if(hdl_attr.clone(), expr_if), - expr => expr, + let (else_token, else_expr) = else_branch.unzip(); + let else_expr = else_expr.map(|else_expr| match *else_expr { + Expr::If(expr_if) => Box::new(self.process_hdl_if(hdl_attr.clone(), expr_if)), + _ => else_expr, }); if let Expr::Let(ExprLet { attrs: let_attrs, @@ -1206,7 +1214,19 @@ impl Visitor<'_> { }, ); } - if let Some(else_expr) = else_expr { + let ExprOptions { sim } = hdl_attr.body; + if sim.is_some() { + ExprIf { + attrs, + if_token, + cond: parse_quote_spanned! {if_token.span=> + *::fayalite::sim::value::SimValue::<::fayalite::int::Bool>::value(&::fayalite::sim::value::ToSimValue::into_sim_value(#cond)) + }, + then_branch, + else_branch: else_token.zip(else_expr), + } + .into() + } else if let Some(else_expr) = else_expr { parse_quote_spanned! {if_token.span=> #(#attrs)* { @@ -1586,7 +1606,7 @@ impl Visitor<'_> { } } -fn empty_let() -> Local { +pub(crate) fn empty_let() -> Local { Local { attrs: vec![], let_token: Default::default(), @@ -1668,20 +1688,42 @@ impl Fold for Visitor<'_> { Repeat => process_hdl_repeat, Struct => process_hdl_struct, Tuple => process_hdl_tuple, + MethodCall => process_hdl_method_call, + Call => process_hdl_call, } } } - fn fold_local(&mut self, let_stmt: Local) -> Local { + fn fold_local(&mut self, mut let_stmt: Local) -> Local { match self .errors - .ok(HdlAttr::::parse_and_leave_attr( + .ok(HdlAttr::::parse_and_leave_attr( &let_stmt.attrs, )) { None => return empty_let(), Some(None) => return fold_local(self, let_stmt), Some(Some(HdlAttr { .. })) => {} }; + let mut pat = &let_stmt.pat; + if let Pat::Type(pat_type) = pat { + pat = &pat_type.pat; + } + let Pat::Ident(syn::PatIdent { + attrs: _, + by_ref: None, + mutability: _, + ident: _, + subpat: None, + }) = pat + else { + let hdl_attr = + HdlAttr::::parse_and_take_attr(&mut let_stmt.attrs) + .ok() + .flatten() + .expect("already checked above"); + let let_stmt = fold_local(self, let_stmt); + return self.process_hdl_let_pat(hdl_attr, let_stmt); + }; let hdl_let = syn::parse2::>>(let_stmt.into_token_stream()); let Some(hdl_let) = self.errors.ok(hdl_let) else { return empty_let(); diff --git a/crates/fayalite-proc-macros-impl/src/module/transform_body/expand_aggregate_literals.rs b/crates/fayalite-proc-macros-impl/src/module/transform_body/expand_aggregate_literals.rs index b5a0ad3..1aabb19 100644 --- a/crates/fayalite-proc-macros-impl/src/module/transform_body/expand_aggregate_literals.rs +++ b/crates/fayalite-proc-macros-impl/src/module/transform_body/expand_aggregate_literals.rs @@ -1,45 +1,102 @@ // SPDX-License-Identifier: LGPL-3.0-or-later // See Notices.txt for copyright information -use crate::{kw, module::transform_body::Visitor, HdlAttr}; + +use crate::{ + HdlAttr, kw, + module::transform_body::{ + ExprOptions, Visitor, + expand_match::{EnumPath, parse_enum_path}, + }, +}; use quote::{format_ident, quote_spanned}; +use std::mem; use syn::{ - parse::Nothing, parse_quote, parse_quote_spanned, spanned::Spanned, Expr, ExprArray, ExprPath, - ExprRepeat, ExprStruct, ExprTuple, FieldValue, TypePath, + Expr, ExprArray, ExprCall, ExprGroup, ExprMethodCall, ExprParen, ExprPath, ExprRepeat, + ExprStruct, ExprTuple, FieldValue, Token, TypePath, parse_quote_spanned, + punctuated::Punctuated, spanned::Spanned, token::Paren, }; impl Visitor<'_> { pub(crate) fn process_hdl_array( &mut self, - hdl_attr: HdlAttr, + hdl_attr: HdlAttr, mut expr_array: ExprArray, ) -> Expr { - self.require_normal_module_or_fn(hdl_attr); - for elem in &mut expr_array.elems { - *elem = parse_quote_spanned! {elem.span()=> - ::fayalite::expr::ToExpr::to_expr(&(#elem)) - }; + let ExprOptions { sim } = hdl_attr.body; + let span = hdl_attr.kw.span; + if sim.is_some() { + for elem in &mut expr_array.elems { + *elem = parse_quote_spanned! {elem.span()=> + ::fayalite::sim::value::ToSimValue::to_sim_value(&(#elem)) + }; + } + parse_quote_spanned! {span=> + ::fayalite::sim::value::ToSimValue::into_sim_value(#expr_array) + } + } else { + for elem in &mut expr_array.elems { + *elem = parse_quote_spanned! {elem.span()=> + ::fayalite::expr::ToExpr::to_expr(&(#elem)) + }; + } + parse_quote_spanned! {span=> + ::fayalite::expr::ToExpr::to_expr(&#expr_array) + } } - parse_quote! {::fayalite::expr::ToExpr::to_expr(&#expr_array)} } pub(crate) fn process_hdl_repeat( &mut self, - hdl_attr: HdlAttr, + hdl_attr: HdlAttr, mut expr_repeat: ExprRepeat, ) -> Expr { - self.require_normal_module_or_fn(hdl_attr); let repeated_value = &expr_repeat.expr; - *expr_repeat.expr = parse_quote_spanned! {repeated_value.span()=> - ::fayalite::expr::ToExpr::to_expr(&(#repeated_value)) - }; - parse_quote! {::fayalite::expr::ToExpr::to_expr(&#expr_repeat)} + let ExprOptions { sim } = hdl_attr.body; + let span = hdl_attr.kw.span; + if sim.is_some() { + *expr_repeat.expr = parse_quote_spanned! {repeated_value.span()=> + ::fayalite::sim::value::ToSimValue::to_sim_value(&(#repeated_value)) + }; + parse_quote_spanned! {span=> + ::fayalite::sim::value::ToSimValue::into_sim_value(#expr_repeat) + } + } else { + *expr_repeat.expr = parse_quote_spanned! {repeated_value.span()=> + ::fayalite::expr::ToExpr::to_expr(&(#repeated_value)) + }; + parse_quote_spanned! {span=> + ::fayalite::expr::ToExpr::to_expr(&#expr_repeat) + } + } } pub(crate) fn process_hdl_struct( &mut self, - hdl_attr: HdlAttr, - expr_struct: ExprStruct, + hdl_attr: HdlAttr, + mut expr_struct: ExprStruct, ) -> Expr { - self.require_normal_module_or_fn(&hdl_attr); let name_span = expr_struct.path.segments.last().unwrap().ident.span(); + let ExprOptions { sim } = hdl_attr.body; + if sim.is_some() { + let ty_path = TypePath { + qself: expr_struct.qself.take(), + path: expr_struct.path, + }; + expr_struct.path = parse_quote_spanned! {name_span=> + __SimValue::<#ty_path> + }; + for field in &mut expr_struct.fields { + let expr = &field.expr; + field.expr = parse_quote_spanned! {field.member.span()=> + ::fayalite::sim::value::ToSimValue::to_sim_value(&(#expr)) + }; + } + return parse_quote_spanned! {name_span=> + { + type __SimValue = ::SimValue; + let value: ::fayalite::sim::value::SimValue<#ty_path> = ::fayalite::sim::value::ToSimValue::into_sim_value(#expr_struct); + value + } + }; + } let builder_ident = format_ident!("__builder", span = name_span); let empty_builder = if expr_struct.qself.is_some() || expr_struct @@ -91,12 +148,126 @@ impl Visitor<'_> { } pub(crate) fn process_hdl_tuple( &mut self, - hdl_attr: HdlAttr, - expr_tuple: ExprTuple, + hdl_attr: HdlAttr, + mut expr_tuple: ExprTuple, ) -> Expr { - self.require_normal_module_or_fn(hdl_attr); - parse_quote_spanned! {expr_tuple.span()=> - ::fayalite::expr::ToExpr::to_expr(&#expr_tuple) + let ExprOptions { sim } = hdl_attr.body; + if sim.is_some() { + for element in &mut expr_tuple.elems { + *element = parse_quote_spanned! {element.span()=> + &(#element) + }; + } + parse_quote_spanned! {expr_tuple.span()=> + ::fayalite::sim::value::ToSimValue::into_sim_value(#expr_tuple) + } + } else { + parse_quote_spanned! {expr_tuple.span()=> + ::fayalite::expr::ToExpr::to_expr(&#expr_tuple) + } } } + pub(crate) fn process_hdl_call( + &mut self, + hdl_attr: HdlAttr, + mut expr_call: ExprCall, + ) -> Expr { + let span = hdl_attr.kw.span; + let mut func = &mut *expr_call.func; + let EnumPath { + variant_path: _, + enum_path, + variant_name, + } = loop { + match func { + Expr::Group(ExprGroup { expr, .. }) | Expr::Paren(ExprParen { expr, .. }) => { + func = &mut **expr; + } + Expr::Path(_) => { + let Expr::Path(ExprPath { attrs, qself, path }) = + mem::replace(func, Expr::PLACEHOLDER) + else { + unreachable!(); + }; + match parse_enum_path(TypePath { qself, path }) { + Ok(path) => break path, + Err(path) => { + self.errors.error(&path, "unsupported enum variant path"); + let TypePath { qself, path } = path; + *func = ExprPath { attrs, qself, path }.into(); + return expr_call.into(); + } + } + } + _ => { + self.errors.error( + &expr_call.func, + "#[hdl] function call -- function must be a possibly-parenthesized path", + ); + return expr_call.into(); + } + } + }; + self.process_hdl_method_call( + hdl_attr, + ExprMethodCall { + attrs: expr_call.attrs, + receiver: parse_quote_spanned! {span=> + <#enum_path as ::fayalite::ty::StaticType>::TYPE + }, + dot_token: Token![.](span), + method: variant_name, + turbofish: None, + paren_token: expr_call.paren_token, + args: expr_call.args, + }, + ) + } + pub(crate) fn process_hdl_method_call( + &mut self, + hdl_attr: HdlAttr, + mut expr_method_call: ExprMethodCall, + ) -> Expr { + let ExprOptions { sim } = hdl_attr.body; + let span = hdl_attr.kw.span; + // remove any number of groups and up to one paren + let mut receiver = &mut *expr_method_call.receiver; + let mut has_group = false; + let receiver = loop { + match receiver { + Expr::Group(ExprGroup { expr, .. }) => { + has_group = true; + receiver = expr; + } + Expr::Paren(ExprParen { expr, .. }) => break &mut **expr, + receiver @ Expr::Path(_) => break receiver, + _ => { + if !has_group { + self.errors.error( + &expr_method_call.receiver, + "#[hdl] on a method call needs parenthesized receiver", + ); + } + break &mut *expr_method_call.receiver; + } + } + }; + let func = if sim.is_some() { + parse_quote_spanned! {span=> + ::fayalite::enum_::enum_type_to_sim_builder + } + } else { + parse_quote_spanned! {span=> + ::fayalite::enum_::assert_is_enum_type + } + }; + *expr_method_call.receiver = ExprCall { + attrs: vec![], + func, + paren_token: Paren(span), + args: Punctuated::from_iter([mem::replace(receiver, Expr::PLACEHOLDER)]), + } + .into(); + expr_method_call.into() + } } diff --git a/crates/fayalite-proc-macros-impl/src/module/transform_body/expand_match.rs b/crates/fayalite-proc-macros-impl/src/module/transform_body/expand_match.rs index 1d53104..069f00d 100644 --- a/crates/fayalite-proc-macros-impl/src/module/transform_body/expand_match.rs +++ b/crates/fayalite-proc-macros-impl/src/module/transform_body/expand_match.rs @@ -1,24 +1,121 @@ // SPDX-License-Identifier: LGPL-3.0-or-later // See Notices.txt for copyright information use crate::{ - fold::{impl_fold, DoFold}, - kw, - module::transform_body::{with_debug_clone_and_fold, Visitor}, Errors, HdlAttr, PairsIterExt, + fold::{DoFold, impl_fold}, + kw, + module::transform_body::{ + ExprOptions, Visitor, empty_let, with_debug_clone_and_fold, wrap_ty_with_expr, + }, }; use proc_macro2::{Span, TokenStream}; -use quote::{format_ident, quote_spanned, ToTokens, TokenStreamExt}; +use quote::{ToTokens, TokenStreamExt, format_ident, quote_spanned}; +use std::collections::BTreeSet; use syn::{ - fold::{fold_arm, fold_expr_match, fold_pat, Fold}, - parse::Nothing, + Arm, Attribute, Expr, ExprMatch, FieldPat, Ident, Local, Member, Pat, PatIdent, PatOr, + PatParen, PatPath, PatRest, PatStruct, PatTuple, PatTupleStruct, PatWild, Path, PathSegment, + Token, TypePath, + fold::{Fold, fold_arm, fold_expr_match, fold_local, fold_pat}, parse_quote_spanned, punctuated::Punctuated, spanned::Spanned, token::{Brace, Paren}, - Arm, Attribute, Expr, ExprMatch, FieldPat, Ident, Member, Pat, PatIdent, PatOr, PatParen, - PatPath, PatRest, PatStruct, PatTupleStruct, PatWild, Path, PathSegment, Token, TypePath, }; +macro_rules! visit_trait { + ( + $($vis:vis fn $fn:ident($state:ident: _, $value:ident: &$Value:ty) $block:block)* + ) => { + trait VisitMatchPat<'a> { + $(fn $fn(&mut self, $value: &'a $Value) { + $fn(self, $value); + })* + } + + $($vis fn $fn<'a>($state: &mut (impl ?Sized + VisitMatchPat<'a>), $value: &'a $Value) $block)* + }; +} + +visit_trait! { + fn visit_match_pat_binding(_state: _, v: &MatchPatBinding) { + let MatchPatBinding { ident: _ } = v; + } + fn visit_match_pat_wild(_state: _, v: &MatchPatWild) { + let MatchPatWild { underscore_token: _ } = v; + } + fn visit_match_pat_rest(_state: _, v: &MatchPatRest) { + let MatchPatRest { dot2_token: _ } = v; + } + fn visit_match_pat_paren(state: _, v: &MatchPatParen) { + let MatchPatParen { paren_token: _, pat } = v; + state.visit_match_pat(pat); + } + fn visit_match_pat_paren_simple(state: _, v: &MatchPatParen) { + let MatchPatParen { paren_token: _, pat } = v; + state.visit_match_pat_simple(pat); + } + fn visit_match_pat_or(state: _, v: &MatchPatOr) { + let MatchPatOr { leading_vert: _, cases } = v; + for v in cases { + state.visit_match_pat(v); + } + } + fn visit_match_pat_or_simple(state: _, v: &MatchPatOr) { + let MatchPatOr { leading_vert: _, cases } = v; + for v in cases { + state.visit_match_pat_simple(v); + } + } + fn visit_match_pat_struct_field(state: _, v: &MatchPatStructField) { + let MatchPatStructField { field_name: _, colon_token: _, pat } = v; + state.visit_match_pat_simple(pat); + } + fn visit_match_pat_struct(state: _, v: &MatchPatStruct) { + let MatchPatStruct { match_span: _, path: _, brace_token: _, fields, rest: _ } = v; + for v in fields { + state.visit_match_pat_struct_field(v); + } + } + fn visit_match_pat_tuple(state: _, v: &MatchPatTuple) { + let MatchPatTuple { paren_token: _, fields } = v; + for v in fields { + state.visit_match_pat_simple(v); + } + } + fn visit_match_pat_enum_variant(state: _, v: &MatchPatEnumVariant) { + let MatchPatEnumVariant { + match_span:_, + sim:_, + variant_path: _, + enum_path: _, + variant_name: _, + field, + } = v; + if let Some((_, v)) = field { + state.visit_match_pat_simple(v); + } + } + fn visit_match_pat_simple(state: _, v: &MatchPatSimple) { + match v { + MatchPatSimple::Paren(v) => state.visit_match_pat_paren_simple(v), + MatchPatSimple::Or(v) => state.visit_match_pat_or_simple(v), + MatchPatSimple::Binding(v) => state.visit_match_pat_binding(v), + MatchPatSimple::Wild(v) => state.visit_match_pat_wild(v), + MatchPatSimple::Rest(v) => state.visit_match_pat_rest(v), + } + } + fn visit_match_pat(state: _, v: &MatchPat) { + match v { + MatchPat::Simple(v) => state.visit_match_pat_simple(v), + MatchPat::Or(v) => state.visit_match_pat_or(v), + MatchPat::Paren(v) => state.visit_match_pat_paren(v), + MatchPat::Struct(v) => state.visit_match_pat_struct(v), + MatchPat::Tuple(v) => state.visit_match_pat_tuple(v), + MatchPat::EnumVariant(v) => state.visit_match_pat_enum_variant(v), + } + } +} + with_debug_clone_and_fold! { struct MatchPatBinding<> { ident: Ident, @@ -53,6 +150,15 @@ with_debug_clone_and_fold! { } } +impl

MatchPatOr

{ + /// returns the first `|` between two patterns + fn first_inner_vert(&self) -> Option { + let mut pairs = self.cases.pairs(); + pairs.next_back(); + pairs.next().and_then(|v| v.into_tuple().1.copied()) + } +} + impl ToTokens for MatchPatOr

{ fn to_tokens(&self, tokens: &mut TokenStream) { let Self { @@ -77,6 +183,19 @@ impl ToTokens for MatchPatWild { } } +with_debug_clone_and_fold! { + struct MatchPatRest<> { + dot2_token: Token![..], + } +} + +impl ToTokens for MatchPatRest { + fn to_tokens(&self, tokens: &mut TokenStream) { + let Self { dot2_token } = self; + dot2_token.to_tokens(tokens); + } +} + with_debug_clone_and_fold! { struct MatchPatStructField<> { field_name: Ident, @@ -159,9 +278,29 @@ impl ToTokens for MatchPatStruct { } } +with_debug_clone_and_fold! { + struct MatchPatTuple<> { + paren_token: Paren, + fields: Punctuated, + } +} + +impl ToTokens for MatchPatTuple { + fn to_tokens(&self, tokens: &mut TokenStream) { + let Self { + paren_token, + fields, + } = self; + paren_token.surround(tokens, |tokens| { + fields.to_tokens(tokens); + }) + } +} + with_debug_clone_and_fold! { struct MatchPatEnumVariant<> { match_span: Span, + sim: Option<(kw::sim,)>, variant_path: Path, enum_path: Path, variant_name: Ident, @@ -173,6 +312,7 @@ impl ToTokens for MatchPatEnumVariant { fn to_tokens(&self, tokens: &mut TokenStream) { let Self { match_span, + sim, variant_path: _, enum_path, variant_name, @@ -182,7 +322,28 @@ impl ToTokens for MatchPatEnumVariant { __MatchTy::<#enum_path>::#variant_name } .to_tokens(tokens); - if let Some((paren_token, field)) = field { + if sim.is_some() { + if let Some((paren_token, field)) = field { + paren_token.surround(tokens, |tokens| { + field.to_tokens(tokens); + match field { + MatchPatSimple::Paren(_) + | MatchPatSimple::Or(_) + | MatchPatSimple::Binding(_) + | MatchPatSimple::Wild(_) => quote_spanned! {*match_span=> + , _ + } + .to_tokens(tokens), + MatchPatSimple::Rest(_) => {} + } + }); + } else { + quote_spanned! {*match_span=> + (_) + } + .to_tokens(tokens); + } + } else if let Some((paren_token, field)) = field { paren_token.surround(tokens, |tokens| field.to_tokens(tokens)); } } @@ -194,6 +355,7 @@ enum MatchPatSimple { Or(MatchPatOr), Binding(MatchPatBinding), Wild(MatchPatWild), + Rest(MatchPatRest), } impl_fold! { @@ -202,6 +364,7 @@ impl_fold! { Or(MatchPatOr), Binding(MatchPatBinding), Wild(MatchPatWild), + Rest(MatchPatRest), } } @@ -212,17 +375,18 @@ impl ToTokens for MatchPatSimple { Self::Paren(v) => v.to_tokens(tokens), Self::Binding(v) => v.to_tokens(tokens), Self::Wild(v) => v.to_tokens(tokens), + Self::Rest(v) => v.to_tokens(tokens), } } } -struct EnumPath { - variant_path: Path, - enum_path: Path, - variant_name: Ident, +pub(crate) struct EnumPath { + pub(crate) variant_path: Path, + pub(crate) enum_path: Path, + pub(crate) variant_name: Ident, } -fn parse_enum_path(variant_path: TypePath) -> Result { +pub(crate) fn parse_enum_path(variant_path: TypePath) -> Result { let TypePath { qself: None, path: variant_path, @@ -278,8 +442,9 @@ trait ParseMatchPat: Sized { fn or(v: MatchPatOr) -> Self; fn paren(v: MatchPatParen) -> Self; fn struct_(state: &mut HdlMatchParseState<'_>, v: MatchPatStruct) -> Result; + fn tuple(state: &mut HdlMatchParseState<'_>, v: MatchPatTuple) -> Result; fn enum_variant(state: &mut HdlMatchParseState<'_>, v: MatchPatEnumVariant) - -> Result; + -> Result; fn parse(state: &mut HdlMatchParseState<'_>, pat: Pat) -> Result { match pat { Pat::Ident(PatIdent { @@ -313,6 +478,7 @@ trait ParseMatchPat: Sized { state, MatchPatEnumVariant { match_span: state.match_span, + sim: state.sim, variant_path, enum_path, variant_name, @@ -359,6 +525,7 @@ trait ParseMatchPat: Sized { state, MatchPatEnumVariant { match_span: state.match_span, + sim: state.sim, variant_path, enum_path, variant_name, @@ -443,6 +610,7 @@ trait ParseMatchPat: Sized { state, MatchPatEnumVariant { match_span: state.match_span, + sim: state.sim, variant_path, enum_path, variant_name, @@ -462,7 +630,34 @@ trait ParseMatchPat: Sized { }) => Ok(Self::simple(MatchPatSimple::Wild(MatchPatWild { underscore_token, }))), - Pat::Tuple(_) | Pat::Slice(_) | Pat::Const(_) | Pat::Lit(_) | Pat::Range(_) => { + Pat::Tuple(PatTuple { + attrs: _, + paren_token, + elems, + }) => { + let fields = elems + .into_pairs() + .filter_map_pair_value(|field_pat| { + if let Pat::Rest(PatRest { + attrs: _, + dot2_token, + }) = field_pat + { + Some(MatchPatSimple::Rest(MatchPatRest { dot2_token })) + } else { + MatchPatSimple::parse(state, field_pat).ok() + } + }) + .collect(); + Self::tuple( + state, + MatchPatTuple { + paren_token, + fields, + }, + ) + } + Pat::Slice(_) | Pat::Const(_) | Pat::Lit(_) | Pat::Range(_) => { state .errors .error(pat, "not yet implemented in #[hdl] patterns"); @@ -497,6 +692,14 @@ impl ParseMatchPat for MatchPatSimple { Err(()) } + fn tuple(state: &mut HdlMatchParseState<'_>, v: MatchPatTuple) -> Result { + state.errors.push(syn::Error::new( + v.paren_token.span.open(), + "matching tuples is not yet implemented inside structs/enums in #[hdl] patterns", + )); + Err(()) + } + fn enum_variant( state: &mut HdlMatchParseState<'_>, v: MatchPatEnumVariant, @@ -515,6 +718,7 @@ enum MatchPat { Or(MatchPatOr), Paren(MatchPatParen), Struct(MatchPatStruct), + Tuple(MatchPatTuple), EnumVariant(MatchPatEnumVariant), } @@ -524,6 +728,7 @@ impl_fold! { Or(MatchPatOr), Paren(MatchPatParen), Struct(MatchPatStruct), + Tuple(MatchPatTuple), EnumVariant(MatchPatEnumVariant), } } @@ -545,6 +750,10 @@ impl ParseMatchPat for MatchPat { Ok(Self::Struct(v)) } + fn tuple(_state: &mut HdlMatchParseState<'_>, v: MatchPatTuple) -> Result { + Ok(Self::Tuple(v)) + } + fn enum_variant( _state: &mut HdlMatchParseState<'_>, v: MatchPatEnumVariant, @@ -560,6 +769,7 @@ impl ToTokens for MatchPat { Self::Or(v) => v.to_tokens(tokens), Self::Paren(v) => v.to_tokens(tokens), Self::Struct(v) => v.to_tokens(tokens), + Self::Tuple(v) => v.to_tokens(tokens), Self::EnumVariant(v) => v.to_tokens(tokens), } } @@ -622,10 +832,6 @@ struct RewriteAsCheckMatch { } impl Fold for RewriteAsCheckMatch { - fn fold_field_pat(&mut self, mut i: FieldPat) -> FieldPat { - i.colon_token = Some(Token![:](i.member.span())); - i - } fn fold_pat(&mut self, pat: Pat) -> Pat { match pat { Pat::Ident(mut pat_ident) => match parse_enum_ident(pat_ident.ident) { @@ -740,17 +946,177 @@ impl Fold for RewriteAsCheckMatch { // don't recurse into expressions i } + fn fold_local(&mut self, mut let_stmt: Local) -> Local { + if let Some(syn::LocalInit { + eq_token, + expr: _, + diverge, + }) = let_stmt.init.take() + { + let_stmt.init = Some(syn::LocalInit { + eq_token, + expr: parse_quote_spanned! {self.span=> + __match_value + }, + diverge: diverge.map(|(else_, _expr)| { + ( + else_, + parse_quote_spanned! {self.span=> + match __infallible {} + }, + ) + }), + }); + } + fold_local(self, let_stmt) + } } struct HdlMatchParseState<'a> { + sim: Option<(kw::sim,)>, match_span: Span, errors: &'a mut Errors, } +struct HdlLetPatVisitState<'a> { + errors: &'a mut Errors, + bindings: BTreeSet<&'a Ident>, +} + +impl<'a> VisitMatchPat<'a> for HdlLetPatVisitState<'a> { + fn visit_match_pat_binding(&mut self, v: &'a MatchPatBinding) { + self.bindings.insert(&v.ident); + } + + fn visit_match_pat_or(&mut self, v: &'a MatchPatOr) { + if let Some(first_inner_vert) = v.first_inner_vert() { + self.errors.error( + first_inner_vert, + "or-patterns are not supported in let statements", + ); + } + visit_match_pat_or(self, v); + } + + fn visit_match_pat_or_simple(&mut self, v: &'a MatchPatOr) { + if let Some(first_inner_vert) = v.first_inner_vert() { + self.errors.error( + first_inner_vert, + "or-patterns are not supported in let statements", + ); + } + visit_match_pat_or_simple(self, v); + } + + fn visit_match_pat_enum_variant(&mut self, v: &'a MatchPatEnumVariant) { + self.errors.error(v, "refutable pattern in let statement"); + } +} + impl Visitor<'_> { + pub(crate) fn process_hdl_let_pat( + &mut self, + hdl_attr: HdlAttr, + mut let_stmt: Local, + ) -> Local { + let span = let_stmt.let_token.span(); + let ExprOptions { sim } = hdl_attr.body; + if let Pat::Type(pat) = &mut let_stmt.pat { + *pat.ty = wrap_ty_with_expr((*pat.ty).clone()); + } + let check_let_stmt = RewriteAsCheckMatch { span }.fold_local(let_stmt.clone()); + let Local { + attrs: _, + let_token, + pat, + init, + semi_token, + } = let_stmt; + let Some(syn::LocalInit { + eq_token, + expr, + diverge, + }) = init + else { + self.errors + .error(let_token, "#[hdl] let must be assigned a value"); + return empty_let(); + }; + if let Some((else_, _)) = diverge { + // TODO: implement let-else + self.errors + .error(else_, "#[hdl] let ... else { ... } is not implemented"); + return empty_let(); + } + let Ok(pat) = MatchPat::parse( + &mut HdlMatchParseState { + sim, + match_span: span, + errors: &mut self.errors, + }, + pat, + ) else { + return empty_let(); + }; + let mut state = HdlLetPatVisitState { + errors: &mut self.errors, + bindings: BTreeSet::new(), + }; + state.visit_match_pat(&pat); + let HdlLetPatVisitState { + errors: _, + bindings, + } = state; + let retval = if sim.is_some() { + parse_quote_spanned! {span=> + let (#(#bindings,)*) = { + type __MatchTy = ::SimValue; + let __match_value = ::fayalite::sim::value::ToSimValue::to_sim_value(&(#expr)); + #let_token #pat #eq_token ::fayalite::sim::value::SimValue::into_value(__match_value) #semi_token + (#(#bindings,)*) + }; + } + } else { + parse_quote_spanned! {span=> + let (#(#bindings,)* __scope,) = { + type __MatchTy = ::MatchVariant; + let __match_expr = ::fayalite::expr::ToExpr::to_expr(&(#expr)); + ::fayalite::expr::check_match_expr( + __match_expr, + |__match_value, __infallible| { + #[allow(unused_variables)] + #check_let_stmt + match __infallible {} + }, + ); + let mut __match_iter = ::fayalite::module::match_(__match_expr); + let ::fayalite::__std::option::Option::Some(__match_variant) = + ::fayalite::__std::iter::Iterator::next(&mut __match_iter) + else { + ::fayalite::__std::unreachable!("#[hdl] let with uninhabited type"); + }; + let ::fayalite::__std::option::Option::None = + ::fayalite::__std::iter::Iterator::next(&mut __match_iter) + else { + ::fayalite::__std::unreachable!("#[hdl] let with refutable pattern"); + }; + let (__match_variant, __scope) = + ::fayalite::ty::MatchVariantAndInactiveScope::match_activate_scope( + __match_variant, + ); + #let_token #pat #eq_token __match_variant #semi_token + (#(#bindings,)* __scope,) + }; + } + }; + match retval { + syn::Stmt::Local(retval) => retval, + _ => unreachable!(), + } + } pub(crate) fn process_hdl_match( &mut self, - _hdl_attr: HdlAttr, + hdl_attr: HdlAttr, expr_match: ExprMatch, ) -> Expr { let span = expr_match.match_token.span(); @@ -762,8 +1128,9 @@ impl Visitor<'_> { brace_token: _, arms, } = expr_match; - self.require_normal_module_or_fn(match_token); + let ExprOptions { sim } = hdl_attr.body; let mut state = HdlMatchParseState { + sim, match_span: span, errors: &mut self.errors, }; @@ -771,24 +1138,36 @@ impl Visitor<'_> { arms.into_iter() .filter_map(|arm| MatchArm::parse(&mut state, arm).ok()), ); - let expr = quote_spanned! {span=> - { - type __MatchTy = ::MatchVariant; - let __match_expr = ::fayalite::expr::ToExpr::to_expr(&(#expr)); - ::fayalite::expr::check_match_expr(__match_expr, |__match_value, __infallible| { - #[allow(unused_variables)] - #check_match - }); - for __match_variant in ::fayalite::module::match_(__match_expr) { - let (__match_variant, __scope) = - ::fayalite::ty::MatchVariantAndInactiveScope::match_activate_scope( - __match_variant, - ); - #match_token __match_variant { + let expr = if sim.is_some() { + quote_spanned! {span=> + { + type __MatchTy = ::SimValue; + let __match_expr = ::fayalite::sim::value::ToSimValue::to_sim_value(&(#expr)); + #match_token ::fayalite::sim::value::SimValue::into_value(__match_expr) { #(#arms)* } } } + } else { + quote_spanned! {span=> + { + type __MatchTy = ::MatchVariant; + let __match_expr = ::fayalite::expr::ToExpr::to_expr(&(#expr)); + ::fayalite::expr::check_match_expr(__match_expr, |__match_value, __infallible| { + #[allow(unused_variables)] + #check_match + }); + for __match_variant in ::fayalite::module::match_(__match_expr) { + let (__match_variant, __scope) = + ::fayalite::ty::MatchVariantAndInactiveScope::match_activate_scope( + __match_variant, + ); + #match_token __match_variant { + #(#arms)* + } + } + } + } }; syn::parse2(expr).unwrap() } diff --git a/crates/fayalite-proc-macros-impl/src/process_cfg.rs b/crates/fayalite-proc-macros-impl/src/process_cfg.rs index 5cff08f..bcf2fa1 100644 --- a/crates/fayalite-proc-macros-impl/src/process_cfg.rs +++ b/crates/fayalite-proc-macros-impl/src/process_cfg.rs @@ -5,8 +5,8 @@ use crate::{Cfg, CfgAttr, Cfgs, Errors}; use proc_macro2::Ident; use std::{collections::VecDeque, marker::PhantomData}; use syn::{ - punctuated::{Pair, Punctuated}, Token, + punctuated::{Pair, Punctuated}, }; struct State { @@ -131,9 +131,9 @@ trait PhaseDispatch { type Args; type Output; fn dispatch_collect(self, args: Self::Args) - -> Self::Output; + -> Self::Output; fn dispatch_process(self, args: Self::Args) - -> Self::Output; + -> Self::Output; } trait Phase: Sized + 'static { @@ -2510,7 +2510,7 @@ pub(crate) fn process_cfgs(item: syn::Item, cfgs: Cfgs) -> syn::Result, +//! b: Bool, +//! } +//! +//! #[hdl_module] +//! fn my_module() { +//! #[hdl] +//! let my_input: MyStruct = m.input(); +//! #[hdl] +//! let my_output: UInt<8> = m.input(); +//! #[hdl] +//! let MyStruct { a, b } = my_input; +//! #[hdl] +//! if b { +//! connect(my_output, a); +//! } else { +//! connect(my_output, 0_hdl_u8); +//! } +//! } +//! ``` diff --git a/crates/fayalite/src/_docs/modules/module_bodies/hdl_match_statements.rs b/crates/fayalite/src/_docs/modules/module_bodies/hdl_match_statements.rs index 9e6c511..6df70f1 100644 --- a/crates/fayalite/src/_docs/modules/module_bodies/hdl_match_statements.rs +++ b/crates/fayalite/src/_docs/modules/module_bodies/hdl_match_statements.rs @@ -7,5 +7,5 @@ //! //! `#[hdl] match` statements' bodies must evaluate to type `()` for now. //! -//! `#[hdl] match` statements can only match one level of struct/enum pattern for now, +//! `#[hdl] match` statements can only match one level of struct/tuple/enum pattern for now, //! e.g. you can match with the pattern `HdlSome(v)`, but not `HdlSome(HdlSome(_))`. diff --git a/crates/fayalite/src/annotations.rs b/crates/fayalite/src/annotations.rs index 8eff4a0..70f0460 100644 --- a/crates/fayalite/src/annotations.rs +++ b/crates/fayalite/src/annotations.rs @@ -12,7 +12,7 @@ use std::{ ops::Deref, }; -#[derive(Clone)] +#[derive(Clone, Debug)] struct CustomFirrtlAnnotationFieldsImpl { value: serde_json::Map, serialized: Interned, @@ -314,10 +314,8 @@ impl> Iterator for IterIntoAnnotations { } impl< - T: FusedIterator< - Item: IntoAnnotations>, - >, - > FusedIterator for IterIntoAnnotations + T: FusedIterator>>, +> FusedIterator for IterIntoAnnotations { } diff --git a/crates/fayalite/src/array.rs b/crates/fayalite/src/array.rs index f617f91..569f2e2 100644 --- a/crates/fayalite/src/array.rs +++ b/crates/fayalite/src/array.rs @@ -2,17 +2,24 @@ // See Notices.txt for copyright information use crate::{ - expr::{ops::ArrayIndex, Expr, ToExpr}, - int::{DynSize, KnownSize, Size, SizeType, DYN_SIZE}, + expr::{ + CastToBits, Expr, HdlPartialEq, ReduceBits, ToExpr, + ops::{ArrayLiteral, ExprFromIterator, ExprIntoIterator, ExprPartialEq}, + }, + int::{Bool, DYN_SIZE, DynSize, KnownSize, Size, SizeType}, intern::{Intern, Interned, LazyInterned}, module::transform::visit::{Fold, Folder, Visit, Visitor}, + sim::value::{SimValue, SimValuePartialEq}, source_location::SourceLocation, ty::{ - CanonicalType, MatchVariantWithoutScope, StaticType, Type, TypeProperties, TypeWithDeref, + CanonicalType, MatchVariantWithoutScope, OpaqueSimValueSlice, OpaqueSimValueWriter, + OpaqueSimValueWritten, StaticType, Type, TypeProperties, TypeWithDeref, + serde_impls::SerdeCanonicalType, }, util::ConstUsize, }; -use std::ops::Index; +use serde::{Deserialize, Deserializer, Serialize, Serializer, de::Error}; +use std::{iter::FusedIterator, ops::Index}; #[derive(Copy, Clone, PartialEq, Eq, Hash)] pub struct ArrayType { @@ -41,15 +48,20 @@ impl ArrayType { is_storable, is_castable_from_bits, bit_width, + sim_only_values_len, } = element; let Some(bit_width) = bit_width.checked_mul(len) else { panic!("array too big"); }; + let Some(sim_only_values_len) = sim_only_values_len.checked_mul(len) else { + panic!("array too big"); + }; TypeProperties { is_passive, is_storable, is_castable_from_bits, bit_width, + sim_only_values_len, } } pub fn new(element: T, len: Len::SizeType) -> Self { @@ -91,6 +103,12 @@ impl> ArrayType { } } +impl Default for ArrayType { + fn default() -> Self { + Self::TYPE + } +} + impl StaticType for ArrayType { const TYPE: Self = Self { element: LazyInterned::new_lazy(&|| T::TYPE.intern_sized()), @@ -139,6 +157,7 @@ impl, Len: Size, State: Visitor + ?Sized> Visit impl Type for ArrayType { type BaseType = Array; type MaskType = ArrayType; + type SimValue = Len::ArraySimValue; type MatchVariant = Len::ArrayMatch; type MatchActiveScope = (); type MatchVariantAndInactiveScope = MatchVariantWithoutScope>; @@ -148,10 +167,8 @@ impl Type for ArrayType { this: Expr, source_location: SourceLocation, ) -> Self::MatchVariantsIter { - let base = Expr::as_dyn_array(this); - let base_ty = Expr::ty(base); let _ = source_location; - let retval = Vec::from_iter((0..base_ty.len()).map(|i| ArrayIndex::new(base, i).to_expr())); + let retval = Vec::from_iter(this); std::iter::once(MatchVariantWithoutScope( Len::ArrayMatch::::try_from(retval) .ok() @@ -177,16 +194,106 @@ impl Type for ArrayType { Len::from_usize(array.len()), ) } + fn source_location() -> SourceLocation { SourceLocation::builtin() } + + fn sim_value_from_opaque(&self, mut opaque: OpaqueSimValueSlice<'_>) -> Self::SimValue { + let element_ty = self.element(); + let element_size = element_ty.canonical().size(); + let mut value = Vec::with_capacity(self.len()); + for _ in 0..self.len() { + let (element_opaque, rest) = opaque.split_at(element_size); + value.push(SimValue::from_opaque(element_ty, element_opaque.to_owned())); + opaque = rest; + } + value.try_into().ok().expect("used correct length") + } + + fn sim_value_clone_from_opaque( + &self, + value: &mut Self::SimValue, + mut opaque: OpaqueSimValueSlice<'_>, + ) { + let element_ty = self.element(); + let element_size = element_ty.canonical().size(); + let value = AsMut::<[SimValue]>::as_mut(value); + assert_eq!(self.len(), value.len()); + for element_value in value { + assert_eq!(SimValue::ty(element_value), element_ty); + let (element_opaque, rest) = opaque.split_at(element_size); + SimValue::opaque_mut(element_value).clone_from_slice(element_opaque); + opaque = rest; + } + } + + fn sim_value_to_opaque<'w>( + &self, + value: &Self::SimValue, + mut writer: OpaqueSimValueWriter<'w>, + ) -> OpaqueSimValueWritten<'w> { + let element_ty = self.element(); + let element_size = element_ty.canonical().size(); + let value = AsRef::<[SimValue]>::as_ref(value); + assert_eq!(self.len(), value.len()); + for element_value in value { + assert_eq!(SimValue::ty(element_value), element_ty); + writer.fill_prefix_with(element_size, |writer| { + writer.fill_cloned_from_slice(SimValue::opaque(element_value).as_slice()) + }); + } + writer.fill_cloned_from_slice(OpaqueSimValueSlice::empty()) + } +} + +impl Serialize for ArrayType { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + SerdeCanonicalType::::Array { + element: self.element(), + len: self.len(), + } + .serialize(serializer) + } +} + +impl<'de, T: Type + Deserialize<'de>, Len: Size> Deserialize<'de> for ArrayType { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let name = |len| -> String { + if let Some(len) = len { + format!("an Array<_, {len}>") + } else { + "an Array<_>".to_string() + } + }; + match SerdeCanonicalType::::deserialize(deserializer)? { + SerdeCanonicalType::Array { element, len } => { + if let Some(len) = Len::try_from_usize(len) { + Ok(Self::new(element, len)) + } else { + Err(Error::invalid_value( + serde::de::Unexpected::Other(&name(Some(len))), + &&*name(Len::KNOWN_VALUE), + )) + } + } + ty => Err(Error::invalid_value( + serde::de::Unexpected::Other(ty.as_serde_unexpected_str()), + &&*name(Len::KNOWN_VALUE), + )), + } + } } impl TypeWithDeref for ArrayType { fn expr_deref(this: &Expr) -> &Self::MatchVariant { - let base = Expr::as_dyn_array(*this); - let base_ty = Expr::ty(base); - let retval = Vec::from_iter((0..base_ty.len()).map(|i| ArrayIndex::new(base, i).to_expr())); + let retval = Vec::from_iter(*this); Interned::into_inner(Intern::intern_sized( Len::ArrayMatch::::try_from(retval) .ok() @@ -218,3 +325,143 @@ impl Index for ArrayWithoutLen { Interned::into_inner(Intern::intern_sized(ArrayType::new(self.element, len))) } } + +impl ExprPartialEq> for ArrayType +where + Lhs: ExprPartialEq, +{ + fn cmp_eq(lhs: Expr, rhs: Expr>) -> Expr { + let lhs_ty = Expr::ty(lhs); + let rhs_ty = Expr::ty(rhs); + assert_eq!(lhs_ty.len(), rhs_ty.len()); + lhs.into_iter() + .zip(rhs) + .map(|(l, r)| l.cmp_eq(r)) + .collect::>>() + .cast_to_bits() + .all_one_bits() + } + + fn cmp_ne(lhs: Expr, rhs: Expr>) -> Expr { + let lhs_ty = Expr::ty(lhs); + let rhs_ty = Expr::ty(rhs); + assert_eq!(lhs_ty.len(), rhs_ty.len()); + lhs.into_iter() + .zip(rhs) + .map(|(l, r)| l.cmp_ne(r)) + .collect::>>() + .cast_to_bits() + .any_one_bits() + } +} + +impl SimValuePartialEq> for ArrayType +where + Lhs: SimValuePartialEq, +{ + fn sim_value_eq(this: &SimValue, other: &SimValue>) -> bool { + AsRef::<[_]>::as_ref(&**this) + .iter() + .zip(AsRef::<[_]>::as_ref(&**other)) + .all(|(l, r)| SimValuePartialEq::sim_value_eq(l, r)) + } +} + +impl ExprIntoIterator for ArrayType { + type Item = T; + type ExprIntoIter = ExprArrayIter; + + fn expr_into_iter(e: Expr) -> Self::ExprIntoIter { + ExprArrayIter { + base: e, + indexes: 0..Expr::ty(e).len(), + } + } +} + +#[derive(Clone, Debug)] +pub struct ExprArrayIter { + base: Expr>, + indexes: std::ops::Range, +} + +impl ExprArrayIter { + pub fn base(&self) -> Expr> { + self.base + } + pub fn indexes(&self) -> std::ops::Range { + self.indexes.clone() + } +} + +impl Iterator for ExprArrayIter { + type Item = Expr; + + fn next(&mut self) -> Option { + self.indexes.next().map(|i| self.base[i]) + } + + fn size_hint(&self) -> (usize, Option) { + self.indexes.size_hint() + } + + fn count(self) -> usize { + self.indexes.count() + } + + fn last(mut self) -> Option { + self.next_back() + } + + fn nth(&mut self, n: usize) -> Option { + self.indexes.nth(n).map(|i| self.base[i]) + } + + fn fold(self, init: B, mut f: F) -> B + where + F: FnMut(B, Self::Item) -> B, + { + self.indexes.fold(init, |b, i| f(b, self.base[i])) + } +} + +impl DoubleEndedIterator for ExprArrayIter { + fn next_back(&mut self) -> Option { + self.indexes.next_back().map(|i| self.base[i]) + } + + fn nth_back(&mut self, n: usize) -> Option { + self.indexes.nth_back(n).map(|i| self.base[i]) + } + + fn rfold(self, init: B, mut f: F) -> B + where + F: FnMut(B, Self::Item) -> B, + { + self.indexes.rfold(init, |b, i| f(b, self.base[i])) + } +} + +impl ExactSizeIterator for ExprArrayIter { + fn len(&self) -> usize { + self.indexes.len() + } +} + +impl FusedIterator for ExprArrayIter {} + +impl ExprFromIterator> for Array { + fn expr_from_iter>>(iter: T) -> Expr { + ArrayLiteral::new( + A::TYPE, + iter.into_iter().map(|v| Expr::canonical(v)).collect(), + ) + .to_expr() + } +} + +impl<'a, A: StaticType> ExprFromIterator<&'a Expr> for Array { + fn expr_from_iter>>(iter: T) -> Expr { + iter.into_iter().copied().collect() + } +} diff --git a/crates/fayalite/src/bundle.rs b/crates/fayalite/src/bundle.rs index 995510e..55843ea 100644 --- a/crates/fayalite/src/bundle.rs +++ b/crates/fayalite/src/bundle.rs @@ -2,20 +2,25 @@ // See Notices.txt for copyright information use crate::{ - expr::{ops::BundleLiteral, Expr, ToExpr}, + expr::{ + CastToBits, Expr, ReduceBits, ToExpr, + ops::{ArrayLiteral, BundleLiteral, ExprPartialEq}, + }, + int::{Bool, DynSize}, intern::{Intern, Interned}, - sim::{SimValue, ToSimValue}, + sim::value::{SimValue, SimValuePartialEq, ToSimValue, ToSimValueWithType}, source_location::SourceLocation, ty::{ - impl_match_variant_as_self, CanonicalType, MatchVariantWithoutScope, StaticType, Type, - TypeProperties, TypeWithDeref, + CanonicalType, MatchVariantWithoutScope, OpaqueSimValue, OpaqueSimValueSize, + OpaqueSimValueSlice, OpaqueSimValueWriter, OpaqueSimValueWritten, StaticType, Type, + TypeProperties, TypeWithDeref, impl_match_variant_as_self, }, + util::HashMap, }; -use bitvec::vec::BitVec; -use hashbrown::HashMap; +use serde::{Deserialize, Serialize}; use std::{fmt, marker::PhantomData}; -#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)] +#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, Serialize, Deserialize)] pub struct BundleField { pub name: Interned, pub flipped: bool, @@ -64,7 +69,7 @@ impl fmt::Display for FmtDebugInStruct { struct BundleImpl { fields: Interned<[BundleField]>, name_indexes: HashMap, usize>, - field_offsets: Interned<[usize]>, + field_offsets: Interned<[OpaqueSimValueSize]>, type_properties: TypeProperties, } @@ -84,12 +89,9 @@ impl std::fmt::Debug for BundleImpl { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.write_str("Bundle ")?; f.debug_set() - .entries( - self.fields - .iter() - .enumerate() - .map(|(index, field)| field.fmt_debug_in_struct(self.field_offsets[index])), - ) + .entries(self.fields.iter().enumerate().map(|(index, field)| { + field.fmt_debug_in_struct(self.field_offsets[index].bit_width) + })) .finish() } } @@ -114,6 +116,7 @@ impl BundleTypePropertiesBuilder { is_storable: true, is_castable_from_bits: true, bit_width: 0, + sim_only_values_len: 0, }) } pub const fn clone(&self) -> Self { @@ -121,8 +124,12 @@ impl BundleTypePropertiesBuilder { } #[must_use] pub const fn field(self, flipped: bool, field_props: TypeProperties) -> Self { - let Some(bit_width) = self.0.bit_width.checked_add(field_props.bit_width) else { - panic!("bundle is too big: bit-width overflowed"); + let Some(OpaqueSimValueSize { + bit_width, + sim_only_values_len, + }) = self.0.size().checked_add(field_props.size()) + else { + panic!("bundle is too big: size overflowed"); }; if flipped { Self(TypeProperties { @@ -130,6 +137,7 @@ impl BundleTypePropertiesBuilder { is_storable: false, is_castable_from_bits: false, bit_width, + sim_only_values_len, }) } else { Self(TypeProperties { @@ -138,6 +146,7 @@ impl BundleTypePropertiesBuilder { is_castable_from_bits: self.0.is_castable_from_bits & field_props.is_castable_from_bits, bit_width, + sim_only_values_len, }) } } @@ -155,14 +164,14 @@ impl Default for BundleTypePropertiesBuilder { impl Bundle { #[track_caller] pub fn new(fields: Interned<[BundleField]>) -> Self { - let mut name_indexes = HashMap::with_capacity(fields.len()); + let mut name_indexes = HashMap::with_capacity_and_hasher(fields.len(), Default::default()); let mut field_offsets = Vec::with_capacity(fields.len()); let mut type_props_builder = BundleTypePropertiesBuilder::new(); for (index, &BundleField { name, flipped, ty }) in fields.iter().enumerate() { if let Some(old_index) = name_indexes.insert(name, index) { panic!("duplicate field name {name:?}: at both index {old_index} and {index}"); } - field_offsets.push(type_props_builder.0.bit_width); + field_offsets.push(type_props_builder.0.size()); type_props_builder = type_props_builder.field(flipped, ty.type_properties()); } Self(Intern::intern_sized(BundleImpl { @@ -178,7 +187,7 @@ impl Bundle { pub fn field_by_name(&self, name: Interned) -> Option { Some(self.0.fields[*self.0.name_indexes.get(&name)?]) } - pub fn field_offsets(self) -> Interned<[usize]> { + pub fn field_offsets(self) -> Interned<[OpaqueSimValueSize]> { self.0.field_offsets } pub fn type_properties(self) -> TypeProperties { @@ -212,6 +221,7 @@ impl Bundle { impl Type for Bundle { type BaseType = Bundle; type MaskType = Bundle; + type SimValue = OpaqueSimValue; impl_match_variant_as_self!(); fn mask_type(&self) -> Self::MaskType { Self::new(Interned::from_iter(self.0.fields.into_iter().map( @@ -235,6 +245,28 @@ impl Type for Bundle { fn source_location() -> SourceLocation { SourceLocation::builtin() } + fn sim_value_from_opaque(&self, opaque: OpaqueSimValueSlice<'_>) -> Self::SimValue { + assert_eq!(self.type_properties().size(), opaque.size()); + opaque.to_owned() + } + fn sim_value_clone_from_opaque( + &self, + value: &mut Self::SimValue, + opaque: OpaqueSimValueSlice<'_>, + ) { + assert_eq!(self.type_properties().size(), opaque.size()); + assert_eq!(value.size(), opaque.size()); + value.clone_from_slice(opaque); + } + fn sim_value_to_opaque<'w>( + &self, + value: &Self::SimValue, + writer: OpaqueSimValueWriter<'w>, + ) -> OpaqueSimValueWritten<'w> { + assert_eq!(self.type_properties().size(), writer.size()); + assert_eq!(value.size(), writer.size()); + writer.fill_cloned_from_slice(value.as_slice()) + } } pub trait BundleType: Type { @@ -243,6 +275,102 @@ pub trait BundleType: Type { fn fields(&self) -> Interned<[BundleField]>; } +pub struct BundleSimValueFromOpaque<'a> { + fields: std::slice::Iter<'static, BundleField>, + opaque: OpaqueSimValueSlice<'a>, +} + +impl<'a> BundleSimValueFromOpaque<'a> { + #[track_caller] + pub fn new(bundle_ty: T, opaque: OpaqueSimValueSlice<'a>) -> Self { + let fields = bundle_ty.fields(); + assert_eq!( + opaque.size(), + fields + .iter() + .map(|BundleField { ty, .. }| ty.size()) + .sum::() + ); + Self { + fields: Interned::into_inner(fields).iter(), + opaque, + } + } + #[track_caller] + fn field_ty_and_opaque(&mut self) -> (T, OpaqueSimValueSlice<'a>) { + let Some(&BundleField { + name: _, + flipped: _, + ty, + }) = self.fields.next() + else { + panic!("tried to read too many fields from BundleSimValueFromBits"); + }; + let (field_opaque, rest) = self.opaque.split_at(ty.size()); + self.opaque = rest; + (T::from_canonical(ty), field_opaque) + } + #[track_caller] + pub fn field_from_opaque(&mut self) -> SimValue { + let (field_ty, field_opaque) = self.field_ty_and_opaque::(); + SimValue::from_opaque(field_ty, field_opaque.to_owned()) + } + #[track_caller] + pub fn field_clone_from_opaque(&mut self, field_value: &mut SimValue) { + let (field_ty, field_opaque) = self.field_ty_and_opaque::(); + assert_eq!(field_ty, SimValue::ty(field_value)); + SimValue::opaque_mut(field_value).clone_from_slice(field_opaque); + } +} + +pub struct BundleSimValueToOpaque<'a> { + fields: std::slice::Iter<'static, BundleField>, + writer: OpaqueSimValueWriter<'a>, +} + +impl<'a> BundleSimValueToOpaque<'a> { + #[track_caller] + pub fn new(bundle_ty: T, writer: OpaqueSimValueWriter<'a>) -> Self { + let fields = bundle_ty.fields(); + assert_eq!( + writer.size(), + fields + .iter() + .map(|BundleField { ty, .. }| ty.size()) + .sum::() + ); + Self { + fields: Interned::into_inner(fields).iter(), + writer, + } + } + #[track_caller] + pub fn field(&mut self, field_value: &SimValue) { + let Some(&BundleField { + name: _, + flipped: _, + ty, + }) = self.fields.next() + else { + panic!("tried to write too many fields with BundleSimValueToOpaque"); + }; + assert_eq!(T::from_canonical(ty), SimValue::ty(field_value)); + self.writer.fill_prefix_with(ty.size(), |writer| { + writer.fill_cloned_from_slice(SimValue::opaque(field_value).as_slice()) + }); + } + #[track_caller] + pub fn finish(mut self) -> OpaqueSimValueWritten<'a> { + assert_eq!( + self.fields.next(), + None, + "wrote too few fields with BundleSimValueToOpaque" + ); + self.writer + .fill_cloned_from_slice(OpaqueSimValueSlice::empty()) + } +} + #[derive(Default)] pub struct NoBuilder; @@ -325,7 +453,19 @@ macro_rules! impl_tuple_builder_fields { } macro_rules! impl_tuples { - ([$({#[num = $num:literal, field = $field:ident, ty = $ty_var:ident: $Ty:ident] $var:ident: $T:ident})*] []) => { + ( + [$({ + #[ + num = $num:tt, + field = $field:ident, + ty = $ty_var:ident: $Ty:ident, + lhs = $lhs_var:ident: $Lhs:ident, + rhs = $rhs_var:ident: $Rhs:ident + ] + $var:ident: $T:ident + })*] + [] + ) => { impl_tuple_builder_fields! { {} [$({ @@ -337,6 +477,7 @@ macro_rules! impl_tuples { impl<$($T: Type,)*> Type for ($($T,)*) { type BaseType = Bundle; type MaskType = ($($T::MaskType,)*); + type SimValue = ($(SimValue<$T>,)*); type MatchVariant = ($(Expr<$T>,)*); type MatchActiveScope = (); type MatchVariantAndInactiveScope = MatchVariantWithoutScope; @@ -375,6 +516,33 @@ macro_rules! impl_tuples { fn source_location() -> SourceLocation { SourceLocation::builtin() } + fn sim_value_from_opaque(&self, opaque: OpaqueSimValueSlice<'_>) -> Self::SimValue { + #![allow(unused_mut, unused_variables)] + let mut v = BundleSimValueFromOpaque::new(*self, opaque); + $(let $var = v.field_from_opaque();)* + ($($var,)*) + } + fn sim_value_clone_from_opaque( + &self, + value: &mut Self::SimValue, + opaque: OpaqueSimValueSlice<'_>, + ) { + #![allow(unused_mut, unused_variables)] + let mut v = BundleSimValueFromOpaque::new(*self, opaque); + let ($($var,)*) = value; + $(v.field_clone_from_opaque($var);)* + } + fn sim_value_to_opaque<'w>( + &self, + value: &Self::SimValue, + writer: OpaqueSimValueWriter<'w>, + ) -> OpaqueSimValueWritten<'w> { + #![allow(unused_mut, unused_variables)] + let mut v = BundleSimValueToOpaque::new(*self, writer); + let ($($var,)*) = value; + $(v.field($var);)* + v.finish() + } } impl<$($T: Type,)*> BundleType for ($($T,)*) { type Builder = TupleBuilder<($(Unfilled<$T>,)*)>; @@ -425,77 +593,104 @@ macro_rules! impl_tuples { BundleLiteral::new(ty, field_values[..].intern()).to_expr() } } - impl<$($T: ToSimValue,)*> ToSimValue for ($($T,)*) { + impl<$($T: ToSimValueWithType,)*> ToSimValueWithType for ($($T,)*) { #[track_caller] - fn to_sim_value(&self, ty: CanonicalType) -> SimValue { - ToSimValue::::to_sim_value(self, Bundle::from_canonical(ty)).into_canonical() + fn to_sim_value_with_type(&self, ty: CanonicalType) -> SimValue { + SimValue::into_canonical(ToSimValueWithType::::to_sim_value_with_type(self, Bundle::from_canonical(ty))) } #[track_caller] - fn into_sim_value(self, ty: CanonicalType) -> SimValue + fn into_sim_value_with_type(self, ty: CanonicalType) -> SimValue { - ToSimValue::::into_sim_value(self, Bundle::from_canonical(ty)).into_canonical() - } - #[track_caller] - fn box_into_sim_value(self: Box, ty: CanonicalType) -> SimValue { - ToSimValue::::box_into_sim_value(self, Bundle::from_canonical(ty)).into_canonical() + SimValue::into_canonical(ToSimValueWithType::::into_sim_value_with_type(self, Bundle::from_canonical(ty))) } } - impl<$($T: ToSimValue,)*> ToSimValue for ($($T,)*) { + impl<$($T: ToSimValueWithType,)*> ToSimValueWithType for ($($T,)*) { #[track_caller] - fn to_sim_value(&self, ty: Bundle) -> SimValue { + fn to_sim_value_with_type(&self, ty: Bundle) -> SimValue { let ($($var,)*) = self; let [$($ty_var,)*] = *ty.fields() else { panic!("bundle has wrong number of fields"); }; - $(let $var = $var.to_sim_value($ty_var.ty);)* - ToSimValue::into_sim_value(($($var,)*), ty) + $(let $var = $var.to_sim_value_with_type($ty_var.ty);)* + ToSimValueWithType::into_sim_value_with_type(($($var,)*), ty) } #[track_caller] - fn into_sim_value(self, ty: Bundle) -> SimValue { + fn into_sim_value_with_type(self, ty: Bundle) -> SimValue { #![allow(unused_mut)] #![allow(clippy::unused_unit)] let ($($var,)*) = self; let [$($ty_var,)*] = *ty.fields() else { panic!("bundle has wrong number of fields"); }; - let mut bits: Option = None; - $(let $var = $var.into_sim_value($ty_var.ty); - assert_eq!($var.ty(), $ty_var.ty); - if !$var.bits().is_empty() { - if let Some(bits) = &mut bits { - bits.extend_from_bitslice($var.bits()); - } else { - let mut $var = $var.into_bits(); - $var.reserve(ty.type_properties().bit_width - $var.len()); - bits = Some($var); - } - } + let mut opaque = OpaqueSimValue::empty(); + $(let $var = $var.into_sim_value_with_type($ty_var.ty); + assert_eq!(SimValue::ty(&$var), $ty_var.ty); + opaque.extend_from_slice(SimValue::opaque(&$var).as_slice()); )* - bits.unwrap_or_else(BitVec::new).into_sim_value(ty) - } - #[track_caller] - fn box_into_sim_value(self: Box, ty: Bundle) -> SimValue { - Self::into_sim_value(*self, ty) + SimValue::from_opaque(ty, opaque) } } - impl<$($T: ToSimValue<$Ty>, $Ty: Type,)*> ToSimValue<($($Ty,)*)> for ($($T,)*) { + impl<$($T: ToSimValueWithType<$Ty>, $Ty: Type,)*> ToSimValueWithType<($($Ty,)*)> for ($($T,)*) { #[track_caller] - fn to_sim_value(&self, ty: ($($Ty,)*)) -> SimValue<($($Ty,)*)> { + fn to_sim_value_with_type(&self, ty: ($($Ty,)*)) -> SimValue<($($Ty,)*)> { let ($($var,)*) = self; let ($($ty_var,)*) = ty; - $(let $var = $var.to_sim_value($ty_var).into_canonical();)* - SimValue::from_canonical(ToSimValue::into_sim_value(($($var,)*), ty.canonical())) + $(let $var = $var.to_sim_value_with_type($ty_var);)* + SimValue::from_value(ty, ($($var,)*)) } #[track_caller] - fn into_sim_value(self, ty: ($($Ty,)*)) -> SimValue<($($Ty,)*)> { + fn into_sim_value_with_type(self, ty: ($($Ty,)*)) -> SimValue<($($Ty,)*)> { let ($($var,)*) = self; let ($($ty_var,)*) = ty; - $(let $var = $var.into_sim_value($ty_var).into_canonical();)* - SimValue::from_canonical(ToSimValue::into_sim_value(($($var,)*), ty.canonical())) + $(let $var = $var.into_sim_value_with_type($ty_var);)* + SimValue::from_value(ty, ($($var,)*)) + } + } + impl<$($T: ToSimValue,)*> ToSimValue for ($($T,)*) { + type Type = ($($T::Type,)*); + #[track_caller] + fn to_sim_value(&self) -> SimValue { + let ($($var,)*) = self; + $(let $var = $var.to_sim_value();)* + SimValue::from_value(($(SimValue::ty(&$var),)*), ($($var,)*)) } #[track_caller] - fn box_into_sim_value(self: Box, ty: ($($Ty,)*)) -> SimValue<($($Ty,)*)> { - Self::into_sim_value(*self, ty) + fn into_sim_value(self) -> SimValue { + let ($($var,)*) = self; + $(let $var = $var.to_sim_value();)* + SimValue::from_value(($(SimValue::ty(&$var),)*), ($($var,)*)) + } + } + impl<$($Lhs: Type + ExprPartialEq<$Rhs>, $Rhs: Type,)*> ExprPartialEq<($($Rhs,)*)> for ($($Lhs,)*) { + fn cmp_eq(lhs: Expr, rhs: Expr<($($Rhs,)*)>) -> Expr { + let ($($lhs_var,)*) = *lhs; + let ($($rhs_var,)*) = *rhs; + ArrayLiteral::::new( + Bool, + FromIterator::from_iter([$(Expr::canonical(ExprPartialEq::cmp_eq($lhs_var, $rhs_var)),)*]), + ) + .cast_to_bits() + .all_one_bits() + } + + fn cmp_ne(lhs: Expr, rhs: Expr<($($Rhs,)*)>) -> Expr { + let ($($lhs_var,)*) = *lhs; + let ($($rhs_var,)*) = *rhs; + ArrayLiteral::::new( + Bool, + FromIterator::from_iter([$(Expr::canonical(ExprPartialEq::cmp_ne($lhs_var, $rhs_var)),)*]), + ) + .cast_to_bits() + .any_one_bits() + } + } + impl<$($Lhs: SimValuePartialEq<$Rhs>, $Rhs: Type,)*> SimValuePartialEq<($($Rhs,)*)> for ($($Lhs,)*) { + fn sim_value_eq(lhs: &SimValue, rhs: &SimValue<($($Rhs,)*)>) -> bool { + let ($($lhs_var,)*) = &**lhs; + let ($($rhs_var,)*) = &**rhs; + let retval = true; + $(let retval = retval && $lhs_var == $rhs_var;)* + retval } } }; @@ -507,24 +702,25 @@ macro_rules! impl_tuples { impl_tuples! { [] [ - {#[num = 0, field = field_0, ty = ty0: Ty0] v0: T0} - {#[num = 1, field = field_1, ty = ty1: Ty1] v1: T1} - {#[num = 2, field = field_2, ty = ty2: Ty2] v2: T2} - {#[num = 3, field = field_3, ty = ty3: Ty3] v3: T3} - {#[num = 4, field = field_4, ty = ty4: Ty4] v4: T4} - {#[num = 5, field = field_5, ty = ty5: Ty5] v5: T5} - {#[num = 6, field = field_6, ty = ty6: Ty6] v6: T6} - {#[num = 7, field = field_7, ty = ty7: Ty7] v7: T7} - {#[num = 8, field = field_8, ty = ty8: Ty8] v8: T8} - {#[num = 9, field = field_9, ty = ty9: Ty9] v9: T9} - {#[num = 10, field = field_10, ty = ty10: Ty10] v10: T10} - {#[num = 11, field = field_11, ty = ty11: Ty11] v11: T11} + {#[num = 0, field = field_0, ty = ty0: Ty0, lhs = lhs0: Lhs0, rhs = rhs0: Rhs0] v0: T0} + {#[num = 1, field = field_1, ty = ty1: Ty1, lhs = lhs1: Lhs1, rhs = rhs1: Rhs1] v1: T1} + {#[num = 2, field = field_2, ty = ty2: Ty2, lhs = lhs2: Lhs2, rhs = rhs2: Rhs2] v2: T2} + {#[num = 3, field = field_3, ty = ty3: Ty3, lhs = lhs3: Lhs3, rhs = rhs3: Rhs3] v3: T3} + {#[num = 4, field = field_4, ty = ty4: Ty4, lhs = lhs4: Lhs4, rhs = rhs4: Rhs4] v4: T4} + {#[num = 5, field = field_5, ty = ty5: Ty5, lhs = lhs5: Lhs5, rhs = rhs5: Rhs5] v5: T5} + {#[num = 6, field = field_6, ty = ty6: Ty6, lhs = lhs6: Lhs6, rhs = rhs6: Rhs6] v6: T6} + {#[num = 7, field = field_7, ty = ty7: Ty7, lhs = lhs7: Lhs7, rhs = rhs7: Rhs7] v7: T7} + {#[num = 8, field = field_8, ty = ty8: Ty8, lhs = lhs8: Lhs8, rhs = rhs8: Rhs8] v8: T8} + {#[num = 9, field = field_9, ty = ty9: Ty9, lhs = lhs9: Lhs9, rhs = rhs9: Rhs9] v9: T9} + {#[num = 10, field = field_10, ty = ty10: Ty10, lhs = lhs10: Lhs10, rhs = rhs10: Rhs10] v10: T10} + {#[num = 11, field = field_11, ty = ty11: Ty11, lhs = lhs11: Lhs11, rhs = rhs11: Rhs11] v11: T11} ] } impl Type for PhantomData { type BaseType = Bundle; type MaskType = (); + type SimValue = PhantomData; type MatchVariant = PhantomData; type MatchActiveScope = (); type MatchVariantAndInactiveScope = MatchVariantWithoutScope; @@ -557,6 +753,24 @@ impl Type for PhantomData { fn source_location() -> SourceLocation { SourceLocation::builtin() } + fn sim_value_from_opaque(&self, opaque: OpaqueSimValueSlice<'_>) -> Self::SimValue { + assert!(opaque.is_empty()); + *self + } + fn sim_value_clone_from_opaque( + &self, + _value: &mut Self::SimValue, + opaque: OpaqueSimValueSlice<'_>, + ) { + assert!(opaque.is_empty()); + } + fn sim_value_to_opaque<'w>( + &self, + _value: &Self::SimValue, + writer: OpaqueSimValueWriter<'w>, + ) -> OpaqueSimValueWritten<'w> { + writer.fill_cloned_from_slice(OpaqueSimValueSlice::empty()) + } } pub struct PhantomDataBuilder(PhantomData); @@ -604,26 +818,35 @@ impl ToExpr for PhantomData { } } -impl ToSimValue for PhantomData { +impl ToSimValue for PhantomData { + type Type = PhantomData; + #[track_caller] - fn to_sim_value(&self, ty: Self) -> SimValue { - ToSimValue::into_sim_value(BitVec::new(), ty) + fn to_sim_value(&self) -> SimValue { + SimValue::from_value(*self, *self) } } -impl ToSimValue for PhantomData { +impl ToSimValueWithType for PhantomData { #[track_caller] - fn to_sim_value(&self, ty: Bundle) -> SimValue { - assert!(ty.fields().is_empty()); - ToSimValue::into_sim_value(BitVec::new(), ty) + fn to_sim_value_with_type(&self, ty: Self) -> SimValue { + SimValue::from_value(ty, *self) } } -impl ToSimValue for PhantomData { +impl ToSimValueWithType for PhantomData { #[track_caller] - fn to_sim_value(&self, ty: CanonicalType) -> SimValue { - let ty = Bundle::from_canonical(ty); + fn to_sim_value_with_type(&self, ty: Bundle) -> SimValue { assert!(ty.fields().is_empty()); - ToSimValue::into_sim_value(BitVec::new(), ty).into_canonical() + SimValue::from_opaque(ty, OpaqueSimValue::empty()) + } +} + +impl ToSimValueWithType for PhantomData { + #[track_caller] + fn to_sim_value_with_type(&self, canonical_ty: CanonicalType) -> SimValue { + let ty = Bundle::from_canonical(canonical_ty); + assert!(ty.fields().is_empty()); + SimValue::from_opaque(canonical_ty, OpaqueSimValue::empty()) } } diff --git a/crates/fayalite/src/cli.rs b/crates/fayalite/src/cli.rs index 66741ef..6fb4b5e 100644 --- a/crates/fayalite/src/cli.rs +++ b/crates/fayalite/src/cli.rs @@ -8,10 +8,10 @@ use crate::{ util::{job_server::AcquiredJob, streaming_read_utf8::streaming_read_utf8}, }; use clap::{ - builder::{OsStringValueParser, TypedValueParser}, Parser, Subcommand, ValueEnum, ValueHint, + builder::{OsStringValueParser, TypedValueParser}, }; -use eyre::{eyre, Report}; +use eyre::{Report, eyre}; use serde::{Deserialize, Serialize}; use std::{ error, @@ -258,7 +258,7 @@ pub struct VerilogArgs { default_value = "firtool", env = "FIRTOOL", value_hint = ValueHint::CommandName, - value_parser = OsStringValueParser::new().try_map(which::which) + value_parser = OsStringValueParser::new().try_map(which) )] pub firtool: PathBuf, #[arg(long)] @@ -301,7 +301,9 @@ impl VerilogArgs { input.split_once(file_separator_prefix) { let Some((next_file_name, rest)) = rest.split_once(file_separator_suffix) else { - return Err(CliError(eyre!("parsing firtool's output failed: found {file_separator_prefix:?} but no {file_separator_suffix:?}"))); + return Err(CliError(eyre!( + "parsing firtool's output failed: found {file_separator_prefix:?} but no {file_separator_suffix:?}" + ))); }; input = rest; (chunk, Some(next_file_name.as_ref())) @@ -428,6 +430,13 @@ impl clap::Args for FormalAdjustArgs { } } +fn which(v: std::ffi::OsString) -> which::Result { + #[cfg(not(miri))] + return which::which(v); + #[cfg(miri)] + return Ok(Path::new("/").join(v)); +} + #[derive(Parser, Clone)] #[non_exhaustive] pub struct FormalArgs { @@ -438,7 +447,7 @@ pub struct FormalArgs { default_value = "sby", env = "SBY", value_hint = ValueHint::CommandName, - value_parser = OsStringValueParser::new().try_map(which::which) + value_parser = OsStringValueParser::new().try_map(which) )] pub sby: PathBuf, #[arg(long)] diff --git a/crates/fayalite/src/clock.rs b/crates/fayalite/src/clock.rs index 711432b..909edbd 100644 --- a/crates/fayalite/src/clock.rs +++ b/crates/fayalite/src/clock.rs @@ -6,8 +6,12 @@ use crate::{ int::Bool, reset::{Reset, ResetType}, source_location::SourceLocation, - ty::{impl_match_variant_as_self, CanonicalType, StaticType, Type, TypeProperties}, + ty::{ + CanonicalType, OpaqueSimValueSize, OpaqueSimValueSlice, OpaqueSimValueWriter, + OpaqueSimValueWritten, StaticType, Type, TypeProperties, impl_match_variant_as_self, + }, }; +use bitvec::{bits, order::Lsb0}; #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, Default)] pub struct Clock; @@ -15,6 +19,7 @@ pub struct Clock; impl Type for Clock { type BaseType = Clock; type MaskType = Bool; + type SimValue = bool; impl_match_variant_as_self!(); @@ -36,6 +41,31 @@ impl Type for Clock { }; retval } + + fn sim_value_from_opaque(&self, opaque: OpaqueSimValueSlice<'_>) -> Self::SimValue { + assert_eq!(opaque.size(), OpaqueSimValueSize::from_bit_width(1)); + opaque.bits()[0] + } + + fn sim_value_clone_from_opaque( + &self, + value: &mut Self::SimValue, + opaque: OpaqueSimValueSlice<'_>, + ) { + assert_eq!(opaque.size(), OpaqueSimValueSize::from_bit_width(1)); + *value = opaque.bits()[0]; + } + + fn sim_value_to_opaque<'w>( + &self, + value: &Self::SimValue, + writer: OpaqueSimValueWriter<'w>, + ) -> OpaqueSimValueWritten<'w> { + assert_eq!(writer.size(), OpaqueSimValueSize::from_bit_width(1)); + writer.fill_cloned_from_slice(OpaqueSimValueSlice::from_bitslice( + [bits![0], bits![1]][*value as usize], + )) + } } impl Clock { @@ -55,6 +85,7 @@ impl StaticType for Clock { is_storable: false, is_castable_from_bits: true, bit_width: 1, + sim_only_values_len: 0, }; const MASK_TYPE_PROPERTIES: TypeProperties = Bool::TYPE_PROPERTIES; } diff --git a/crates/fayalite/src/enum_.rs b/crates/fayalite/src/enum_.rs index 2ed0b8e..083072b 100644 --- a/crates/fayalite/src/enum_.rs +++ b/crates/fayalite/src/enum_.rs @@ -2,21 +2,31 @@ // See Notices.txt for copyright information use crate::{ - expr::{ops::VariantAccess, Expr, ToExpr}, + expr::{ + Expr, ToExpr, + ops::{ExprPartialEq, VariantAccess}, + }, hdl, - int::Bool, + int::{Bool, UIntValue}, intern::{Intern, Interned}, module::{ - connect, enum_match_variants_helper, incomplete_wire, wire, - EnumMatchVariantAndInactiveScopeImpl, EnumMatchVariantsIterImpl, Scope, + EnumMatchVariantAndInactiveScopeImpl, EnumMatchVariantsIterImpl, Scope, connect, + enum_match_variants_helper, incomplete_wire, wire, }, + sim::value::{SimValue, SimValuePartialEq}, source_location::SourceLocation, - ty::{CanonicalType, MatchVariantAndInactiveScope, StaticType, Type, TypeProperties}, + ty::{ + CanonicalType, MatchVariantAndInactiveScope, OpaqueSimValue, OpaqueSimValueSize, + OpaqueSimValueSlice, OpaqueSimValueWriter, OpaqueSimValueWritten, StaticType, Type, + TypeProperties, + }, + util::HashMap, }; -use hashbrown::HashMap; -use std::{convert::Infallible, fmt, iter::FusedIterator}; +use bitvec::{order::Lsb0, slice::BitSlice, view::BitView}; +use serde::{Deserialize, Serialize}; +use std::{convert::Infallible, fmt, iter::FusedIterator, sync::Arc}; -#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, Serialize, Deserialize)] pub struct EnumVariant { pub name: Interned, pub ty: Option, @@ -111,6 +121,7 @@ impl EnumTypePropertiesBuilder { is_storable: true, is_castable_from_bits: true, bit_width: 0, + sim_only_values_len: 0, }, variant_count: 0, } @@ -129,9 +140,14 @@ impl EnumTypePropertiesBuilder { is_storable, is_castable_from_bits, bit_width, + sim_only_values_len, }) = field_props { assert!(is_passive, "variant type must be a passive type"); + assert!( + sim_only_values_len == 0, + "can't have `SimOnlyValue`s in an Enum" + ); type_properties = TypeProperties { is_passive: true, is_storable: type_properties.is_storable & is_storable, @@ -142,6 +158,7 @@ impl EnumTypePropertiesBuilder { } else { type_properties.bit_width }, + sim_only_values_len: 0, }; } Self { @@ -149,6 +166,12 @@ impl EnumTypePropertiesBuilder { variant_count: variant_count + 1, } } + #[must_use] + pub fn variants(self, variants: impl IntoIterator) -> Self { + variants.into_iter().fold(self, |this, variant| { + this.variant(variant.ty.map(CanonicalType::type_properties)) + }) + } pub const fn finish(self) -> TypeProperties { assert!( self.variant_count != 0, @@ -178,7 +201,8 @@ impl Default for EnumTypePropertiesBuilder { impl Enum { #[track_caller] pub fn new(variants: Interned<[EnumVariant]>) -> Self { - let mut name_indexes = HashMap::with_capacity(variants.len()); + let mut name_indexes = + HashMap::with_capacity_and_hasher(variants.len(), Default::default()); let mut type_props_builder = EnumTypePropertiesBuilder::new(); for (index, EnumVariant { name, ty }) in variants.iter().enumerate() { if let Some(old_index) = name_indexes.insert(*name, index) { @@ -238,13 +262,14 @@ impl Enum { pub trait EnumType: Type< - BaseType = Enum, - MaskType = Bool, - MatchActiveScope = Scope, - MatchVariantAndInactiveScope = EnumMatchVariantAndInactiveScope, - MatchVariantsIter = EnumMatchVariantsIter, -> + BaseType = Enum, + MaskType = Bool, + MatchActiveScope = Scope, + MatchVariantAndInactiveScope = EnumMatchVariantAndInactiveScope, + MatchVariantsIter = EnumMatchVariantsIter, + > { + type SimBuilder: From; fn variants(&self) -> Interned<[EnumVariant]>; fn match_activate_scope( v: Self::MatchVariantAndInactiveScope, @@ -307,7 +332,18 @@ impl DoubleEndedIterator for EnumMatchVariantsIter { } } +pub struct NoBuilder { + _ty: Enum, +} + +impl From for NoBuilder { + fn from(_ty: Enum) -> Self { + Self { _ty } + } +} + impl EnumType for Enum { + type SimBuilder = NoBuilder; fn match_activate_scope( v: Self::MatchVariantAndInactiveScope, ) -> (Self::MatchVariant, Self::MatchActiveScope) { @@ -322,6 +358,7 @@ impl EnumType for Enum { impl Type for Enum { type BaseType = Enum; type MaskType = Bool; + type SimValue = OpaqueSimValue; type MatchVariant = Option>; type MatchActiveScope = Scope; type MatchVariantAndInactiveScope = EnumMatchVariantAndInactiveScope; @@ -352,6 +389,341 @@ impl Type for Enum { fn source_location() -> SourceLocation { SourceLocation::builtin() } + fn sim_value_from_opaque(&self, opaque: OpaqueSimValueSlice<'_>) -> Self::SimValue { + assert_eq!(self.type_properties().size(), opaque.size()); + opaque.to_owned() + } + fn sim_value_clone_from_opaque( + &self, + value: &mut Self::SimValue, + opaque: OpaqueSimValueSlice<'_>, + ) { + assert_eq!(self.type_properties().size(), opaque.size()); + assert_eq!(value.size(), opaque.size()); + value.clone_from_slice(opaque); + } + fn sim_value_to_opaque<'w>( + &self, + value: &Self::SimValue, + writer: OpaqueSimValueWriter<'w>, + ) -> OpaqueSimValueWritten<'w> { + assert_eq!(self.type_properties().size(), writer.size()); + assert_eq!(value.size(), writer.size()); + writer.fill_cloned_from_slice(value.as_slice()) + } +} + +#[derive(Clone, PartialEq, Eq, Hash, Debug, Default)] +pub struct EnumPaddingSimValue { + bits: Option, +} + +impl EnumPaddingSimValue { + pub const fn new() -> Self { + Self { bits: None } + } + pub fn bit_width(&self) -> Option { + self.bits.as_ref().map(UIntValue::width) + } + pub fn bits(&self) -> &Option { + &self.bits + } + pub fn bits_mut(&mut self) -> &mut Option { + &mut self.bits + } + pub fn into_bits(self) -> Option { + self.bits + } + pub fn from_bits(bits: Option) -> Self { + Self { bits } + } + pub fn from_bitslice(v: &BitSlice) -> Self { + Self { + bits: Some(UIntValue::new(Arc::new(v.to_bitvec()))), + } + } +} + +#[derive(Clone, PartialEq, Eq, Hash, Debug)] +pub struct UnknownVariantSimValue { + discriminant: usize, + body_bits: UIntValue, +} + +impl UnknownVariantSimValue { + pub fn discriminant(&self) -> usize { + self.discriminant + } + pub fn body_bits(&self) -> &UIntValue { + &self.body_bits + } + pub fn body_bits_mut(&mut self) -> &mut UIntValue { + &mut self.body_bits + } + pub fn into_body_bits(self) -> UIntValue { + self.body_bits + } + pub fn into_parts(self) -> (usize, UIntValue) { + (self.discriminant, self.body_bits) + } + pub fn new(discriminant: usize, body_bits: UIntValue) -> Self { + Self { + discriminant, + body_bits, + } + } +} + +pub struct EnumSimValueFromOpaque<'a> { + variants: Interned<[EnumVariant]>, + discriminant: usize, + body_bits: &'a BitSlice, +} + +impl<'a> EnumSimValueFromOpaque<'a> { + #[track_caller] + pub fn new(ty: T, opaque: OpaqueSimValueSlice<'a>) -> Self { + let variants = ty.variants(); + let size = EnumTypePropertiesBuilder::new() + .variants(variants) + .finish() + .size(); + assert!(size.only_bit_width().is_some()); + assert_eq!(size, opaque.size()); + let (discriminant_bits, body_bits) = opaque + .bits() + .split_at(discriminant_bit_width_impl(variants.len())); + let mut discriminant = 0usize; + discriminant.view_bits_mut::()[..discriminant_bits.len()] + .copy_from_bitslice(discriminant_bits); + Self { + variants, + discriminant, + body_bits, + } + } + pub fn discriminant(&self) -> usize { + self.discriminant + } + #[track_caller] + #[cold] + fn usage_error(&self, clone: bool) -> ! { + let clone = if clone { "clone_" } else { "" }; + match self.variants.get(self.discriminant) { + None => { + panic!("should have called EnumSimValueFromBits::unknown_variant_{clone}from_bits"); + } + Some(EnumVariant { ty: None, .. }) => { + panic!( + "should have called EnumSimValueFromBits::variant_no_field_{clone}from_bits" + ); + } + Some(EnumVariant { ty: Some(_), .. }) => { + panic!( + "should have called EnumSimValueFromBits::variant_with_field_{clone}from_bits" + ); + } + } + } + #[track_caller] + fn known_variant(&self, clone: bool) -> (Option, &'a BitSlice, &'a BitSlice) { + let Some(EnumVariant { ty, .. }) = self.variants.get(self.discriminant) else { + self.usage_error(clone); + }; + let variant_bit_width = ty.map_or(0, CanonicalType::bit_width); + let (variant_bits, padding_bits) = self.body_bits.split_at(variant_bit_width); + (*ty, variant_bits, padding_bits) + } + #[track_caller] + pub fn unknown_variant_from_opaque(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_opaque(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_opaque(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_opaque(self) -> (SimValue, EnumPaddingSimValue) { + let (Some(variant_ty), variant_bits, padding_bits) = self.known_variant(false) else { + self.usage_error(false); + }; + ( + SimValue::from_bitslice(T::from_canonical(variant_ty), variant_bits), + EnumPaddingSimValue::from_bitslice(padding_bits), + ) + } + #[track_caller] + fn clone_padding_from_bits(padding: &mut EnumPaddingSimValue, padding_bits: &BitSlice) { + match padding.bits_mut() { + None => *padding = EnumPaddingSimValue::from_bitslice(padding_bits), + Some(padding) => { + assert_eq!(padding.width(), padding_bits.len()); + padding.bits_mut().copy_from_bitslice(padding_bits); + } + } + } + #[track_caller] + pub fn variant_no_field_clone_from_opaque(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_opaque( + self, + value: &mut SimValue, + padding: &mut EnumPaddingSimValue, + ) { + let (Some(variant_ty), variant_bits, padding_bits) = self.known_variant(true) else { + self.usage_error(true); + }; + assert_eq!(SimValue::ty(value), T::from_canonical(variant_ty)); + SimValue::bits_mut(value) + .bits_mut() + .copy_from_bitslice(variant_bits); + Self::clone_padding_from_bits(padding, padding_bits); + } +} + +pub struct EnumSimValueToOpaque<'a> { + variants: Interned<[EnumVariant]>, + bit_width: usize, + discriminant_bit_width: usize, + writer: OpaqueSimValueWriter<'a>, +} + +impl<'a> EnumSimValueToOpaque<'a> { + #[track_caller] + pub fn new(ty: T, writer: OpaqueSimValueWriter<'a>) -> Self { + let variants = ty.variants(); + let size = EnumTypePropertiesBuilder::new() + .variants(variants) + .finish() + .size(); + assert_eq!(size, writer.size()); + Self { + variants, + bit_width: size + .only_bit_width() + .expect("enums should only contain bits"), + discriminant_bit_width: discriminant_bit_width_impl(variants.len()), + writer, + } + } + #[track_caller] + fn write_discriminant(&mut self, mut discriminant: usize) { + let orig_discriminant = discriminant; + let discriminant_bits = + &mut discriminant.view_bits_mut::()[..self.discriminant_bit_width]; + self.writer.fill_prefix_with( + OpaqueSimValueSize::from_bit_width(self.discriminant_bit_width), + |writer| { + writer.fill_cloned_from_slice(OpaqueSimValueSlice::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_opaque( + mut self, + value: &UnknownVariantSimValue, + ) -> OpaqueSimValueWritten<'a> { + self.write_discriminant(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.writer + .fill_cloned_from_slice(OpaqueSimValueSlice::from_bitslice(value.body_bits.bits())) + } + #[track_caller] + fn known_variant( + mut self, + discriminant: usize, + value: Option<&OpaqueSimValue>, + padding: &EnumPaddingSimValue, + ) -> OpaqueSimValueWritten<'a> { + self.write_discriminant(discriminant); + let variant_ty = self.variants[discriminant].ty; + let variant_size = variant_ty.map_or(OpaqueSimValueSize::empty(), CanonicalType::size); + if let Some(value) = value { + if variant_ty.is_none() { + panic!("expected variant to have no field"); + } + self.writer.fill_prefix_with(variant_size, |writer| { + writer.fill_cloned_from_slice(value.as_slice()) + }); + } else if variant_ty.is_some() { + panic!("expected variant to have a field"); + } + if let Some(padding) = padding.bits() { + assert_eq!(padding.ty().type_properties().size(), self.writer.size()); + self.writer + .fill_cloned_from_slice(OpaqueSimValueSlice::from_bitslice(padding.bits())) + } else { + self.writer.fill_with_zeros() + } + } + #[track_caller] + pub fn variant_no_field_to_opaque( + self, + discriminant: usize, + padding: &EnumPaddingSimValue, + ) -> OpaqueSimValueWritten<'a> { + self.known_variant(discriminant, None, padding) + } + #[track_caller] + pub fn variant_with_field_to_opaque( + self, + discriminant: usize, + value: &SimValue, + padding: &EnumPaddingSimValue, + ) -> OpaqueSimValueWritten<'a> { + let Some(variant_ty) = self.variants[discriminant].ty else { + panic!("expected variant to have no field"); + }; + assert_eq!(SimValue::ty(value), T::from_canonical(variant_ty)); + self.known_variant(discriminant, Some(SimValue::opaque(value)), padding) + } +} + +#[doc(hidden)] +pub fn assert_is_enum_type(v: T) -> T { + v +} + +#[doc(hidden)] +pub fn enum_type_to_sim_builder(v: T) -> T::SimBuilder { + v.into() } #[hdl] @@ -360,6 +732,79 @@ pub enum HdlOption { HdlSome(T), } +impl, Rhs: Type> ExprPartialEq> for HdlOption { + #[hdl] + fn cmp_eq(lhs: Expr, rhs: Expr>) -> Expr { + #[hdl] + let cmp_eq = wire(); + #[hdl] + match lhs { + HdlSome(lhs) => + { + #[hdl] + match rhs { + HdlSome(rhs) => connect(cmp_eq, ExprPartialEq::cmp_eq(lhs, rhs)), + HdlNone => connect(cmp_eq, false), + } + } + HdlNone => + { + #[hdl] + match rhs { + HdlSome(_) => connect(cmp_eq, false), + HdlNone => connect(cmp_eq, true), + } + } + } + cmp_eq + } + + #[hdl] + fn cmp_ne(lhs: Expr, rhs: Expr>) -> Expr { + #[hdl] + let cmp_ne = wire(); + #[hdl] + match lhs { + HdlSome(lhs) => + { + #[hdl] + match rhs { + HdlSome(rhs) => connect(cmp_ne, ExprPartialEq::cmp_ne(lhs, rhs)), + HdlNone => connect(cmp_ne, true), + } + } + HdlNone => + { + #[hdl] + match rhs { + HdlSome(_) => connect(cmp_ne, true), + HdlNone => connect(cmp_ne, false), + } + } + } + cmp_ne + } +} + +impl, Rhs: Type> SimValuePartialEq> for HdlOption { + fn sim_value_eq(this: &SimValue, other: &SimValue>) -> bool { + type SimValueMatch = ::SimValue; + match (&**this, &**other) { + (SimValueMatch::::HdlNone(_), SimValueMatch::>::HdlNone(_)) => { + true + } + (SimValueMatch::::HdlSome(..), SimValueMatch::>::HdlNone(_)) + | (SimValueMatch::::HdlNone(_), SimValueMatch::>::HdlSome(..)) => { + false + } + ( + SimValueMatch::::HdlSome(l, _), + SimValueMatch::>::HdlSome(r, _), + ) => l == r, + } + } +} + #[allow(non_snake_case)] pub fn HdlNone() -> Expr> { HdlOption[T::TYPE].HdlNone() diff --git a/crates/fayalite/src/expr.rs b/crates/fayalite/src/expr.rs index f0008f4..89e60cd 100644 --- a/crates/fayalite/src/expr.rs +++ b/crates/fayalite/src/expr.rs @@ -13,9 +13,10 @@ use crate::{ intern::{Intern, Interned}, memory::{DynPortType, MemPort, PortType}, module::{ - transform::visit::{Fold, Folder, Visit, Visitor}, Instance, ModuleIO, + transform::visit::{Fold, Folder, Visit, Visitor}, }, + phantom_const::PhantomConst, reg::Reg, reset::{AsyncReset, Reset, ResetType, ResetTypeDispatch, SyncReset}, ty::{CanonicalType, StaticType, Type, TypeWithDeref}, @@ -109,6 +110,7 @@ expr_enum! { UIntLiteral(Interned), SIntLiteral(Interned), BoolLiteral(bool), + PhantomConst(PhantomConst), BundleLiteral(ops::BundleLiteral), ArrayLiteral(ops::ArrayLiteral), EnumLiteral(ops::EnumLiteral), @@ -272,6 +274,20 @@ pub struct Expr { impl fmt::Debug for Expr { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + #[cfg(debug_assertions)] + { + let Self { + __enum, + __ty, + __flow, + } = self; + let expr_ty = __ty.canonical(); + let enum_ty = __enum.to_expr().__ty; + assert_eq!( + expr_ty, enum_ty, + "expr ty mismatch:\nExpr {{\n__enum: {__enum:?},\n__ty: {__ty:?},\n__flow: {__flow:?}\n}}" + ); + } self.__enum.fmt(f) } } @@ -516,11 +532,7 @@ impl Flow { } } pub const fn flip_if(self, flipped: bool) -> Flow { - if flipped { - self.flip() - } else { - self - } + if flipped { self.flip() } else { self } } } @@ -698,6 +710,7 @@ impl CastToBits for T { } pub trait CastBitsTo { + #[track_caller] fn cast_bits_to(&self, ty: T) -> Expr; } @@ -755,3 +768,27 @@ pub fn repeat( ) .to_expr() } + +impl ToExpr for PhantomConst { + type Type = Self; + + fn to_expr(&self) -> Expr { + Expr { + __enum: ExprEnum::PhantomConst(self.canonical_phantom_const()).intern_sized(), + __ty: *self, + __flow: Flow::Source, + } + } +} + +impl GetTarget for PhantomConst { + fn target(&self) -> Option> { + None + } +} + +impl ToLiteralBits for PhantomConst { + fn to_literal_bits(&self) -> Result, NotALiteralExpr> { + Ok(Interned::default()) + } +} diff --git a/crates/fayalite/src/expr/ops.rs b/crates/fayalite/src/expr/ops.rs index 15c195e..b10e3ae 100644 --- a/crates/fayalite/src/expr/ops.rs +++ b/crates/fayalite/src/expr/ops.rs @@ -7,18 +7,19 @@ use crate::{ clock::{Clock, ToClock}, enum_::{Enum, EnumType, EnumVariant}, expr::{ + CastBitsTo as _, CastTo, CastToBits as _, Expr, ExprEnum, Flow, HdlPartialEq, + HdlPartialOrd, NotALiteralExpr, ReduceBits, ToExpr, ToLiteralBits, target::{ GetTarget, Target, TargetPathArrayElement, TargetPathBundleField, TargetPathDynArrayElement, TargetPathElement, }, - CastTo, Expr, ExprEnum, Flow, HdlPartialEq, HdlPartialOrd, NotALiteralExpr, ReduceBits, - ToExpr, ToLiteralBits, }, int::{ Bool, BoolOrIntType, DynSize, IntType, KnownSize, SInt, SIntType, SIntValue, Size, UInt, UIntType, UIntValue, }, intern::{Intern, Interned}, + phantom_const::{PhantomConst, PhantomConstValue}, reset::{ AsyncReset, Reset, ResetType, ResetTypeDispatch, SyncReset, ToAsyncReset, ToReset, ToSyncReset, @@ -1892,6 +1893,26 @@ impl ExprCastTo for Clock { } } +impl ExprCastTo<()> for PhantomConst { + fn cast_to(src: Expr, to_type: ()) -> Expr<()> { + src.cast_to_bits().cast_bits_to(to_type) + } +} + +impl ExprCastTo> for () { + fn cast_to(src: Expr, to_type: PhantomConst) -> Expr> { + src.cast_to_bits().cast_bits_to(to_type) + } +} + +impl ExprCastTo> + for PhantomConst +{ + fn cast_to(src: Expr, to_type: PhantomConst) -> Expr> { + src.cast_to_bits().cast_bits_to(to_type) + } +} + #[derive(Copy, Clone, PartialEq, Eq, Hash)] pub struct FieldAccess { base: Expr, @@ -1916,7 +1937,8 @@ impl FieldAccess { let field = Expr::ty(base).fields()[field_index]; let field_type = FieldType::from_canonical(field.ty); let literal_bits = base.to_literal_bits().map(|bits| { - bits[Expr::ty(base).field_offsets()[field_index]..][..field.ty.bit_width()].intern() + bits[Expr::ty(base).field_offsets()[field_index].bit_width..][..field.ty.bit_width()] + .intern() }); let target = base.target().map(|base| { Intern::intern_sized(base.join(TargetPathElement::intern_sized( @@ -2708,3 +2730,47 @@ impl ToExpr for Uninit { } } } + +pub trait ExprIntoIterator: Type { + type Item: Type; + type ExprIntoIter: Iterator>; + + fn expr_into_iter(e: Expr) -> Self::ExprIntoIter; +} + +impl IntoIterator for Expr { + type Item = Expr; + type IntoIter = T::ExprIntoIter; + + fn into_iter(self) -> Self::IntoIter { + T::expr_into_iter(self) + } +} + +impl IntoIterator for &'_ Expr { + type Item = Expr; + type IntoIter = T::ExprIntoIter; + + fn into_iter(self) -> Self::IntoIter { + T::expr_into_iter(*self) + } +} + +impl IntoIterator for &'_ mut Expr { + type Item = Expr; + type IntoIter = T::ExprIntoIter; + + fn into_iter(self) -> Self::IntoIter { + T::expr_into_iter(*self) + } +} + +pub trait ExprFromIterator: Type { + fn expr_from_iter>(iter: T) -> Expr; +} + +impl, A> FromIterator for Expr { + fn from_iter>(iter: T) -> Self { + This::expr_from_iter(iter) + } +} diff --git a/crates/fayalite/src/firrtl.rs b/crates/fayalite/src/firrtl.rs index ea76cf8..b4a1d14 100644 --- a/crates/fayalite/src/firrtl.rs +++ b/crates/fayalite/src/firrtl.rs @@ -11,37 +11,36 @@ use crate::{ clock::Clock, enum_::{Enum, EnumType, EnumVariant}, expr::{ + CastBitsTo, Expr, ExprEnum, ops::{self, VariantAccess}, target::{ Target, TargetBase, TargetPathArrayElement, TargetPathBundleField, TargetPathElement, }, - Expr, ExprEnum, }, formal::FormalKind, int::{Bool, DynSize, IntType, SIntValue, UInt, UIntValue}, intern::{Intern, Interned}, memory::{Mem, PortKind, PortName, ReadUnderWrite}, module::{ - transform::{ - simplify_enums::{simplify_enums, SimplifyEnumsError, SimplifyEnumsKind}, - simplify_memories::simplify_memories, - }, AnnotatedModuleIO, Block, ExternModuleBody, ExternModuleParameter, ExternModuleParameterValue, Module, ModuleBody, NameOptId, NormalModuleBody, Stmt, StmtConnect, StmtDeclaration, StmtFormal, StmtIf, StmtInstance, StmtMatch, StmtReg, StmtWire, + transform::{ + simplify_enums::{SimplifyEnumsError, SimplifyEnumsKind, simplify_enums}, + simplify_memories::simplify_memories, + }, }, reset::{AsyncReset, Reset, ResetType, SyncReset}, source_location::SourceLocation, - ty::{CanonicalType, Type}, + ty::{CanonicalType, OpaqueSimValueSize, Type}, util::{ - const_str_array_is_strictly_ascending, BitSliceWriteWithBase, DebugAsRawString, - GenericConstBool, + BitSliceWriteWithBase, DebugAsRawString, GenericConstBool, HashMap, HashSet, + const_str_array_is_strictly_ascending, }, }; use bitvec::slice::BitSlice; use clap::value_parser; -use hashbrown::{HashMap, HashSet}; use num_traits::Signed; use serde::Serialize; use std::{ @@ -58,6 +57,43 @@ use std::{ rc::Rc, }; +#[derive(Clone, Debug)] +#[non_exhaustive] +enum FirrtlError { + SimOnlyValuesAreNotPermitted, +} + +impl fmt::Display for FirrtlError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + FirrtlError::SimOnlyValuesAreNotPermitted => { + f.write_str("`SimOnlyValue`s are not permitted") + } + } + } +} + +impl std::error::Error for FirrtlError {} + +enum FirrtlOrWrappedError { + FirrtlError(FirrtlError), + WrappedError(WrappedError), +} + +impl From for FirrtlOrWrappedError { + fn from(value: FirrtlError) -> Self { + Self::FirrtlError(value) + } +} + +impl From for FirrtlOrWrappedError { + fn from(value: WrappedError) -> Self { + Self::WrappedError(value) + } +} + +type Result = std::result::Result; + struct EscapedString<'a> { value: &'a str, raw: bool, @@ -321,20 +357,20 @@ impl DefinitionsMap { map: Default::default(), } } - fn get_or_make<'a>( + fn get_or_make<'a, E>( &'a self, key: K, - make: impl FnOnce(&K, &'a RcDefinitions) -> (Ident, V), - ) -> (Ident, V) + make: impl FnOnce(&K, &'a RcDefinitions) -> Result<(Ident, V), E>, + ) -> Result<(Ident, V), E> where K: Hash + Eq, V: Clone, { if let Some(retval) = self.map.borrow().get(&key) { - return retval.clone(); + return Ok(retval.clone()); } - let value = make(&key, &self.definitions); - self.map.borrow_mut().entry(key).or_insert(value).clone() + let value = make(&key, &self.definitions)?; + Ok(self.map.borrow_mut().entry(key).or_insert(value).clone()) } } @@ -368,10 +404,10 @@ impl TypeState { self.next_type_name.set(id + 1); Ident(Intern::intern_owned(format!("Ty{id}"))) } - fn get_bundle_field(&mut self, ty: Bundle, name: Interned) -> Ident { - self.bundle_ns(ty).borrow_mut().get(name) + fn get_bundle_field(&mut self, ty: Bundle, name: Interned) -> Result { + Ok(self.bundle_ns(ty)?.borrow_mut().get(name)) } - fn bundle_def(&self, ty: Bundle) -> (Ident, Rc>) { + fn bundle_def(&self, ty: Bundle) -> Result<(Ident, Rc>)> { self.bundle_defs.get_or_make(ty, |&ty, definitions| { let mut ns = Namespace::default(); let mut body = String::new(); @@ -384,21 +420,21 @@ impl TypeState { body.push_str("flip "); } write!(body, "{}: ", ns.get(name)).unwrap(); - body.push_str(&self.ty(ty)); + body.push_str(&self.ty(ty)?); } body.push('}'); let name = self.make_type_name(); definitions.add_definition_line(format_args!("type {name} = {body}")); - (name, Rc::new(RefCell::new(ns))) + Ok((name, Rc::new(RefCell::new(ns)))) }) } - fn bundle_ty(&self, ty: Bundle) -> Ident { - self.bundle_def(ty).0 + fn bundle_ty(&self, ty: Bundle) -> Result { + Ok(self.bundle_def(ty)?.0) } - fn bundle_ns(&self, ty: Bundle) -> Rc> { - self.bundle_def(ty).1 + fn bundle_ns(&self, ty: Bundle) -> Result>> { + Ok(self.bundle_def(ty)?.1) } - fn enum_def(&self, ty: Enum) -> (Ident, Rc) { + fn enum_def(&self, ty: Enum) -> Result<(Ident, Rc)> { self.enum_defs.get_or_make(ty, |&ty, definitions| { let mut variants = Namespace::default(); let mut body = String::new(); @@ -410,33 +446,33 @@ impl TypeState { write!(body, "{}", variants.get(name)).unwrap(); if let Some(ty) = ty { body.push_str(": "); - body.push_str(&self.ty(ty)); + body.push_str(&self.ty(ty)?); } } body.push_str("|}"); let name = self.make_type_name(); definitions.add_definition_line(format_args!("type {name} = {body}")); - ( + Ok(( name, Rc::new(EnumDef { variants: RefCell::new(variants), body, }), - ) + )) }) } - fn enum_ty(&self, ty: Enum) -> Ident { - self.enum_def(ty).0 + fn enum_ty(&self, ty: Enum) -> Result { + Ok(self.enum_def(ty)?.0) } - fn get_enum_variant(&mut self, ty: Enum, name: Interned) -> Ident { - self.enum_def(ty).1.variants.borrow_mut().get(name) + fn get_enum_variant(&mut self, ty: Enum, name: Interned) -> Result { + Ok(self.enum_def(ty)?.1.variants.borrow_mut().get(name)) } - fn ty(&self, ty: T) -> String { - match ty.canonical() { - CanonicalType::Bundle(ty) => self.bundle_ty(ty).to_string(), - CanonicalType::Enum(ty) => self.enum_ty(ty).to_string(), + fn ty(&self, ty: T) -> Result { + Ok(match ty.canonical() { + CanonicalType::Bundle(ty) => self.bundle_ty(ty)?.to_string(), + CanonicalType::Enum(ty) => self.enum_ty(ty)?.to_string(), CanonicalType::Array(ty) => { - let mut retval = self.ty(ty.element()); + let mut retval = self.ty(ty.element())?; write!(retval, "[{}]", ty.len()).unwrap(); retval } @@ -447,7 +483,11 @@ impl TypeState { CanonicalType::AsyncReset(AsyncReset {}) => "AsyncReset".into(), CanonicalType::SyncReset(SyncReset {}) => "UInt<1>".into(), CanonicalType::Reset(Reset {}) => "Reset".into(), - } + CanonicalType::PhantomConst(_) => "{}".into(), + CanonicalType::DynSimOnly(_) => { + return Err(FirrtlError::SimOnlyValuesAreNotPermitted.into()); + } + }) } } @@ -483,6 +523,7 @@ trait WrappedFileBackendTrait { contents: String, ) -> Result<(), WrappedError>; fn simplify_enums_error(&mut self, error: SimplifyEnumsError) -> WrappedError; + fn firrtl_error(&mut self, error: FirrtlError) -> WrappedError; } struct WrappedFileBackend { @@ -545,6 +586,11 @@ impl WrappedFileBackendTrait for WrappedFileBackend { self.error = Err(error.into()); WrappedError } + + fn firrtl_error(&mut self, error: FirrtlError) -> WrappedError { + self.error = Err(self.file_backend.custom_error(Box::new(error))); + WrappedError + } } #[derive(Clone)] @@ -747,7 +793,10 @@ impl<'a> Exporter<'a> { } fn run(&mut self, top_module: Interned>) -> Result<(), WrappedError> { let mut contents = self.version(); - let circuit = self.circuit(top_module)?; + let circuit = self.circuit(top_module).map_err(|e| match e { + FirrtlOrWrappedError::FirrtlError(e) => self.file_backend.firrtl_error(e), + FirrtlOrWrappedError::WrappedError(e) => e, + })?; contents.push_str(&circuit); self.file_backend .write_top_fir_file(self.circuit_name.to_string(), contents) @@ -755,7 +804,7 @@ impl<'a> Exporter<'a> { fn version(&mut self) -> String { "FIRRTL version 3.2.0\n".to_string() } - fn circuit(&mut self, top_module: Interned>) -> Result { + fn circuit(&mut self, top_module: Interned>) -> Result { let indent = self.indent; self.add_module(top_module); let circuit_indent = indent.push(); @@ -785,9 +834,9 @@ impl<'a> Exporter<'a> { enum_ty: Enum, variant_name: Interned, variant_expr: Option, - ) -> String { - let (_, enum_def) = self.type_state.enum_def(enum_ty); - let variant_ident = self.type_state.get_enum_variant(enum_ty, variant_name); + ) -> Result { + let (_, enum_def) = self.type_state.enum_def(enum_ty)?; + let variant_ident = self.type_state.get_enum_variant(enum_ty, variant_name)?; let mut retval = enum_def.body.clone(); write!(retval, "({variant_ident}").unwrap(); if let Some(variant_expr) = variant_expr { @@ -795,7 +844,7 @@ impl<'a> Exporter<'a> { retval.push_str(&variant_expr); } retval.push(')'); - retval + Ok(retval) } fn uint_literal(&mut self, value: &UIntValue) -> String { format!( @@ -824,32 +873,32 @@ impl<'a> Exporter<'a> { to_ty: ToTy, definitions: &RcDefinitions, const_ty: bool, - ) -> String { + ) -> Result { let from_ty = Expr::ty(value); - let mut value = self.expr(Expr::canonical(value), definitions, const_ty); + let mut value = self.expr(Expr::canonical(value), definitions, const_ty)?; if from_ty.width().checked_add(1) == Some(to_ty.width()) && !FromTy::Signed::VALUE && ToTy::Signed::VALUE { - format!("cvt({value})") + Ok(format!("cvt({value})")) } else if from_ty.width() <= to_ty.width() { // must pad before changing type to preserve value modulo 2^to_ty.width if from_ty.width() < to_ty.width() { value = format!("pad({value}, {})", to_ty.width()); } if FromTy::Signed::VALUE == ToTy::Signed::VALUE { - value + Ok(value) } else if ToTy::Signed::VALUE { - format!("asSInt({value})") + Ok(format!("asSInt({value})")) } else { - format!("asUInt({value})") + Ok(format!("asUInt({value})")) } } else { value = format!("tail({value}, {})", from_ty.width() - to_ty.width()); if ToTy::Signed::VALUE { - format!("asSInt({value})") + Ok(format!("asSInt({value})")) } else { - value + Ok(value) } } } @@ -859,12 +908,12 @@ impl<'a> Exporter<'a> { value: Expr, definitions: &RcDefinitions, const_ty: bool, - ) -> String { - let value = self.expr(Expr::canonical(value), definitions, const_ty); + ) -> Result { + let value = self.expr(Expr::canonical(value), definitions, const_ty)?; if let Some(firrtl_cast_fn) = firrtl_cast_fn { - format!("{firrtl_cast_fn}({value})") + Ok(format!("{firrtl_cast_fn}({value})")) } else { - value + Ok(value) } } fn slice( @@ -873,17 +922,17 @@ impl<'a> Exporter<'a> { range: Range, definitions: &RcDefinitions, const_ty: bool, - ) -> String { + ) -> Result { let base_width = Expr::ty(base).width(); - let base = self.expr(Expr::canonical(base), definitions, const_ty); + let base = self.expr(Expr::canonical(base), definitions, const_ty)?; if range.is_empty() { - format!("tail({base}, {base_width})") + Ok(format!("tail({base}, {base_width})")) } else { - format!( + Ok(format!( "bits({base}, {hi}, {lo})", hi = range.end - 1, lo = range.start, - ) + )) } } fn array_literal_expr( @@ -891,29 +940,29 @@ impl<'a> Exporter<'a> { expr: ops::ArrayLiteral, definitions: &RcDefinitions, const_ty: bool, - ) -> String { + ) -> Result { let ident = self.module.ns.make_new("_array_literal_expr"); - let ty_str = self.type_state.ty(expr.ty()); + let ty_str = self.type_state.ty(expr.ty())?; let const_ = if const_ty { "const " } else { "" }; definitions.add_definition_line(format_args!("wire {ident}: {const_}{ty_str}")); for (index, element) in expr.element_values().into_iter().enumerate() { - let element = self.expr(Expr::canonical(element), definitions, const_ty); + let element = self.expr(Expr::canonical(element), definitions, const_ty)?; definitions.add_definition_line(format_args!("connect {ident}[{index}], {element}")); } if expr.element_values().is_empty() { definitions.add_definition_line(format_args!("invalidate {ident}")); } - ident.to_string() + Ok(ident.to_string()) } fn bundle_literal_expr( &mut self, expr: ops::BundleLiteral, definitions: &RcDefinitions, const_ty: bool, - ) -> String { + ) -> Result { let ident = self.module.ns.make_new("_bundle_literal_expr"); let ty = expr.ty(); - let (ty_ident, bundle_ns) = self.type_state.bundle_def(ty); + let (ty_ident, bundle_ns) = self.type_state.bundle_def(ty)?; let const_ = if const_ty { "const " } else { "" }; definitions.add_definition_line(format_args!("wire {ident}: {const_}{ty_ident}")); for ( @@ -925,39 +974,43 @@ impl<'a> Exporter<'a> { }, ) in expr.field_values().into_iter().zip(ty.fields()) { - debug_assert!(!flipped, "can't have bundle literal with flipped field -- this should have been caught in BundleLiteral::new_unchecked"); + debug_assert!( + !flipped, + "can't have bundle literal with flipped field -- this should have been caught in BundleLiteral::new_unchecked" + ); let name = bundle_ns.borrow_mut().get(name); - let field_value = self.expr(Expr::canonical(field_value), definitions, const_ty); + let field_value = self.expr(Expr::canonical(field_value), definitions, const_ty)?; definitions.add_definition_line(format_args!("connect {ident}.{name}, {field_value}")); } if ty.fields().is_empty() { definitions.add_definition_line(format_args!("invalidate {ident}")); } - ident.to_string() + Ok(ident.to_string()) } fn uninit_expr( &mut self, expr: ops::Uninit, definitions: &RcDefinitions, const_ty: bool, - ) -> String { + ) -> Result { let ident = self.module.ns.make_new("_uninit_expr"); let ty = expr.ty(); - let ty_ident = self.type_state.ty(ty); + let ty_ident = self.type_state.ty(ty)?; let const_ = if const_ty { "const " } else { "" }; definitions.add_definition_line(format_args!("wire {ident}: {const_}{ty_ident}")); definitions.add_definition_line(format_args!("invalidate {ident}")); - ident.to_string() + Ok(ident.to_string()) } fn enum_literal_expr( &mut self, expr: ops::EnumLiteral, definitions: &RcDefinitions, const_ty: bool, - ) -> String { + ) -> Result { let variant_expr = expr .variant_value() - .map(|variant_value| self.expr(variant_value, definitions, const_ty)); + .map(|variant_value| self.expr(variant_value, definitions, const_ty)) + .transpose()?; self.enum_expr_impl(expr.ty(), expr.variant_name(), variant_expr) } fn expr_cast_bundle_to_bits( @@ -966,12 +1019,12 @@ impl<'a> Exporter<'a> { ty: Bundle, definitions: &RcDefinitions, extra_indent: Indent<'_>, - ) -> String { + ) -> Result { if ty.fields().is_empty() { - return "UInt<0>(0)".into(); + return Ok("UInt<0>(0)".into()); } if let [field] = *ty.fields() { - let field_ident = self.type_state.get_bundle_field(ty, field.name); + let field_ident = self.type_state.get_bundle_field(ty, field.name)?; return self.expr_cast_to_bits( format!("{value_str}.{field_ident}"), field.ty, @@ -990,23 +1043,23 @@ impl<'a> Exporter<'a> { ty: UInt[field_ty.bit_width()].canonical(), }, ))); - let (flattened_ty_ident, _) = self.type_state.bundle_def(flattened_bundle_ty); + let (flattened_ty_ident, _) = self.type_state.bundle_def(flattened_bundle_ty)?; let ident = self.module.ns.make_new("_cast_bundle_to_bits_expr"); definitions.add_definition_line(format_args!( "{extra_indent}wire {ident}: {flattened_ty_ident}" )); let mut cat_expr = None; for field in ty.fields() { - let field_ident = self.type_state.get_bundle_field(ty, field.name); + let field_ident = self.type_state.get_bundle_field(ty, field.name)?; let flattened_field_ident = self .type_state - .get_bundle_field(flattened_bundle_ty, field.name); + .get_bundle_field(flattened_bundle_ty, field.name)?; let field_bits = self.expr_cast_to_bits( format!("{value_str}.{field_ident}"), field.ty, definitions, extra_indent, - ); + )?; definitions.add_definition_line(format_args!( "{extra_indent}connect {ident}.{flattened_field_ident}, {field_bits}" )); @@ -1023,7 +1076,7 @@ impl<'a> Exporter<'a> { )); let cat_expr = cat_expr.expect("bundle already checked to have fields"); definitions.add_definition_line(format_args!("{extra_indent}connect {retval}, {cat_expr}")); - retval.to_string() + Ok(retval.to_string()) } fn expr_cast_enum_to_bits( &mut self, @@ -1031,9 +1084,9 @@ impl<'a> Exporter<'a> { ty: Enum, definitions: &RcDefinitions, extra_indent: Indent<'_>, - ) -> String { + ) -> Result { if ty.variants().is_empty() { - return "UInt<0>(0)".into(); + return Ok("UInt<0>(0)".into()); } let retval = self.module.ns.make_new("_cast_enum_to_bits_expr"); definitions.add_definition_line(format_args!( @@ -1050,7 +1103,7 @@ impl<'a> Exporter<'a> { .make_new(&format!("_cast_enum_to_bits_expr_{}", variant.name)); definitions.add_definition_line(format_args!( "{extra_indent}{}({variant_value}):", - self.type_state.get_enum_variant(ty, variant.name), + self.type_state.get_enum_variant(ty, variant.name)?, )); let _match_arm_indent = extra_indent.push(); let variant_bits = self.expr_cast_to_bits( @@ -1058,7 +1111,7 @@ impl<'a> Exporter<'a> { variant_ty, definitions, extra_indent, - ); + )?; definitions.add_definition_line(format_args!( "{extra_indent}connect {retval}, pad(cat({variant_bits}, UInt<{}>({variant_index})), {})", ty.discriminant_bit_width(), @@ -1067,7 +1120,7 @@ impl<'a> Exporter<'a> { } else { definitions.add_definition_line(format_args!( "{extra_indent}{}:", - self.type_state.get_enum_variant(ty, variant.name), + self.type_state.get_enum_variant(ty, variant.name)?, )); let _match_arm_indent = extra_indent.push(); definitions.add_definition_line(format_args!( @@ -1076,7 +1129,7 @@ impl<'a> Exporter<'a> { )); } } - retval.to_string() + Ok(retval.to_string()) } fn expr_cast_array_to_bits( &mut self, @@ -1084,9 +1137,9 @@ impl<'a> Exporter<'a> { ty: Array, definitions: &RcDefinitions, extra_indent: Indent<'_>, - ) -> String { + ) -> Result { if ty.is_empty() { - return "UInt<0>(0)".into(); + return Ok("UInt<0>(0)".into()); } if ty.len() == 1 { return self.expr_cast_to_bits( @@ -1109,7 +1162,7 @@ impl<'a> Exporter<'a> { ty.element(), definitions, extra_indent, - ); + )?; definitions.add_definition_line(format_args!( "{extra_indent}connect {ident}[{index}], {element_bits}" )); @@ -1126,7 +1179,7 @@ impl<'a> Exporter<'a> { )); let cat_expr = cat_expr.expect("array already checked to have elements"); definitions.add_definition_line(format_args!("{extra_indent}connect {retval}, {cat_expr}")); - retval.to_string() + Ok(retval.to_string()) } fn expr_cast_to_bits( &mut self, @@ -1134,7 +1187,7 @@ impl<'a> Exporter<'a> { ty: CanonicalType, definitions: &RcDefinitions, extra_indent: Indent<'_>, - ) -> String { + ) -> Result { match ty { CanonicalType::Bundle(ty) => { self.expr_cast_bundle_to_bits(value_str, ty, definitions, extra_indent) @@ -1146,12 +1199,16 @@ impl<'a> Exporter<'a> { self.expr_cast_array_to_bits(value_str, ty, definitions, extra_indent) } CanonicalType::UInt(_) | CanonicalType::SyncReset(_) | CanonicalType::Bool(_) => { - value_str + Ok(value_str) } CanonicalType::SInt(_) | CanonicalType::Clock(_) | CanonicalType::AsyncReset(_) - | CanonicalType::Reset(_) => format!("asUInt({value_str})"), + | CanonicalType::Reset(_) => Ok(format!("asUInt({value_str})")), + CanonicalType::PhantomConst(_) => Ok("UInt<0>(0)".into()), + CanonicalType::DynSimOnly(_) => { + Err(FirrtlError::SimOnlyValuesAreNotPermitted.into()) + } } } fn expr_cast_bits_to_bundle( @@ -1160,13 +1217,13 @@ impl<'a> Exporter<'a> { ty: Bundle, definitions: &RcDefinitions, extra_indent: Indent<'_>, - ) -> String { - let (ty_ident, _) = self.type_state.bundle_def(ty); + ) -> Result { + let (ty_ident, _) = self.type_state.bundle_def(ty)?; let retval = self.module.ns.make_new("_cast_bits_to_bundle_expr"); definitions.add_definition_line(format_args!("{extra_indent}wire {retval}: {ty_ident}")); if ty.fields().is_empty() { definitions.add_definition_line(format_args!("{extra_indent}invalidate {retval}")); - return retval.to_string(); + return Ok(retval.to_string()); } let flattened_bundle_ty = Bundle::new(Interned::from_iter(ty.fields().iter().map( |&BundleField { @@ -1179,7 +1236,7 @@ impl<'a> Exporter<'a> { ty: UInt[field_ty.bit_width()].canonical(), }, ))); - let (flattened_ty_ident, _) = self.type_state.bundle_def(flattened_bundle_ty); + let (flattened_ty_ident, _) = self.type_state.bundle_def(flattened_bundle_ty)?; let flattened_ident = self .module .ns @@ -1187,11 +1244,18 @@ impl<'a> Exporter<'a> { definitions.add_definition_line(format_args!( "{extra_indent}wire {flattened_ident}: {flattened_ty_ident}" )); - for (field, field_offset) in ty.fields().into_iter().zip(ty.field_offsets()) { + for ( + field, + OpaqueSimValueSize { + bit_width: field_offset, + sim_only_values_len: _, + }, + ) in ty.fields().into_iter().zip(ty.field_offsets()) + { let flattened_field_ident = self .type_state - .get_bundle_field(flattened_bundle_ty, field.name); - let field_ident = self.type_state.get_bundle_field(ty, field.name); + .get_bundle_field(flattened_bundle_ty, field.name)?; + let field_ident = self.type_state.get_bundle_field(ty, field.name)?; if let Some(field_bit_width_minus_one) = field.ty.bit_width().checked_sub(1usize) { definitions.add_definition_line(format_args!( "{extra_indent}connect {flattened_ident}.{flattened_field_ident}, bits({value_str}, {}, {field_offset})", @@ -1207,12 +1271,12 @@ impl<'a> Exporter<'a> { field.ty, definitions, extra_indent, - ); + )?; definitions.add_definition_line(format_args!( "{extra_indent}connect {retval}.{field_ident}, {field_value}" )); } - retval.to_string() + Ok(retval.to_string()) } fn expr_cast_bits_to_enum( &mut self, @@ -1220,19 +1284,19 @@ impl<'a> Exporter<'a> { ty: Enum, definitions: &RcDefinitions, extra_indent: Indent<'_>, - ) -> String { - let (ty_ident, enum_def) = self.type_state.enum_def(ty); + ) -> Result { + let (ty_ident, enum_def) = self.type_state.enum_def(ty)?; let retval = self.module.ns.make_new("_cast_bits_to_enum_expr"); definitions.add_definition_line(format_args!("{extra_indent}wire {retval}: {ty_ident}")); if ty.variants().is_empty() { definitions.add_definition_line(format_args!("{extra_indent}invalidate {retval}")); - return retval.to_string(); + return Ok(retval.to_string()); } if let [variant] = *ty.variants() { - let enum_variant = self.type_state.get_enum_variant(ty, variant.name); + let enum_variant = self.type_state.get_enum_variant(ty, variant.name)?; if let Some(variant_ty) = variant.ty { let variant_value = - self.expr_cast_bits_to(value_str, variant_ty, definitions, extra_indent); + self.expr_cast_bits_to(value_str, variant_ty, definitions, extra_indent)?; definitions.add_definition_line(format_args!( "{extra_indent}connect {retval}, {}({enum_variant}, {variant_value})", enum_def.body @@ -1243,7 +1307,7 @@ impl<'a> Exporter<'a> { enum_def.body )); } - return retval.to_string(); + return Ok(retval.to_string()); } let discriminant_bit_width = ty.discriminant_bit_width(); let body_bit_width = ty.type_properties().bit_width - discriminant_bit_width; @@ -1260,7 +1324,9 @@ impl<'a> Exporter<'a> { "UInt<0>(0)".into() }; for (variant_index, variant) in ty.variants().into_iter().enumerate() { - let when_cond = format!("eq(UInt<{discriminant_bit_width}>({variant_index}), tail({value_str}, {body_bit_width}))"); + let when_cond = format!( + "eq(UInt<{discriminant_bit_width}>({variant_index}), tail({value_str}, {body_bit_width}))" + ); if variant_index == ty.variants().len() - 1 { definitions.add_definition_line(format_args!("{extra_indent}else:")); } else if variant_index == 0 { @@ -1270,14 +1336,14 @@ impl<'a> Exporter<'a> { .add_definition_line(format_args!("{extra_indent}else when {when_cond}:")); } let when_pushed_indent = extra_indent.push(); - let enum_variant = self.type_state.get_enum_variant(ty, variant.name); + let enum_variant = self.type_state.get_enum_variant(ty, variant.name)?; if let Some(variant_ty) = variant.ty { let variant_value = self.expr_cast_bits_to( body_value.clone(), variant_ty, definitions, extra_indent, - ); + )?; definitions.add_definition_line(format_args!( "{extra_indent}connect {retval}, {}({enum_variant}, {variant_value})", enum_def.body @@ -1290,7 +1356,7 @@ impl<'a> Exporter<'a> { } drop(when_pushed_indent); } - retval.to_string() + Ok(retval.to_string()) } fn expr_cast_bits_to_array( &mut self, @@ -1298,14 +1364,14 @@ impl<'a> Exporter<'a> { ty: Array, definitions: &RcDefinitions, extra_indent: Indent<'_>, - ) -> String { + ) -> Result { let retval = self.module.ns.make_new("_cast_bits_to_array_expr"); - let array_ty = self.type_state.ty(ty); + let array_ty = self.type_state.ty(ty)?; definitions.add_definition_line(format_args!("{extra_indent}wire {retval}: {array_ty}")); let element_bit_width = ty.element().bit_width(); if ty.is_empty() || element_bit_width == 0 { definitions.add_definition_line(format_args!("{extra_indent}invalidate {retval}")); - return retval.to_string(); + return Ok(retval.to_string()); } let flattened_ident = self .module @@ -1326,12 +1392,12 @@ impl<'a> Exporter<'a> { ty.element(), definitions, extra_indent, - ); + )?; definitions.add_definition_line(format_args!( "{extra_indent}connect {retval}[{index}], {element_value}" )); } - retval.to_string() + Ok(retval.to_string()) } fn expr_cast_bits_to( &mut self, @@ -1339,7 +1405,7 @@ impl<'a> Exporter<'a> { ty: CanonicalType, definitions: &RcDefinitions, extra_indent: Indent<'_>, - ) -> String { + ) -> Result { match ty { CanonicalType::Bundle(ty) => { self.expr_cast_bits_to_bundle(value_str, ty, definitions, extra_indent) @@ -1350,13 +1416,22 @@ impl<'a> Exporter<'a> { CanonicalType::Array(ty) => { self.expr_cast_bits_to_array(value_str, ty, definitions, extra_indent) } - CanonicalType::UInt(_) => value_str, - CanonicalType::SInt(_) => format!("asSInt({value_str})"), - CanonicalType::Bool(_) => value_str, - CanonicalType::Clock(_) => format!("asClock({value_str})"), - CanonicalType::AsyncReset(_) => format!("asAsyncReset({value_str})"), - CanonicalType::SyncReset(_) => value_str, + CanonicalType::UInt(_) => Ok(value_str), + CanonicalType::SInt(_) => Ok(format!("asSInt({value_str})")), + CanonicalType::Bool(_) => Ok(value_str), + CanonicalType::Clock(_) => Ok(format!("asClock({value_str})")), + CanonicalType::AsyncReset(_) => Ok(format!("asAsyncReset({value_str})")), + CanonicalType::SyncReset(_) => Ok(value_str), CanonicalType::Reset(_) => unreachable!("Reset is not bit castable to"), + CanonicalType::PhantomConst(_) => { + let retval = self.module.ns.make_new("_cast_bits_to_phantom_const_expr"); + definitions.add_definition_line(format_args!("{extra_indent}wire {retval}: {{}}")); + definitions.add_definition_line(format_args!("{extra_indent}invalidate {retval}")); + return Ok(retval.to_string()); + } + CanonicalType::DynSimOnly(_) => { + Err(FirrtlError::SimOnlyValuesAreNotPermitted.into()) + } } } fn expr_unary( @@ -1365,11 +1440,11 @@ impl<'a> Exporter<'a> { arg: Expr, definitions: &RcDefinitions, const_ty: bool, - ) -> String { - format!( + ) -> Result { + Ok(format!( "{func}({arg})", - arg = self.expr(Expr::canonical(arg), definitions, const_ty) - ) + arg = self.expr(Expr::canonical(arg), definitions, const_ty)?, + )) } fn expr_binary( &mut self, @@ -1378,23 +1453,28 @@ impl<'a> Exporter<'a> { rhs: Expr, definitions: &RcDefinitions, const_ty: bool, - ) -> String { - format!( + ) -> Result { + Ok(format!( "{func}({lhs}, {rhs})", - lhs = self.expr(Expr::canonical(lhs), definitions, const_ty), - rhs = self.expr(Expr::canonical(rhs), definitions, const_ty) - ) + lhs = self.expr(Expr::canonical(lhs), definitions, const_ty)?, + rhs = self.expr(Expr::canonical(rhs), definitions, const_ty)?, + )) } fn expr( &mut self, expr: Expr, definitions: &RcDefinitions, const_ty: bool, - ) -> String { + ) -> Result { match *Expr::expr_enum(expr) { - ExprEnum::UIntLiteral(literal) => self.uint_literal(&literal), - ExprEnum::SIntLiteral(literal) => self.sint_literal(&literal), - ExprEnum::BoolLiteral(literal) => self.bool_literal(literal), + ExprEnum::UIntLiteral(literal) => Ok(self.uint_literal(&literal)), + ExprEnum::SIntLiteral(literal) => Ok(self.sint_literal(&literal)), + ExprEnum::BoolLiteral(literal) => Ok(self.bool_literal(literal)), + ExprEnum::PhantomConst(ty) => self.expr( + UInt[0].zero().cast_bits_to(ty.canonical()), + definitions, + const_ty, + ), ExprEnum::ArrayLiteral(array_literal) => { self.array_literal_expr(array_literal, definitions, const_ty) } @@ -1478,34 +1558,26 @@ impl<'a> Exporter<'a> { ExprEnum::DynShrS(expr) => { self.expr_binary("dshr", expr.lhs(), expr.rhs(), definitions, const_ty) } - ExprEnum::FixedShlU(expr) => { - format!( - "shl({lhs}, {rhs})", - lhs = self.expr(Expr::canonical(expr.lhs()), definitions, const_ty), - rhs = expr.rhs(), - ) - } - ExprEnum::FixedShlS(expr) => { - format!( - "shl({lhs}, {rhs})", - lhs = self.expr(Expr::canonical(expr.lhs()), definitions, const_ty), - rhs = expr.rhs(), - ) - } - ExprEnum::FixedShrU(expr) => { - format!( - "shr({lhs}, {rhs})", - lhs = self.expr(Expr::canonical(expr.lhs()), definitions, const_ty), - rhs = expr.rhs(), - ) - } - ExprEnum::FixedShrS(expr) => { - format!( - "shr({lhs}, {rhs})", - lhs = self.expr(Expr::canonical(expr.lhs()), definitions, const_ty), - rhs = expr.rhs(), - ) - } + ExprEnum::FixedShlU(expr) => Ok(format!( + "shl({lhs}, {rhs})", + lhs = self.expr(Expr::canonical(expr.lhs()), definitions, const_ty)?, + rhs = expr.rhs(), + )), + ExprEnum::FixedShlS(expr) => Ok(format!( + "shl({lhs}, {rhs})", + lhs = self.expr(Expr::canonical(expr.lhs()), definitions, const_ty)?, + rhs = expr.rhs(), + )), + ExprEnum::FixedShrU(expr) => Ok(format!( + "shr({lhs}, {rhs})", + lhs = self.expr(Expr::canonical(expr.lhs()), definitions, const_ty)?, + rhs = expr.rhs(), + )), + ExprEnum::FixedShrS(expr) => Ok(format!( + "shr({lhs}, {rhs})", + lhs = self.expr(Expr::canonical(expr.lhs()), definitions, const_ty)?, + rhs = expr.rhs(), + )), ExprEnum::CmpLtU(expr) => { self.expr_binary("lt", expr.lhs(), expr.rhs(), definitions, const_ty) } @@ -1579,7 +1651,7 @@ impl<'a> Exporter<'a> { self.slice(expr.base(), expr.range(), definitions, const_ty) } ExprEnum::CastToBits(expr) => { - let value_str = self.expr(expr.arg(), definitions, const_ty); + let value_str = self.expr(expr.arg(), definitions, const_ty)?; self.expr_cast_to_bits( value_str, Expr::ty(expr.arg()), @@ -1591,7 +1663,7 @@ impl<'a> Exporter<'a> { ) } ExprEnum::CastBitsTo(expr) => { - let value_str = self.expr(Expr::canonical(expr.arg()), definitions, const_ty); + let value_str = self.expr(Expr::canonical(expr.arg()), definitions, const_ty)?; self.expr_cast_bits_to( value_str, expr.ty(), @@ -1702,56 +1774,57 @@ impl<'a> Exporter<'a> { self.expr_unary("xorr", expr.arg(), definitions, const_ty) } ExprEnum::FieldAccess(expr) => { - let mut out = self.expr(Expr::canonical(expr.base()), definitions, const_ty); + let mut out = self.expr(Expr::canonical(expr.base()), definitions, const_ty)?; let name = self .type_state - .get_bundle_field(Expr::ty(expr.base()), expr.field_name()); + .get_bundle_field(Expr::ty(expr.base()), expr.field_name())?; write!(out, ".{name}").unwrap(); - out + Ok(out) } - ExprEnum::VariantAccess(variant_access) => self + ExprEnum::VariantAccess(variant_access) => Ok(self .module .match_arm_values .get(&variant_access) .expect("VariantAccess must be in its corresponding match arm") - .to_string(), + .to_string()), ExprEnum::ArrayIndex(expr) => { - let mut out = self.expr(Expr::canonical(expr.base()), definitions, const_ty); + let mut out = self.expr(Expr::canonical(expr.base()), definitions, const_ty)?; write!(out, "[{}]", expr.element_index()).unwrap(); - out + Ok(out) } ExprEnum::DynArrayIndex(expr) => { - let mut out = self.expr(Expr::canonical(expr.base()), definitions, const_ty); - let index = self.expr(Expr::canonical(expr.element_index()), definitions, const_ty); + let mut out = self.expr(Expr::canonical(expr.base()), definitions, const_ty)?; + let index = + self.expr(Expr::canonical(expr.element_index()), definitions, const_ty)?; write!(out, "[{index}]").unwrap(); - out + Ok(out) } - ExprEnum::ModuleIO(expr) => self.module.ns.get(expr.name_id()).to_string(), + ExprEnum::ModuleIO(expr) => Ok(self.module.ns.get(expr.name_id()).to_string()), ExprEnum::Instance(expr) => { assert!(!const_ty, "not a constant"); - self.module.ns.get(expr.scoped_name().1).to_string() + Ok(self.module.ns.get(expr.scoped_name().1).to_string()) } ExprEnum::Wire(expr) => { assert!(!const_ty, "not a constant"); - self.module.ns.get(expr.scoped_name().1).to_string() + Ok(self.module.ns.get(expr.scoped_name().1).to_string()) } ExprEnum::Reg(expr) => { assert!(!const_ty, "not a constant"); - self.module.ns.get(expr.scoped_name().1).to_string() + Ok(self.module.ns.get(expr.scoped_name().1).to_string()) } ExprEnum::RegSync(expr) => { assert!(!const_ty, "not a constant"); - self.module.ns.get(expr.scoped_name().1).to_string() + Ok(self.module.ns.get(expr.scoped_name().1).to_string()) } ExprEnum::RegAsync(expr) => { assert!(!const_ty, "not a constant"); - self.module.ns.get(expr.scoped_name().1).to_string() + Ok(self.module.ns.get(expr.scoped_name().1).to_string()) } ExprEnum::MemPort(expr) => { assert!(!const_ty, "not a constant"); let mem_name = self.module.ns.get(expr.mem_name().1); let port_name = Ident::from(expr.port_name()); - format!("{mem_name}.{port_name}") + Ok(format!("{mem_name}.{port_name}")) } } } @@ -1761,7 +1834,7 @@ impl<'a> Exporter<'a> { memory_name: Ident, array_type: Array, initial_value: Interned, - ) -> Result<(), WrappedError> { + ) -> Result<()> { assert_eq!( initial_value.len(), array_type.type_properties().bit_width, @@ -1843,7 +1916,7 @@ impl<'a> Exporter<'a> { }, }) } - fn annotation_target_ref(&mut self, target: Interned) -> AnnotationTargetRef { + fn annotation_target_ref(&mut self, target: Interned) -> Result { match *target { Target::Base(base) => { let mut segments = vec![]; @@ -1861,17 +1934,17 @@ impl<'a> Exporter<'a> { TargetBase::Wire(v) => self.module.ns.get(v.name_id()), TargetBase::Instance(v) => self.module.ns.get(v.name_id()), }; - AnnotationTargetRef { base, segments } + Ok(AnnotationTargetRef { base, segments }) } Target::Child(child) => { - let mut retval = self.annotation_target_ref(child.parent()); + let mut retval = self.annotation_target_ref(child.parent())?; match *child.path_element() { TargetPathElement::BundleField(TargetPathBundleField { name }) => { retval.segments.push(AnnotationTargetRefSegment::Field { name: self.type_state.get_bundle_field( Bundle::from_canonical(child.parent().canonical_ty()), name, - ), + )?, }) } TargetPathElement::ArrayElement(TargetPathArrayElement { index, .. }) => retval @@ -1879,7 +1952,7 @@ impl<'a> Exporter<'a> { .push(AnnotationTargetRefSegment::Index { index }), TargetPathElement::DynArrayElement(_) => unreachable!(), } - retval + Ok(retval) } } } @@ -1888,9 +1961,9 @@ impl<'a> Exporter<'a> { base_module: Ident, submodules: Vec, annotations: &[crate::annotations::TargetedAnnotation], - ) { + ) -> Result<()> { for annotation in annotations { - let target_ref = Some(self.annotation_target_ref(annotation.target())); + let target_ref = Some(self.annotation_target_ref(annotation.target())?); self.annotation( AnnotationTargetPath { base_module, @@ -1900,8 +1973,9 @@ impl<'a> Exporter<'a> { annotation.annotation(), ); } + Ok(()) } - fn write_mem(&mut self, module_name: Ident, memory: Mem) -> Result { + fn write_mem(&mut self, module_name: Ident, memory: Mem) -> Result { let indent = self.indent; let name_id = memory.scoped_name().1; let source_location = memory.source_location(); @@ -1925,11 +1999,11 @@ impl<'a> Exporter<'a> { annotation, ); } - self.targeted_annotations(module_name, vec![], &memory.port_annotations()); + self.targeted_annotations(module_name, vec![], &memory.port_annotations())?; if let Some(initial_value) = initial_value { self.write_mem_init(module_name, name, array_type, initial_value)?; } - let data_type = self.type_state.ty(array_type.element()); + let data_type = self.type_state.ty(array_type.element())?; let mut body = String::new(); writeln!( body, @@ -1972,16 +2046,16 @@ impl<'a> Exporter<'a> { module_name: Ident, definitions: &RcDefinitions, body: &mut String, - ) { + ) -> Result<()> { let StmtReg { annotations, reg } = stmt_reg; let indent = self.indent; - self.targeted_annotations(module_name, vec![], &annotations); + self.targeted_annotations(module_name, vec![], &annotations)?; let name = self.module.ns.get(reg.name_id()); - let ty = self.type_state.ty(reg.ty()); - let clk = self.expr(Expr::canonical(reg.clock_domain().clk), definitions, false); + let ty = self.type_state.ty(reg.ty())?; + let clk = self.expr(Expr::canonical(reg.clock_domain().clk), definitions, false)?; if let Some(init) = reg.init() { - let rst = self.expr(Expr::canonical(reg.clock_domain().rst), definitions, false); - let init = self.expr(init, definitions, false); + let rst = self.expr(Expr::canonical(reg.clock_domain().rst), definitions, false)?; + let init = self.expr(init, definitions, false)?; writeln!( body, "{indent}regreset {name}: {ty}, {clk}, {rst}, {init}{}", @@ -1996,6 +2070,7 @@ impl<'a> Exporter<'a> { ) .unwrap(); } + Ok(()) } fn block( &mut self, @@ -2003,7 +2078,7 @@ impl<'a> Exporter<'a> { block: Block, _block_indent: &PushIndent<'_>, definitions: Option, - ) -> Result { + ) -> Result { let indent = self.indent; let definitions = definitions.unwrap_or_default(); let mut body = String::new(); @@ -2029,8 +2104,8 @@ impl<'a> Exporter<'a> { ) .unwrap(); } - let lhs = self.expr(lhs, &definitions, false); - let rhs = self.expr(rhs, &definitions, false); + let lhs = self.expr(lhs, &definitions, false)?; + let rhs = self.expr(rhs, &definitions, false)?; writeln!( body, "{indent}connect {lhs}, {rhs}{}", @@ -2046,9 +2121,9 @@ impl<'a> Exporter<'a> { text, source_location, }) => { - let clk = self.expr(Expr::canonical(clk), &definitions, false); - let pred = self.expr(Expr::canonical(pred), &definitions, false); - let en = self.expr(Expr::canonical(en), &definitions, false); + let clk = self.expr(Expr::canonical(clk), &definitions, false)?; + let pred = self.expr(Expr::canonical(pred), &definitions, false)?; + let en = self.expr(Expr::canonical(en), &definitions, false)?; let kind = match kind { FormalKind::Assert => "assert", FormalKind::Assume => "assume", @@ -2073,7 +2148,7 @@ impl<'a> Exporter<'a> { let mut when = "when"; let mut pushed_indent; loop { - let cond_str = self.expr(Expr::canonical(cond), &definitions, false); + let cond_str = self.expr(Expr::canonical(cond), &definitions, false)?; writeln!( body, "{indent}{when} {cond_str}:{}", @@ -2115,7 +2190,7 @@ impl<'a> Exporter<'a> { writeln!( body, "{indent}match {}:{}", - self.expr(Expr::canonical(expr), &definitions, false), + self.expr(Expr::canonical(expr), &definitions, false)?, FileInfo::new(source_location), ) .unwrap(); @@ -2127,7 +2202,7 @@ impl<'a> Exporter<'a> { write!( body, "{indent}{}", - self.type_state.get_enum_variant(enum_ty, variant.name), + self.type_state.get_enum_variant(enum_ty, variant.name)?, ) .unwrap(); let variant_access = if variant.ty.is_some() { @@ -2157,9 +2232,9 @@ impl<'a> Exporter<'a> { drop(match_arms_indent); } Stmt::Declaration(StmtDeclaration::Wire(StmtWire { annotations, wire })) => { - self.targeted_annotations(module_name, vec![], &annotations); + self.targeted_annotations(module_name, vec![], &annotations)?; let name = self.module.ns.get(wire.name_id()); - let ty = self.type_state.ty(wire.ty()); + let ty = self.type_state.ty(wire.ty())?; writeln!( body, "{indent}wire {name}: {ty}{}", @@ -2168,19 +2243,19 @@ impl<'a> Exporter<'a> { .unwrap(); } Stmt::Declaration(StmtDeclaration::Reg(stmt_reg)) => { - self.stmt_reg(stmt_reg, module_name, &definitions, &mut body); + self.stmt_reg(stmt_reg, module_name, &definitions, &mut body)?; } Stmt::Declaration(StmtDeclaration::RegSync(stmt_reg)) => { - self.stmt_reg(stmt_reg, module_name, &definitions, &mut body); + self.stmt_reg(stmt_reg, module_name, &definitions, &mut body)?; } Stmt::Declaration(StmtDeclaration::RegAsync(stmt_reg)) => { - self.stmt_reg(stmt_reg, module_name, &definitions, &mut body); + self.stmt_reg(stmt_reg, module_name, &definitions, &mut body)?; } Stmt::Declaration(StmtDeclaration::Instance(StmtInstance { annotations, instance, })) => { - self.targeted_annotations(module_name, vec![], &annotations); + self.targeted_annotations(module_name, vec![], &annotations)?; let name = self.module.ns.get(instance.name_id()); let instantiated = instance.instantiated(); self.add_module(instantiated); @@ -2199,7 +2274,7 @@ impl<'a> Exporter<'a> { } Ok(out) } - fn module(&mut self, module: Interned>) -> Result { + fn module(&mut self, module: Interned>) -> Result { self.module = ModuleState::default(); let indent = self.indent; let module_name = self.global_ns.get(module.name_id()); @@ -2220,9 +2295,9 @@ impl<'a> Exporter<'a> { module_io, } in module.module_io().iter() { - self.targeted_annotations(module_name, vec![], annotations); + self.targeted_annotations(module_name, vec![], annotations)?; let name = self.module.ns.get(module_io.name_id()); - let ty = self.type_state.ty(module_io.ty()); + let ty = self.type_state.ty(module_io.ty())?; if module_io.is_input() { writeln!( body, @@ -2245,6 +2320,7 @@ impl<'a> Exporter<'a> { ModuleBody::Extern(ExternModuleBody { verilog_name, parameters, + simulation: _, }) => { let verilog_name = Ident(verilog_name); writeln!(body, "{indent}defname = {verilog_name}").unwrap(); @@ -2296,6 +2372,7 @@ pub trait FileBackendTrait { type Error: From; type Path: AsRef + fmt::Debug + ?Sized; type PathBuf: AsRef + fmt::Debug; + fn custom_error(&self, error: Box) -> Self::Error; fn path_to_string(&mut self, path: &Self::Path) -> Result; fn write_mem_init_file( &mut self, @@ -2315,6 +2392,10 @@ impl FileBackendTrait for Box { type Path = T::Path; type PathBuf = T::PathBuf; + fn custom_error(&self, error: Box) -> Self::Error { + (**self).custom_error(error) + } + fn path_to_string(&mut self, path: &Self::Path) -> Result { (**self).path_to_string(path) } @@ -2342,6 +2423,10 @@ impl FileBackendTrait for &'_ mut T { type Path = T::Path; type PathBuf = T::PathBuf; + fn custom_error(&self, error: Box) -> Self::Error { + (**self).custom_error(error) + } + fn path_to_string(&mut self, path: &Self::Path) -> Result { (**self).path_to_string(path) } @@ -2387,6 +2472,10 @@ impl FileBackendTrait for FileBackend { type Path = Path; type PathBuf = PathBuf; + fn custom_error(&self, error: Box) -> Self::Error { + io::Error::new(io::ErrorKind::Other, error) + } + fn path_to_string(&mut self, path: &Self::Path) -> Result { path.to_str() .map(String::from) @@ -2553,6 +2642,10 @@ impl FileBackendTrait for TestBackend { type Path = str; type PathBuf = String; + fn custom_error(&self, error: Box) -> Self::Error { + TestBackendError(error.to_string()) + } + fn path_to_string(&mut self, path: &Self::Path) -> Result { self.step_error_after(&path)?; Ok(path.to_owned()) @@ -2608,7 +2701,7 @@ fn export_impl( indent_depth: &indent_depth, indent: " ", }, - seen_modules: HashSet::new(), + seen_modules: HashSet::default(), unwritten_modules: VecDeque::new(), global_ns, module: ModuleState::default(), diff --git a/crates/fayalite/src/int.rs b/crates/fayalite/src/int.rs index b49ca3f..7fa77ce 100644 --- a/crates/fayalite/src/int.rs +++ b/crates/fayalite/src/int.rs @@ -2,27 +2,55 @@ // See Notices.txt for copyright information use crate::{ + array::ArrayType, expr::{ - target::{GetTarget, Target}, Expr, NotALiteralExpr, ToExpr, ToLiteralBits, + target::{GetTarget, Target}, }, + hdl, intern::{Intern, Interned, Memoize}, + sim::value::{SimValue, ToSimValueWithType}, source_location::SourceLocation, - ty::{impl_match_variant_as_self, CanonicalType, StaticType, Type, TypeProperties}, - util::{interned_bit, ConstBool, ConstUsize, GenericConstBool, GenericConstUsize}, + ty::{ + CanonicalType, OpaqueSimValueSize, OpaqueSimValueSlice, OpaqueSimValueWriter, + OpaqueSimValueWritten, StaticType, Type, TypeProperties, impl_match_variant_as_self, + }, + util::{ConstBool, ConstUsize, GenericConstBool, GenericConstUsize, interned_bit, slice_range}, }; -use bitvec::{order::Lsb0, slice::BitSlice, vec::BitVec}; +use bitvec::{bits, order::Lsb0, slice::BitSlice, vec::BitVec, view::BitView}; use num_bigint::{BigInt, BigUint, Sign}; -use num_traits::{Signed, Zero}; +use num_traits::{One, Signed, Zero}; +use serde::{ + Deserialize, Deserializer, Serialize, Serializer, + de::{DeserializeOwned, Error, Visitor}, +}; use std::{ borrow::{BorrowMut, Cow}, fmt, marker::PhantomData, num::NonZero, - ops::{Bound, Index, Not, Range, RangeBounds, RangeInclusive}, + ops::{Index, Not, Range, RangeBounds, RangeInclusive}, + str::FromStr, sync::Arc, }; +mod uint_in_range; + +#[hdl] +pub type UIntInRangeType = uint_in_range::UIntInRangeType; + +#[hdl] +pub type UIntInRange = + UIntInRangeType, ConstUsize>; + +#[hdl] +pub type UIntInRangeInclusiveType = + uint_in_range::UIntInRangeInclusiveType; + +#[hdl] +pub type UIntInRangeInclusive = + UIntInRangeInclusiveType, ConstUsize>; + mod sealed { pub trait BoolOrIntTypeSealed {} pub trait SizeSealed {} @@ -49,6 +77,16 @@ pub trait KnownSize: + IntoIterator> + TryFrom>> + Into>>; + type ArraySimValue: AsRef<[SimValue]> + + AsMut<[SimValue]> + + BorrowMut<[SimValue]> + + 'static + + Clone + + std::fmt::Debug + + IntoIterator> + + TryFrom>> + + Into>> + + ToSimValueWithType>; } macro_rules! known_widths { @@ -60,6 +98,7 @@ macro_rules! known_widths { }> { const SIZE: Self = Self; type ArrayMatch = [Expr; Self::VALUE]; + type ArraySimValue = [SimValue; Self::VALUE]; } }; ([2 $($rest:tt)*] $($bits:literal)+) => { @@ -72,6 +111,7 @@ macro_rules! known_widths { impl KnownSize for ConstUsize<{2 $(* $rest)*}> { const SIZE: Self = Self; type ArrayMatch = [Expr; Self::VALUE]; + type ArraySimValue = [SimValue; Self::VALUE]; } }; } @@ -79,13 +119,31 @@ macro_rules! known_widths { known_widths!([2 2 2 2 2 2 2 2 2]); pub trait SizeType: - sealed::SizeTypeSealed + Copy + Ord + std::hash::Hash + std::fmt::Debug + Send + Sync + 'static + sealed::SizeTypeSealed + + Copy + + Ord + + std::hash::Hash + + std::fmt::Debug + + Send + + Sync + + 'static + + Serialize + + DeserializeOwned { type Size: Size; } pub trait Size: - sealed::SizeSealed + Copy + Ord + std::hash::Hash + std::fmt::Debug + Send + Sync + 'static + sealed::SizeSealed + + Copy + + Ord + + std::hash::Hash + + std::fmt::Debug + + Send + + Sync + + 'static + + Serialize + + DeserializeOwned { type ArrayMatch: AsRef<[Expr]> + AsMut<[Expr]> @@ -100,6 +158,16 @@ pub trait Size: + IntoIterator> + TryFrom>> + Into>>; + type ArraySimValue: AsRef<[SimValue]> + + AsMut<[SimValue]> + + BorrowMut<[SimValue]> + + 'static + + Clone + + std::fmt::Debug + + IntoIterator> + + TryFrom>> + + Into>> + + ToSimValueWithType>; const KNOWN_VALUE: Option; type SizeType: SizeType + Copy @@ -125,6 +193,7 @@ impl SizeType for usize { impl Size for DynSize { type ArrayMatch = Box<[Expr]>; + type ArraySimValue = Box<[SimValue]>; const KNOWN_VALUE: Option = None; type SizeType = usize; @@ -147,6 +216,7 @@ impl SizeType for T { impl Size for T { type ArrayMatch = ::ArrayMatch; + type ArraySimValue = ::ArraySimValue; const KNOWN_VALUE: Option = Some(T::VALUE); @@ -157,14 +227,309 @@ impl Size for T { } fn try_from_usize(v: usize) -> Option { - if v == T::VALUE { - Some(T::SIZE) + if v == T::VALUE { Some(T::SIZE) } else { None } + } +} + +#[derive(Clone, PartialEq, Eq, Debug)] +pub enum ParseIntValueError { + Empty, + InvalidDigit, + MissingDigits, + InvalidRadix, + MissingType, + InvalidType, + TypeMismatch { + parsed_signed: bool, + parsed_width: usize, + expected_signed: bool, + expected_width: usize, + }, + PosOverflow, + NegOverflow, + WidthOverflow, + MissingWidth, +} + +impl std::error::Error for ParseIntValueError {} + +impl fmt::Display for ParseIntValueError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str(match self { + Self::Empty => "can't parse integer from empty string", + Self::InvalidDigit => "invalid digit", + Self::MissingDigits => "missing digits", + Self::InvalidRadix => "invalid radix", + Self::MissingType => "missing type", + Self::InvalidType => "invalid type", + Self::TypeMismatch { + parsed_signed, + parsed_width, + expected_signed, + expected_width, + } => { + return write!( + f, + "type mismatch: parsed type {parsed_signed_str}{parsed_width}, \ + expected type {expected_signed_str}{expected_width}", + parsed_signed_str = if *parsed_signed { "i" } else { "u" }, + expected_signed_str = if *expected_signed { "i" } else { "u" }, + ); + } + Self::PosOverflow => "value too large to fit in type", + Self::NegOverflow => "value too small to fit in type", + Self::WidthOverflow => "width is too large", + Self::MissingWidth => "missing width", + }) + } +} + +fn parse_int_value( + s: &str, + type_is_signed: bool, + type_width: Option, + parse_type: bool, +) -> Result, ParseIntValueError> { + if !parse_type && type_width.is_none() { + return Err(ParseIntValueError::MissingWidth); + } + let mut s = s.trim(); + if s.is_empty() { + return Err(ParseIntValueError::Empty); + } + let negative = match s.bytes().next() { + Some(ch @ (b'+' | b'-')) => { + s = s[1..].trim_start(); + ch == b'-' + } + _ => false, + }; + let radix = match s.bytes().next() { + Some(b'0') => match s.bytes().nth(1) { + Some(b'x' | b'X') => { + s = &s[2..]; + 16 + } + Some(b'b' | b'B') => { + s = &s[2..]; + 2 + } + Some(b'o' | b'O') => { + s = &s[2..]; + 8 + } + _ => 10, + }, + Some(b'1'..=b'9') => 10, + _ => return Err(ParseIntValueError::InvalidDigit), + }; + let mut any_digits = false; + let digits_end = s + .as_bytes() + .iter() + .position(|&ch| { + if ch == b'_' { + false + } else if (ch as char).to_digit(radix).is_some() { + any_digits = true; + false + } else { + true + } + }) + .unwrap_or(s.len()); + let digits = &s[..digits_end]; + s = &s[digits_end..]; + if !any_digits { + return Err(ParseIntValueError::MissingDigits); + } + let width = if parse_type { + const HDL_PREFIX: &[u8] = b"hdl_"; + let mut missing_type = ParseIntValueError::MissingType; + if s.as_bytes() + .get(..HDL_PREFIX.len()) + .is_some_and(|bytes| bytes.eq_ignore_ascii_case(HDL_PREFIX)) + { + s = &s[HDL_PREFIX.len()..]; + missing_type = ParseIntValueError::InvalidType; + } + let signed = match s.bytes().next() { + Some(b'u' | b'U') => false, + Some(b'i' | b'I') => true, + Some(_) => return Err(ParseIntValueError::InvalidType), + None => return Err(missing_type), + }; + s = &s[1..]; + let mut width = 0usize; + let mut any_digits = false; + for ch in s.bytes() { + let digit = (ch as char) + .to_digit(10) + .ok_or(ParseIntValueError::InvalidDigit)?; + any_digits = true; + width = width + .checked_mul(10) + .and_then(|v| v.checked_add(digit as usize)) + .ok_or(ParseIntValueError::WidthOverflow)?; + } + if !any_digits { + return Err(ParseIntValueError::MissingDigits); + } + if width > ::MAX_BITS { + return Err(ParseIntValueError::WidthOverflow); + } + let expected_width = type_width.unwrap_or(width); + if type_is_signed != signed || expected_width != width { + let expected_width = type_width.unwrap_or(width); + return Err(ParseIntValueError::TypeMismatch { + parsed_signed: signed, + parsed_width: width, + expected_signed: type_is_signed, + expected_width, + }); + } + width + } else { + if !s.is_empty() { + return Err(ParseIntValueError::InvalidDigit); + } + type_width.expect("checked earlier") + }; + if !type_is_signed && negative { + return Err(ParseIntValueError::InvalidDigit); + } + if radix == 10 { + let mut value: BigInt = digits + .replace("_", "") + .parse() + .expect("checked that the digits are valid already"); + if negative { + value = -value; + } + let uint_value: UIntValue = UInt::new(width).from_bigint_wrapping(&value); + if value.is_zero() { + Ok(uint_value.into_bits()) } else { - None + for i in 0..width { + value.set_bit(i as u64, type_is_signed && negative); + } + if value.is_zero() { + Ok(uint_value.into_bits()) + } else if type_is_signed && negative { + if value.sign() == Sign::Minus && value.magnitude().is_one() { + Ok(uint_value.into_bits()) + } else { + Err(ParseIntValueError::NegOverflow) + } + } else { + Err(ParseIntValueError::PosOverflow) + } + } + } else { + let mut value = BitVec::repeat(false, width); + let bits_per_digit = match radix { + 2 => 1, + 8 => 3, + 16 => 4, + _ => unreachable!(), + }; + let mut digits = digits + .bytes() + .rev() + .filter_map(|ch| (ch as char).to_digit(radix)); + let overflow_error = if negative { + ParseIntValueError::NegOverflow + } else { + ParseIntValueError::PosOverflow + }; + for chunk in value.chunks_mut(bits_per_digit) { + if let Some(mut digit) = digits.next() { + let digit_bits = &mut digit.view_bits_mut::()[..chunk.len()]; + chunk.clone_from_bitslice(digit_bits); + digit_bits.fill(false); + if digit != 0 { + return Err(overflow_error); + } + } else { + break; + } + } + for digit in digits { + if digit != 0 { + return Err(overflow_error); + } + } + let negative_zero = if negative { + // negating a value happens in three regions: + // * the least-significant zeros, which are left as zeros + // * the least-significant one bit, which is left as a one bit + // * all the most-significant bits, which are inverted + // e.g.: + const { + let inp = 0b1010_1_000_u8; + let out = 0b0101_1_000_u8; + assert!(inp.wrapping_neg() == out); + }; + if let Some(first_one) = value.first_one() { + let most_significant_bits = &mut value[first_one + 1..]; + // modifies in-place despite using `Not::not` + let _ = Not::not(most_significant_bits); + false + } else { + true + } + } else { + false + }; + if !negative_zero && type_is_signed && negative != value[value.len() - 1] { + Err(overflow_error) + } else { + Ok(Arc::new(value)) } } } +fn deserialize_int_value<'de, D: Deserializer<'de>>( + deserializer: D, + type_is_signed: bool, + type_width: Option, +) -> Result, D::Error> { + struct IntValueVisitor { + type_is_signed: bool, + type_width: Option, + } + impl<'de> Visitor<'de> for IntValueVisitor { + type Value = Arc; + + fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str(if self.type_is_signed { + "SIntValue" + } else { + "UIntValue" + })?; + if let Some(type_width) = self.type_width { + write!(f, "<{type_width}>")?; + } + Ok(()) + } + + fn visit_str(self, v: &str) -> Result { + parse_int_value(v, self.type_is_signed, self.type_width, true).map_err(E::custom) + } + + fn visit_bytes(self, v: &[u8]) -> Result { + match std::str::from_utf8(v) { + Ok(v) => self.visit_str(v), + Err(_) => Err(Error::invalid_value(serde::de::Unexpected::Bytes(v), &self)), + } + } + } + deserializer.deserialize_str(IntValueVisitor { + type_is_signed, + type_width, + }) +} + macro_rules! impl_int { ($pretty_name:ident, $name:ident, $generic_name:ident, $value:ident, $SIGNED:literal) => { #[derive(Copy, Clone, PartialEq, Eq, Hash)] @@ -188,19 +553,14 @@ macro_rules! impl_int { pub const $name: $generic_name = $generic_name; impl $name { - pub fn new(width: Width::SizeType) -> Self { + pub const fn new(width: Width::SizeType) -> Self { Self { width } } pub fn width(self) -> usize { Width::as_usize(self.width) } pub fn type_properties(self) -> TypeProperties { - TypeProperties { - is_passive: true, - is_storable: true, - is_castable_from_bits: true, - bit_width: self.width(), - } + self.as_dyn_int().type_properties_dyn() } pub fn bits_from_bigint_wrapping(self, v: &BigInt) -> BitVec { BoolOrIntType::bits_from_bigint_wrapping(self, v) @@ -263,6 +623,12 @@ macro_rules! impl_int { } Expr::from_dyn_int(MemoizeBitsToExpr.get_cow(bits)) } + fn from_str_without_ty( + self, + s: &str, + ) -> Result::Err> { + parse_int_value(s, $SIGNED, Some(self.width()), false).map(Self::Value::new) + } } impl IntType for $name { @@ -270,12 +636,21 @@ macro_rules! impl_int { } impl $name { - pub fn new_dyn(width: usize) -> Self { + pub const fn new_dyn(width: usize) -> Self { Self { width } } pub fn bits_to_bigint(bits: &BitSlice) -> BigInt { ::bits_to_bigint(bits) } + pub const fn type_properties_dyn(self) -> TypeProperties { + TypeProperties { + is_passive: true, + is_storable: true, + is_castable_from_bits: true, + bit_width: self.width, + sim_only_values_len: 0, + } + } } impl $name { @@ -287,6 +662,7 @@ macro_rules! impl_int { impl Type for $name { type BaseType = $pretty_name; type MaskType = Bool; + type SimValue = $value; impl_match_variant_as_self!(); fn mask_type(&self) -> Self::MaskType { Bool @@ -297,7 +673,7 @@ macro_rules! impl_int { #[track_caller] fn from_canonical(canonical_type: CanonicalType) -> Self { let CanonicalType::$pretty_name(retval) = canonical_type else { - panic!("expected {}", stringify!($name)); + panic!("expected {}", stringify!($pretty_name)); }; $name { width: Width::from_usize(retval.width), @@ -306,20 +682,95 @@ macro_rules! impl_int { fn source_location() -> SourceLocation { SourceLocation::builtin() } + fn sim_value_from_opaque(&self, opaque: OpaqueSimValueSlice<'_>) -> Self::SimValue { + assert_eq!( + opaque.size(), + OpaqueSimValueSize::from_bit_width(self.width()) + ); + $value::new(Arc::new(opaque.bits().to_bitvec())) + } + fn sim_value_clone_from_opaque( + &self, + value: &mut Self::SimValue, + opaque: OpaqueSimValueSlice<'_>, + ) { + assert_eq!( + opaque.size(), + OpaqueSimValueSize::from_bit_width(self.width()) + ); + assert_eq!(value.width(), self.width()); + value.bits_mut().copy_from_bitslice(opaque.bits()); + } + fn sim_value_to_opaque<'w>( + &self, + value: &Self::SimValue, + writer: OpaqueSimValueWriter<'w>, + ) -> OpaqueSimValueWritten<'w> { + assert_eq!( + writer.size(), + OpaqueSimValueSize::from_bit_width(self.width()) + ); + assert_eq!(value.width(), self.width()); + writer.fill_cloned_from_slice(OpaqueSimValueSlice::from_bitslice(value.bits())) + } + } + + impl Default for $name { + fn default() -> Self { + Self::TYPE + } } impl StaticType for $name { const TYPE: Self = Self { width: Width::SIZE }; const MASK_TYPE: Self::MaskType = Bool; - const TYPE_PROPERTIES: TypeProperties = TypeProperties { - is_passive: true, - is_storable: true, - is_castable_from_bits: true, - bit_width: Width::VALUE, - }; + const TYPE_PROPERTIES: TypeProperties = $name { + width: Width::VALUE, + } + .type_properties_dyn(); const MASK_TYPE_PROPERTIES: TypeProperties = Bool::TYPE_PROPERTIES; } + impl Serialize for $name { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + self.canonical().serialize(serializer) + } + } + + impl<'de, Width: Size> Deserialize<'de> for $name { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let name = |width| -> String { + if let Some(width) = width { + format!("a {}<{width}>", stringify!($pretty_name)) + } else { + format!("a {}", stringify!($pretty_name)) + } + }; + match CanonicalType::deserialize(deserializer)? { + CanonicalType::$pretty_name(retval) => { + if let Some(width) = Width::try_from_usize(retval.width()) { + Ok($name { width }) + } else { + Err(Error::invalid_value( + serde::de::Unexpected::Other(&name(Some(retval.width()))), + &&*name(Width::KNOWN_VALUE), + )) + } + } + ty => Err(Error::invalid_value( + serde::de::Unexpected::Other(ty.as_serde_unexpected_str()), + &&*name(Width::KNOWN_VALUE), + )), + } + } + } + #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, Default)] pub struct $generic_name; @@ -337,7 +788,7 @@ macro_rules! impl_int { _phantom: PhantomData, } - impl fmt::Debug for $value { + impl fmt::Display for $value { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let value = self.to_bigint(); let (sign, magnitude) = value.into_parts(); @@ -351,15 +802,38 @@ macro_rules! impl_int { } } - impl PartialOrd for $value { - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) + impl fmt::Debug for $value { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Display::fmt(self, f) } } - impl Ord for $value { - fn cmp(&self, other: &Self) -> std::cmp::Ordering { - self.to_bigint().cmp(&other.to_bigint()) + impl std::str::FromStr for $value { + type Err = ParseIntValueError; + + fn from_str(s: &str) -> Result { + parse_int_value(s, $SIGNED, Width::KNOWN_VALUE, true).map(Self::new) + } + } + + impl Serialize for $value { + fn serialize(&self, serializer: S) -> Result { + self.to_string().serialize(serializer) + } + } + + impl<'de, Width: Size> Deserialize<'de> for $value { + fn deserialize>(deserializer: D) -> Result { + deserialize_int_value(deserializer, $SIGNED, Width::KNOWN_VALUE).map(Self::new) + } + } + + impl PartialOrd for $value { + fn partial_cmp(&self, other: &Self) -> Option { + if self.width() != other.width() { + return None; + } + Some(self.to_bigint().cmp(&other.to_bigint())) } } @@ -401,6 +875,9 @@ macro_rules! impl_int { pub fn bits(&self) -> &Arc { &self.bits } + pub fn bits_mut(&mut self) -> &mut BitSlice { + Arc::::make_mut(&mut self.bits) + } } impl ToLiteralBits for $value { @@ -442,6 +919,9 @@ macro_rules! impl_int { _phantom: PhantomData, } } + pub fn bitvec_mut(&mut self) -> &mut BitVec { + Arc::make_mut(&mut self.bits) + } } }; } @@ -455,6 +935,10 @@ impl UInt { let v: BigUint = v.into(); Self::new(v.bits().try_into().expect("too big")) } + /// gets the smallest `UInt` that fits `v` losslessly + pub const fn for_value_usize(v: usize) -> Self { + Self::new((usize::BITS - v.leading_zeros()) as usize) + } /// gets the smallest `UInt` that fits `r` losslessly, panics if `r` is empty #[track_caller] pub fn range(r: Range>) -> Self { @@ -465,6 +949,12 @@ impl UInt { } /// gets the smallest `UInt` that fits `r` losslessly, panics if `r` is empty #[track_caller] + pub const fn range_usize(r: Range) -> Self { + assert!(r.end != 0, "empty range"); + Self::range_inclusive_usize(r.start..=(r.end - 1)) + } + /// gets the smallest `UInt` that fits `r` losslessly, panics if `r` is empty + #[track_caller] pub fn range_inclusive(r: RangeInclusive>) -> Self { let (start, end) = r.into_inner(); let start: BigUint = start.into(); @@ -474,6 +964,16 @@ impl UInt { // so must not take more bits than `end` Self::for_value(end) } + /// gets the smallest `UInt` that fits `r` losslessly, panics if `r` is empty + #[track_caller] + pub const fn range_inclusive_usize(r: RangeInclusive) -> Self { + let start = *r.start(); + let end = *r.end(); + assert!(start <= end, "empty range"); + // no need to check `start`` since it's no larger than `end` + // so must not take more bits than `end` + Self::for_value_usize(end) + } } impl SInt { @@ -567,12 +1067,12 @@ impl_prim_int!(i64, SInt<64>); impl_prim_int!(i128, SInt<128>); impl_prim_int!( - /// for portability reasons, [`usize`] always translates to [`UInt<64>`] + /// for portability reasons, [`usize`] always translates to [`UInt<64>`][type@UInt] usize, UInt<64> ); impl_prim_int!( - /// for portability reasons, [`isize`] always translates to [`SInt<64>`] + /// for portability reasons, [`isize`] always translates to [`SInt<64>`][type@SInt] isize, SInt<64> ); @@ -580,14 +1080,17 @@ pub trait BoolOrIntType: Type + sealed::BoolOrIntTypeSealed { type Width: Size; type Signed: GenericConstBool; type Value: Clone - + Ord + + PartialOrd + + Eq + std::hash::Hash + fmt::Debug + + fmt::Display + Send + Sync + 'static + ToExpr - + Into; + + Into + + std::str::FromStr; fn width(self) -> usize; fn new(width: ::SizeType) -> Self; fn new_static() -> Self @@ -621,6 +1124,12 @@ pub trait BoolOrIntType: Type + sealed::BoolOrIntTypeSealed { let bitslice = &BitSlice::::from_slice(&bytes)[..width]; bits.clone_from_bitslice(bitslice); } + fn bits_equal_bigint_wrapping(v: &BigInt, bits: &BitSlice) -> bool { + bits.iter() + .by_vals() + .enumerate() + .all(|(bit_index, bit): (usize, bool)| v.bit(bit_index as u64) == bit) + } fn bits_to_bigint(bits: &BitSlice) -> BigInt { let sign_byte = if Self::Signed::VALUE && bits.last().as_deref().copied().unwrap_or(false) { 0xFF @@ -654,9 +1163,12 @@ pub trait BoolOrIntType: Type + sealed::BoolOrIntTypeSealed { bytes, bit_width, ))) } + fn from_str_without_ty(self, s: &str) -> Result::Err>; } -pub trait IntType: BoolOrIntType::Dyn> { +pub trait IntType: + BoolOrIntType::Dyn, Value: FromStr> +{ type Dyn: IntType; fn as_dyn_int(self) -> Self::Dyn { Self::new_dyn(self.width()) @@ -672,19 +1184,7 @@ pub trait IntType: BoolOrIntType::Dyn> { Self::Dyn::new(width) } fn slice_index_to_range>(self, index: I) -> Range { - let width = self.width(); - let start = match index.start_bound() { - Bound::Included(start) => *start, - Bound::Excluded(start) => *start + 1, - Bound::Unbounded => 0, - }; - let end = match index.end_bound() { - Bound::Included(end) => *end + 1, - Bound::Excluded(end) => *end, - Bound::Unbounded => width, - }; - assert!(start <= end && end <= width, "slice range out-of-range"); - start..end + slice_range(index, self.width()) } fn slice_and_shift>(self, index: I) -> (UInt, usize) { let range = self.slice_index_to_range(index); @@ -696,7 +1196,7 @@ pub trait IntType: BoolOrIntType::Dyn> { } } -#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, Default)] pub struct Bool; impl sealed::BoolOrIntTypeSealed for Bool {} @@ -728,6 +1228,10 @@ impl BoolOrIntType for Bool { assert_eq!(bits.len(), 1); bits[0] } + + fn from_str_without_ty(self, s: &str) -> Result::Err> { + FromStr::from_str(s) + } } impl Bool { @@ -742,6 +1246,7 @@ impl Bool { impl Type for Bool { type BaseType = Bool; type MaskType = Bool; + type SimValue = bool; impl_match_variant_as_self!(); fn mask_type(&self) -> Self::MaskType { Bool @@ -759,6 +1264,28 @@ impl Type for Bool { fn source_location() -> SourceLocation { SourceLocation::builtin() } + fn sim_value_from_opaque(&self, opaque: OpaqueSimValueSlice<'_>) -> Self::SimValue { + assert_eq!(opaque.size(), OpaqueSimValueSize::from_bit_width(1)); + opaque.bits()[0] + } + fn sim_value_clone_from_opaque( + &self, + value: &mut Self::SimValue, + opaque: OpaqueSimValueSlice<'_>, + ) { + assert_eq!(opaque.size(), OpaqueSimValueSize::from_bit_width(1)); + *value = opaque.bits()[0]; + } + fn sim_value_to_opaque<'w>( + &self, + value: &Self::SimValue, + writer: OpaqueSimValueWriter<'w>, + ) -> OpaqueSimValueWritten<'w> { + assert_eq!(writer.size(), OpaqueSimValueSize::from_bit_width(1)); + writer.fill_cloned_from_slice(OpaqueSimValueSlice::from_bitslice( + [bits![0], bits![1]][*value as usize], + )) + } } impl StaticType for Bool { @@ -769,6 +1296,7 @@ impl StaticType for Bool { is_storable: true, is_castable_from_bits: true, bit_width: 1, + sim_only_values_len: 0, }; const MASK_TYPE_PROPERTIES: TypeProperties = Bool::TYPE_PROPERTIES; } @@ -783,6 +1311,13 @@ impl ToLiteralBits for bool { mod tests { use super::*; + #[test] + fn test_different_value_widths_compare_ne() { + // interning relies on [SU]IntValue with different `width` comparing not equal + assert_ne!(UInt[3].from_int_wrapping(0), UInt[4].from_int_wrapping(0)); + assert_ne!(SInt[3].from_int_wrapping(0), SInt[4].from_int_wrapping(0)); + } + #[test] fn test_uint_for_value() { assert_eq!(UInt::for_value(0u8).width, 0); @@ -805,4 +1340,104 @@ mod tests { assert_eq!(SInt::for_value(3).width, 3); assert_eq!(SInt::for_value(4).width, 4); } + + #[test] + fn test_serde_round_trip() { + use serde_json::json; + #[track_caller] + fn check( + value: T, + expected: serde_json::Value, + ) { + assert_eq!(serde_json::to_value(&value).unwrap(), expected); + assert_eq!(value, T::deserialize(expected).unwrap()); + } + check(UInt[0], json! { { "UInt": { "width": 0 } } }); + check(UInt::<0>::TYPE, json! { { "UInt": { "width": 0 } } }); + check(UInt::<35>::TYPE, json! { { "UInt": { "width": 35 } } }); + check(SInt[0], json! { { "SInt": { "width": 0 } } }); + check(SInt::<0>::TYPE, json! { { "SInt": { "width": 0 } } }); + check(SInt::<35>::TYPE, json! { { "SInt": { "width": 35 } } }); + check(Bool, json! { "Bool" }); + check(UIntValue::from(0u8), json! { "0x0_u8" }); + check(SIntValue::from(-128i8), json! { "-0x80_i8" }); + check(UInt[3].from_int_wrapping(5), json! { "0x5_u3" }); + check(UInt[12].from_int_wrapping(0x1123), json! { "0x123_u12" }); + check(SInt[12].from_int_wrapping(0xFEE), json! { "-0x12_i12" }); + check(SInt[12].from_int_wrapping(0x7EE), json! { "0x7EE_i12" }); + } + + #[test] + fn test_deserialize() { + use serde_json::json; + #[track_caller] + fn check( + expected: Result, + input: serde_json::Value, + ) { + let mut error = String::new(); + let value = T::deserialize(input).map_err(|e| -> &str { + error = e.to_string(); + &error + }); + assert_eq!(value, expected); + } + check::>( + Err("invalid value: a UInt<2>, expected a UInt<0>"), + json! { { "UInt": { "width": 2 } } }, + ); + check::>( + Err("invalid value: a Bool, expected a UInt<0>"), + json! { "Bool" }, + ); + check::>( + Err("invalid value: a Bool, expected a SInt<0>"), + json! { "Bool" }, + ); + check::( + Err("invalid value: a Bool, expected a UInt"), + json! { "Bool" }, + ); + check::( + Err("invalid value: a Bool, expected a SInt"), + json! { "Bool" }, + ); + check::(Err("value too large to fit in type"), json! { "2_u1" }); + check::(Err("value too large to fit in type"), json! { "10_u1" }); + check::(Err("value too large to fit in type"), json! { "0x2_u1" }); + check::(Err("value too large to fit in type"), json! { "0b10_u1" }); + check::(Err("value too large to fit in type"), json! { "0o2_u1" }); + check::(Err("value too large to fit in type"), json! { "0o377_i8" }); + check::(Err("value too large to fit in type"), json! { "0o200_i8" }); + check(Ok(SInt[8].from_int_wrapping(i8::MAX)), json! { "0o177_i8" }); + check::(Err("value too small to fit in type"), json! { "-0o201_i8" }); + check::(Err("value too small to fit in type"), json! { "-0o377_i8" }); + check::(Err("value too small to fit in type"), json! { "-0o400_i8" }); + check::( + Err("value too small to fit in type"), + json! { "-0o4000_i8" }, + ); + check(Ok(UIntValue::from(0u8)), json! { "0_u8" }); + check(Ok(UIntValue::from(0u8)), json! { "0b0_u8" }); + check(Ok(UIntValue::from(0u8)), json! { "00_u8" }); + check(Ok(UIntValue::from(0u8)), json! { "0x0_u8" }); + check(Ok(UIntValue::from(0u8)), json! { "0o0_u8" }); + check(Ok(SIntValue::from(-128i8)), json! { "-0x000_80_i8" }); + check(Ok(SIntValue::from(-128i8)), json! { "-0o002_00_hdl_i8" }); + check(Ok(SIntValue::from(-128i8)), json! { "-0b1__000_0000_i8" }); + check(Ok(UInt[3].from_int_wrapping(5)), json! { " + 0x5_u3 " }); + check( + Ok(UInt[12].from_int_wrapping(0x1123)), + json! { "0x1_2_3_hdl_u12" }, + ); + check(Ok(SInt[12].from_int_wrapping(0xFEE)), json! { "-0x12_i12" }); + check( + Ok(SInt[12].from_int_wrapping(0x7EE)), + json! { " + \t0x7__E_e_i012\n" }, + ); + check(Ok(SInt[0].from_int_wrapping(0)), json! { "-0i0" }); + check(Ok(SInt[1].from_int_wrapping(0)), json! { "-0i1" }); + check(Ok(SInt[0].from_int_wrapping(0)), json! { "-0x0i0" }); + check(Ok(SInt[1].from_int_wrapping(0)), json! { "-0x0i1" }); + } } diff --git a/crates/fayalite/src/int/uint_in_range.rs b/crates/fayalite/src/int/uint_in_range.rs new file mode 100644 index 0000000..5ddd38c --- /dev/null +++ b/crates/fayalite/src/int/uint_in_range.rs @@ -0,0 +1,643 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// See Notices.txt for copyright information + +use crate::{ + bundle::{Bundle, BundleField, BundleType, BundleTypePropertiesBuilder, NoBuilder}, + expr::{ + CastBitsTo, CastTo, CastToBits, Expr, HdlPartialEq, HdlPartialOrd, + ops::{ExprCastTo, ExprPartialEq, ExprPartialOrd}, + }, + int::{Bool, DynSize, KnownSize, Size, SizeType, UInt, UIntType}, + intern::{Intern, Interned}, + phantom_const::PhantomConst, + sim::value::{SimValue, SimValuePartialEq, ToSimValueWithType}, + source_location::SourceLocation, + ty::{ + CanonicalType, OpaqueSimValueSlice, OpaqueSimValueWriter, OpaqueSimValueWritten, + StaticType, Type, TypeProperties, impl_match_variant_as_self, + }, +}; +use bitvec::{order::Lsb0, view::BitView}; +use serde::{ + Deserialize, Deserializer, Serialize, Serializer, + de::{Error, Visitor, value::UsizeDeserializer}, +}; +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_opaque(&self, opaque: OpaqueSimValueSlice<'_>) -> Self::SimValue { + Bool.sim_value_from_opaque(opaque) + } + + fn sim_value_clone_from_opaque( + &self, + value: &mut Self::SimValue, + opaque: OpaqueSimValueSlice<'_>, + ) { + Bool.sim_value_clone_from_opaque(value, opaque); + } + + fn sim_value_to_opaque<'w>( + &self, + value: &Self::SimValue, + writer: OpaqueSimValueWriter<'w>, + ) -> OpaqueSimValueWritten<'w> { + Bool.sim_value_to_opaque(value, writer) + } +} + +impl BundleType for UIntInRangeMaskType { + type Builder = NoBuilder; + type FilledBuilder = Expr; + + fn fields(&self) -> Interned<[BundleField]> { + let [value_name, range_name] = UINT_IN_RANGE_TYPE_FIELD_NAMES; + let Self { value, range } = self; + [ + BundleField { + name: value_name.intern(), + flipped: false, + ty: value.canonical(), + }, + BundleField { + name: range_name.intern(), + flipped: false, + ty: range.canonical(), + }, + ][..] + .intern() + } +} + +impl StaticType for UIntInRangeMaskType { + const TYPE: Self = Self { + value: Bool, + range: PhantomConstRangeMaskType::TYPE, + }; + const MASK_TYPE: Self::MaskType = Self::TYPE; + const TYPE_PROPERTIES: TypeProperties = BundleTypePropertiesBuilder::new() + .field(false, Bool::TYPE_PROPERTIES) + .field(false, PhantomConstRangeMaskType::TYPE_PROPERTIES) + .finish(); + const MASK_TYPE_PROPERTIES: TypeProperties = Self::TYPE_PROPERTIES; +} + +impl ToSimValueWithType for bool { + fn to_sim_value_with_type(&self, ty: UIntInRangeMaskType) -> SimValue { + SimValue::from_value(ty, *self) + } +} + +impl ExprCastTo for UIntInRangeMaskType { + fn cast_to(src: Expr, to_type: Bool) -> Expr { + src.cast_to_bits().cast_to(to_type) + } +} + +impl ExprCastTo for Bool { + fn cast_to(src: Expr, to_type: UIntInRangeMaskType) -> Expr { + src.cast_to_static::>().cast_bits_to(to_type) + } +} + +impl ExprPartialEq for UIntInRangeMaskType { + fn cmp_eq(lhs: Expr, rhs: Expr) -> Expr { + lhs.cast_to_bits().cmp_eq(rhs.cast_to_bits()) + } + fn cmp_ne(lhs: Expr, rhs: Expr) -> Expr { + lhs.cast_to_bits().cmp_ne(rhs.cast_to_bits()) + } +} + +impl SimValuePartialEq for UIntInRangeMaskType { + fn sim_value_eq(this: &SimValue, other: &SimValue) -> bool { + **this == **other + } +} + +type PhantomConstRangeMaskType = > as Type>::MaskType; + +#[derive(Default, Copy, Clone, Debug)] +struct RangeParseError; + +macro_rules! define_uint_in_range_type { + ( + $UIntInRange:ident, + $UIntInRangeType:ident, + $UIntInRangeTypeWithoutGenerics:ident, + $UIntInRangeTypeWithStart:ident, + $SerdeRange:ident, + $range_operator_str:literal, + |$uint_range_usize_start:ident, $uint_range_usize_end:ident| $uint_range_usize:expr, + ) => { + #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] + struct $SerdeRange { + start: Start::SizeType, + end: End::SizeType, + } + + impl Default for $SerdeRange { + fn default() -> Self { + Self { + start: Start::SIZE, + end: End::SIZE, + } + } + } + + impl std::str::FromStr for $SerdeRange { + type Err = RangeParseError; + + fn from_str(s: &str) -> Result { + let Some((start, end)) = s.split_once($range_operator_str) else { + return Err(RangeParseError); + }; + if start.is_empty() + || start.bytes().any(|b| !b.is_ascii_digit()) + || end.is_empty() + || end.bytes().any(|b| !b.is_ascii_digit()) + { + return Err(RangeParseError); + } + let start = start.parse().map_err(|_| RangeParseError)?; + let end = end.parse().map_err(|_| RangeParseError)?; + let retval = Self { start, end }; + if retval.is_empty() { + Err(RangeParseError) + } else { + Ok(retval) + } + } + } + + impl fmt::Display for $SerdeRange { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let Self { start, end } = *self; + write!( + f, + "{}{}{}", + Start::as_usize(start), + $range_operator_str, + End::as_usize(end), + ) + } + } + + impl Serialize for $SerdeRange { + fn serialize(&self, serializer: S) -> Result { + serializer.collect_str(self) + } + } + + impl<'de, Start: Size, End: Size> Deserialize<'de> for $SerdeRange { + fn deserialize>(deserializer: D) -> Result { + struct SerdeRangeVisitor(PhantomData<(Start, End)>); + impl<'de, Start: Size, End: Size> Visitor<'de> for SerdeRangeVisitor { + type Value = $SerdeRange; + + fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("a string with format \"")?; + if let Some(start) = Start::KNOWN_VALUE { + write!(f, "{start}")?; + } else { + f.write_str("")?; + }; + f.write_str($range_operator_str)?; + if let Some(end) = End::KNOWN_VALUE { + write!(f, "{end}")?; + } else { + f.write_str("")?; + }; + f.write_str("\" that is a non-empty range") + } + + fn visit_str(self, v: &str) -> Result { + let $SerdeRange:: { start, end } = + v.parse().map_err(|_| { + Error::invalid_value(serde::de::Unexpected::Str(v), &self) + })?; + let start = + Start::SizeType::deserialize(UsizeDeserializer::::new(start))?; + let end = End::SizeType::deserialize(UsizeDeserializer::::new(end))?; + Ok($SerdeRange { start, end }) + } + + fn visit_bytes(self, v: &[u8]) -> Result { + match std::str::from_utf8(v) { + Ok(v) => self.visit_str(v), + Err(_) => { + Err(Error::invalid_value(serde::de::Unexpected::Bytes(v), &self)) + } + } + } + } + deserializer.deserialize_str(SerdeRangeVisitor(PhantomData)) + } + } + + #[derive(Copy, Clone, PartialEq, Eq, Hash)] + pub struct $UIntInRangeType { + value: UInt, + range: PhantomConst<$SerdeRange>, + } + + impl $UIntInRangeType { + fn from_phantom_const_range(range: PhantomConst<$SerdeRange>) -> Self { + let $SerdeRange { start, end } = *range.get(); + let $uint_range_usize_start = Start::as_usize(start); + let $uint_range_usize_end = End::as_usize(end); + Self { + value: $uint_range_usize, + range, + } + } + pub fn new(start: Start::SizeType, end: End::SizeType) -> Self { + Self::from_phantom_const_range(PhantomConst::new( + $SerdeRange { start, end }.intern_sized(), + )) + } + } + + impl fmt::Debug for $UIntInRangeType { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let Self { value, range } = self; + let $SerdeRange { start, end } = *range.get(); + f.debug_struct(&format!( + "{}<{}, {}>", + stringify!($UIntInRange), + Start::as_usize(start), + End::as_usize(end), + )) + .field("value", value) + .finish_non_exhaustive() + } + } + + impl Type for $UIntInRangeType { + type BaseType = Bundle; + type MaskType = UIntInRangeMaskType; + type SimValue = usize; + impl_match_variant_as_self!(); + + fn mask_type(&self) -> Self::MaskType { + UIntInRangeMaskType::TYPE + } + + fn canonical(&self) -> CanonicalType { + CanonicalType::Bundle(Bundle::new(self.fields())) + } + + fn from_canonical(canonical_type: CanonicalType) -> Self { + let fields = Bundle::from_canonical(canonical_type).fields(); + let [ + BundleField { + name: value_name, + flipped: false, + ty: value, + }, + BundleField { + name: range_name, + flipped: false, + ty: range, + }, + ] = *fields + else { + panic!("expected {}", stringify!($UIntInRange)); + }; + assert_eq!([&*value_name, &*range_name], UINT_IN_RANGE_TYPE_FIELD_NAMES); + let value = UInt::from_canonical(value); + let range = PhantomConst::<$SerdeRange>::from_canonical(range); + let retval = Self::from_phantom_const_range(range); + assert_eq!(retval, Self { value, range }); + retval + } + + fn source_location() -> SourceLocation { + SourceLocation::builtin() + } + + fn sim_value_from_opaque(&self, opaque: OpaqueSimValueSlice<'_>) -> Self::SimValue { + assert_eq!(opaque.size(), self.value.type_properties().size()); + let mut retval = 0usize; + retval.view_bits_mut::()[..opaque.bit_width()] + .clone_from_bitslice(opaque.bits()); + retval + } + + fn sim_value_clone_from_opaque( + &self, + value: &mut Self::SimValue, + opaque: OpaqueSimValueSlice<'_>, + ) { + *value = self.sim_value_from_opaque(opaque); + } + + fn sim_value_to_opaque<'w>( + &self, + value: &Self::SimValue, + writer: OpaqueSimValueWriter<'w>, + ) -> OpaqueSimValueWritten<'w> { + writer.fill_cloned_from_slice(OpaqueSimValueSlice::from_bitslice( + &value.view_bits::()[..self.value.width()], + )) + } + } + + impl BundleType for $UIntInRangeType { + type Builder = NoBuilder; + type FilledBuilder = Expr; + + fn fields(&self) -> Interned<[BundleField]> { + let [value_name, range_name] = UINT_IN_RANGE_TYPE_FIELD_NAMES; + let Self { value, range } = self; + [ + BundleField { + name: value_name.intern(), + flipped: false, + ty: value.canonical(), + }, + BundleField { + name: range_name.intern(), + flipped: false, + ty: range.canonical(), + }, + ][..] + .intern() + } + } + + impl Default for $UIntInRangeType { + fn default() -> Self { + Self::TYPE + } + } + + impl StaticType for $UIntInRangeType { + const TYPE: Self = { + let $uint_range_usize_start = Start::VALUE; + let $uint_range_usize_end = End::VALUE; + Self { + value: $uint_range_usize, + range: PhantomConst::<$SerdeRange>::TYPE, + } + }; + const MASK_TYPE: Self::MaskType = UIntInRangeMaskType::TYPE; + const TYPE_PROPERTIES: TypeProperties = BundleTypePropertiesBuilder::new() + .field(false, Self::TYPE.value.type_properties_dyn()) + .field( + false, + PhantomConst::<$SerdeRange>::TYPE_PROPERTIES, + ) + .finish(); + const MASK_TYPE_PROPERTIES: TypeProperties = UIntInRangeMaskType::TYPE_PROPERTIES; + } + + impl ToSimValueWithType<$UIntInRangeType> for usize { + fn to_sim_value_with_type( + &self, + ty: $UIntInRangeType, + ) -> SimValue<$UIntInRangeType> { + SimValue::from_value(ty, *self) + } + } + + #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, Default)] + pub struct $UIntInRangeTypeWithoutGenerics; + + #[allow(non_upper_case_globals)] + pub const $UIntInRangeType: $UIntInRangeTypeWithoutGenerics = + $UIntInRangeTypeWithoutGenerics; + + impl Index for $UIntInRangeTypeWithoutGenerics { + type Output = $UIntInRangeTypeWithStart; + + fn index(&self, start: StartSize) -> &Self::Output { + Interned::into_inner($UIntInRangeTypeWithStart(start).intern_sized()) + } + } + + #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] + pub struct $UIntInRangeTypeWithStart(Start::SizeType); + + impl, End: Size> + Index for $UIntInRangeTypeWithStart + { + type Output = $UIntInRangeType; + + fn index(&self, end: EndSize) -> &Self::Output { + Interned::into_inner($UIntInRangeType::new(self.0, end).intern_sized()) + } + } + + impl ExprCastTo for $UIntInRangeType { + fn cast_to(src: Expr, to_type: UInt) -> Expr { + src.cast_to_bits().cast_to(to_type) + } + } + + impl ExprCastTo<$UIntInRangeType> for UInt { + fn cast_to( + src: Expr, + to_type: $UIntInRangeType, + ) -> Expr<$UIntInRangeType> { + src.cast_bits_to(to_type) + } + } + + impl + ExprPartialEq<$UIntInRangeType> + for $UIntInRangeType + { + fn cmp_eq( + lhs: Expr, + rhs: Expr<$UIntInRangeType>, + ) -> Expr { + lhs.cast_to_bits().cmp_eq(rhs.cast_to_bits()) + } + fn cmp_ne( + lhs: Expr, + rhs: Expr<$UIntInRangeType>, + ) -> Expr { + lhs.cast_to_bits().cmp_ne(rhs.cast_to_bits()) + } + } + + impl + ExprPartialOrd<$UIntInRangeType> + for $UIntInRangeType + { + fn cmp_lt( + lhs: Expr, + rhs: Expr<$UIntInRangeType>, + ) -> Expr { + lhs.cast_to_bits().cmp_lt(rhs.cast_to_bits()) + } + fn cmp_le( + lhs: Expr, + rhs: Expr<$UIntInRangeType>, + ) -> Expr { + lhs.cast_to_bits().cmp_le(rhs.cast_to_bits()) + } + fn cmp_gt( + lhs: Expr, + rhs: Expr<$UIntInRangeType>, + ) -> Expr { + lhs.cast_to_bits().cmp_gt(rhs.cast_to_bits()) + } + fn cmp_ge( + lhs: Expr, + rhs: Expr<$UIntInRangeType>, + ) -> Expr { + lhs.cast_to_bits().cmp_ge(rhs.cast_to_bits()) + } + } + + impl + SimValuePartialEq<$UIntInRangeType> + for $UIntInRangeType + { + fn sim_value_eq( + this: &SimValue, + other: &SimValue<$UIntInRangeType>, + ) -> bool { + **this == **other + } + } + + impl ExprPartialEq> + for $UIntInRangeType + { + fn cmp_eq(lhs: Expr, rhs: Expr>) -> Expr { + lhs.cast_to_bits().cmp_eq(rhs) + } + fn cmp_ne(lhs: Expr, rhs: Expr>) -> Expr { + lhs.cast_to_bits().cmp_ne(rhs) + } + } + + impl ExprPartialEq<$UIntInRangeType> + for UIntType + { + fn cmp_eq(lhs: Expr, rhs: Expr<$UIntInRangeType>) -> Expr { + lhs.cmp_eq(rhs.cast_to_bits()) + } + fn cmp_ne(lhs: Expr, rhs: Expr<$UIntInRangeType>) -> Expr { + lhs.cmp_ne(rhs.cast_to_bits()) + } + } + + impl ExprPartialOrd> + for $UIntInRangeType + { + fn cmp_lt(lhs: Expr, rhs: Expr>) -> Expr { + lhs.cast_to_bits().cmp_lt(rhs) + } + fn cmp_le(lhs: Expr, rhs: Expr>) -> Expr { + lhs.cast_to_bits().cmp_le(rhs) + } + fn cmp_gt(lhs: Expr, rhs: Expr>) -> Expr { + lhs.cast_to_bits().cmp_gt(rhs) + } + fn cmp_ge(lhs: Expr, rhs: Expr>) -> Expr { + lhs.cast_to_bits().cmp_ge(rhs) + } + } + + impl ExprPartialOrd<$UIntInRangeType> + for UIntType + { + fn cmp_lt(lhs: Expr, rhs: Expr<$UIntInRangeType>) -> Expr { + lhs.cmp_lt(rhs.cast_to_bits()) + } + fn cmp_le(lhs: Expr, rhs: Expr<$UIntInRangeType>) -> Expr { + lhs.cmp_le(rhs.cast_to_bits()) + } + fn cmp_gt(lhs: Expr, rhs: Expr<$UIntInRangeType>) -> Expr { + lhs.cmp_gt(rhs.cast_to_bits()) + } + fn cmp_ge(lhs: Expr, rhs: Expr<$UIntInRangeType>) -> Expr { + lhs.cmp_ge(rhs.cast_to_bits()) + } + } + }; +} + +define_uint_in_range_type! { + UIntInRange, + UIntInRangeType, + UIntInRangeTypeWithoutGenerics, + UIntInRangeTypeWithStart, + SerdeRange, + "..", + |start, end| UInt::range_usize(start..end), +} + +define_uint_in_range_type! { + UIntInRangeInclusive, + UIntInRangeInclusiveType, + UIntInRangeInclusiveTypeWithoutGenerics, + UIntInRangeInclusiveTypeWithStart, + SerdeRangeInclusive, + "..=", + |start, end| UInt::range_inclusive_usize(start..=end), +} + +impl SerdeRange { + fn is_empty(self) -> bool { + self.start >= self.end + } +} + +impl SerdeRangeInclusive { + fn is_empty(self) -> bool { + self.start > self.end + } +} diff --git a/crates/fayalite/src/intern.rs b/crates/fayalite/src/intern.rs index 3780ad3..af91f0a 100644 --- a/crates/fayalite/src/intern.rs +++ b/crates/fayalite/src/intern.rs @@ -1,9 +1,9 @@ // SPDX-License-Identifier: LGPL-3.0-or-later // See Notices.txt for copyright information #![allow(clippy::type_complexity)] -use crate::intern::type_map::TypeIdMap; +use crate::{intern::type_map::TypeIdMap, util::DefaultBuildHasher}; use bitvec::{ptr::BitPtr, slice::BitSlice, vec::BitVec}; -use hashbrown::{hash_map::RawEntryMut, HashMap, HashTable}; +use hashbrown::HashTable; use serde::{Deserialize, Serialize}; use std::{ any::{Any, TypeId}, @@ -17,7 +17,7 @@ use std::{ sync::{Mutex, RwLock}, }; -pub mod type_map; +mod type_map; pub trait LazyInternedTrait: Send + Sync + Any { fn get(&self) -> Interned; @@ -316,8 +316,13 @@ pub trait Intern: Any + Send + Sync { } } +struct InternerState { + table: HashTable<&'static T>, + hasher: DefaultBuildHasher, +} + pub struct Interner { - map: Mutex>, + state: Mutex>, } impl Interner { @@ -330,7 +335,10 @@ impl Interner { impl Default for Interner { fn default() -> Self { Self { - map: Default::default(), + state: Mutex::new(InternerState { + table: HashTable::new(), + hasher: Default::default(), + }), } } } @@ -341,17 +349,16 @@ impl Interner { alloc: F, value: Cow<'_, T>, ) -> Interned { - let mut map = self.map.lock().unwrap(); - let hasher = map.hasher().clone(); - let hash = hasher.hash_one(&*value); - let inner = match map.raw_entry_mut().from_hash(hash, |k| **k == *value) { - RawEntryMut::Occupied(entry) => *entry.key(), - RawEntryMut::Vacant(entry) => { - *entry - .insert_with_hasher(hash, alloc(value), (), |k| hasher.hash_one(&**k)) - .0 - } - }; + let mut state = self.state.lock().unwrap(); + let InternerState { table, hasher } = &mut *state; + let inner = *table + .entry( + hasher.hash_one(&*value), + |k| **k == *value, + |k| hasher.hash_one(&**k), + ) + .or_insert_with(|| alloc(value)) + .get(); Interned { inner } } } @@ -742,7 +749,7 @@ pub trait MemoizeGeneric: 'static + Send + Sync + Hash + Eq + Copy { fn get_cow(self, input: Self::InputCow<'_>) -> Self::Output { static TYPE_ID_MAP: TypeIdMap = TypeIdMap::new(); let map: &RwLock<( - hashbrown::hash_map::DefaultHashBuilder, + DefaultBuildHasher, HashTable<(Self, Self::InputOwned, Self::Output)>, )> = TYPE_ID_MAP.get_or_insert_default(); fn hash_eq_key<'a, 'b, T: MemoizeGeneric>( diff --git a/crates/fayalite/src/intern/type_map.rs b/crates/fayalite/src/intern/type_map.rs index 48433af..945116b 100644 --- a/crates/fayalite/src/intern/type_map.rs +++ b/crates/fayalite/src/intern/type_map.rs @@ -1,10 +1,8 @@ // SPDX-License-Identifier: LGPL-3.0-or-later // See Notices.txt for copyright information -use hashbrown::HashMap; use std::{ any::{Any, TypeId}, hash::{BuildHasher, Hasher}, - ptr::NonNull, sync::RwLock, }; @@ -75,59 +73,36 @@ impl BuildHasher for TypeIdBuildHasher { } } -struct Value(NonNull); - -impl Value { - unsafe fn get_transmute_lifetime<'b>(&self) -> &'b (dyn Any + Send + Sync) { - unsafe { &*self.0.as_ptr() } - } - fn new(v: Box) -> Self { - unsafe { Self(NonNull::new_unchecked(Box::into_raw(v))) } - } -} - -unsafe impl Send for Value {} -unsafe impl Sync for Value {} - -impl Drop for Value { - fn drop(&mut self) { - unsafe { std::ptr::drop_in_place(self.0.as_ptr()) } - } -} - -pub struct TypeIdMap(RwLock>); +pub(crate) struct TypeIdMap( + RwLock>, +); impl TypeIdMap { - pub const fn new() -> Self { - Self(RwLock::new(HashMap::with_hasher(TypeIdBuildHasher))) + pub(crate) const fn new() -> Self { + Self(RwLock::new(hashbrown::HashMap::with_hasher( + TypeIdBuildHasher, + ))) } #[cold] - unsafe fn insert_slow( + fn insert_slow( &self, type_id: TypeId, make: fn() -> Box, - ) -> &(dyn Any + Sync + Send) { - let value = Value::new(make()); + ) -> &'static (dyn Any + Sync + Send) { + let value = Box::leak(make()); let mut write_guard = self.0.write().unwrap(); - unsafe { - write_guard - .entry(type_id) - .or_insert(value) - .get_transmute_lifetime() - } + *write_guard.entry(type_id).or_insert(value) } - pub fn get_or_insert_default(&self) -> &T { + pub(crate) fn get_or_insert_default(&self) -> &T { let type_id = TypeId::of::(); let read_guard = self.0.read().unwrap(); - let retval = read_guard - .get(&type_id) - .map(|v| unsafe { Value::get_transmute_lifetime(v) }); + let retval = read_guard.get(&type_id).map(|v| *v); drop(read_guard); let retval = match retval { Some(retval) => retval, - None => unsafe { self.insert_slow(type_id, move || Box::new(T::default())) }, + None => self.insert_slow(type_id, move || Box::new(T::default())), }; - unsafe { &*(retval as *const dyn Any as *const T) } + retval.downcast_ref().expect("known to have correct TypeId") } } diff --git a/crates/fayalite/src/lib.rs b/crates/fayalite/src/lib.rs index 88fe169..932464b 100644 --- a/crates/fayalite/src/lib.rs +++ b/crates/fayalite/src/lib.rs @@ -76,6 +76,8 @@ pub use fayalite_proc_macros::hdl_module; #[doc(inline)] pub use fayalite_proc_macros::hdl; +pub use bitvec; + /// struct used as a placeholder when applying defaults #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Default)] pub struct __; @@ -96,6 +98,7 @@ pub mod int; pub mod intern; pub mod memory; pub mod module; +pub mod phantom_const; pub mod prelude; pub mod reg; pub mod reset; diff --git a/crates/fayalite/src/memory.rs b/crates/fayalite/src/memory.rs index 2f0ec47..46eb59b 100644 --- a/crates/fayalite/src/memory.rs +++ b/crates/fayalite/src/memory.rs @@ -7,7 +7,7 @@ use crate::{ array::{Array, ArrayType}, bundle::{Bundle, BundleType}, clock::Clock, - expr::{ops::BundleLiteral, repeat, Expr, Flow, ToExpr, ToLiteralBits}, + expr::{Expr, Flow, ToExpr, ToLiteralBits, ops::BundleLiteral, repeat}, hdl, int::{Bool, DynSize, Size, UInt, UIntType}, intern::{Intern, Interned}, @@ -470,7 +470,7 @@ pub enum ReadUnderWrite { Undefined, } -#[derive(Copy, Clone, PartialEq, Eq, Hash)] +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] struct MemImpl { scoped_name: ScopedNameId, source_location: SourceLocation, @@ -1066,7 +1066,8 @@ pub fn splat_mask(ty: T, value: Expr) -> Expr> { | CanonicalType::SyncReset(_) | CanonicalType::Reset(_) | CanonicalType::Clock(_) - | CanonicalType::Enum(_) => Expr::from_canonical(Expr::canonical(value)), + | CanonicalType::Enum(_) + | CanonicalType::DynSimOnly(_) => Expr::from_canonical(Expr::canonical(value)), CanonicalType::Array(array) => Expr::from_canonical(Expr::canonical(repeat( splat_mask(array.element(), value), array.len(), @@ -1082,6 +1083,7 @@ pub fn splat_mask(ty: T, value: Expr) -> Expr> { ) .to_expr(), )), + CanonicalType::PhantomConst(_) => Expr::from_canonical(Expr::canonical(().to_expr())), } } diff --git a/crates/fayalite/src/module.rs b/crates/fayalite/src/module.rs index 5a18ac9..a81893d 100644 --- a/crates/fayalite/src/module.rs +++ b/crates/fayalite/src/module.rs @@ -8,12 +8,12 @@ use crate::{ clock::{Clock, ClockDomain}, enum_::{Enum, EnumMatchVariantsIter, EnumType}, expr::{ + Expr, Flow, ToExpr, ops::VariantAccess, target::{ GetTarget, Target, TargetBase, TargetPathArrayElement, TargetPathBundleField, TargetPathElement, }, - Expr, Flow, ToExpr, }, formal::FormalKind, int::{Bool, DynSize, Size}, @@ -21,18 +21,20 @@ use crate::{ memory::{Mem, MemBuilder, MemBuilderTarget, PortName}, reg::Reg, reset::{AsyncReset, Reset, ResetType, ResetTypeDispatch, SyncReset}, + sim::{ExternModuleSimGenerator, ExternModuleSimulation}, source_location::SourceLocation, ty::{CanonicalType, Type}, - util::ScopedRef, + util::{HashMap, HashSet, ScopedRef}, wire::{IncompleteWire, Wire}, }; -use hashbrown::{hash_map::Entry, HashMap, HashSet}; +use hashbrown::hash_map::Entry; use num_bigint::BigInt; use std::{ cell::RefCell, - collections::VecDeque, + collections::{BTreeMap, VecDeque}, convert::Infallible, fmt, + future::IntoFuture, hash::{Hash, Hasher}, iter::FusedIterator, marker::PhantomData, @@ -1081,6 +1083,7 @@ pub struct ExternModuleBody< > { pub verilog_name: Interned, pub parameters: P, + pub simulation: Option, } impl From>> for ExternModuleBody { @@ -1088,11 +1091,13 @@ impl From>> for ExternModuleBody { let ExternModuleBody { verilog_name, parameters, + simulation, } = value; let parameters = Intern::intern_owned(parameters); Self { verilog_name, parameters, + simulation, } } } @@ -1283,10 +1288,12 @@ impl fmt::Debug for DebugModuleBody { ModuleBody::Extern(ExternModuleBody { verilog_name, parameters, + simulation, }) => { debug_struct .field("verilog_name", verilog_name) - .field("parameters", parameters); + .field("parameters", parameters) + .field("simulation", simulation); } } debug_struct.finish_non_exhaustive() @@ -1452,7 +1459,9 @@ impl TargetState { }) .reduce(TargetWritten::conditional_merge_written) else { - unreachable!("merge_conditional_sub_blocks_into_block must be called with at least one sub-block"); + unreachable!( + "merge_conditional_sub_blocks_into_block must be called with at least one sub-block" + ); }; let mut written_in_blocks = written_in_blocks.borrow_mut(); if target_block >= written_in_blocks.len() { @@ -1490,6 +1499,9 @@ impl TargetState { }) .collect(), }, + CanonicalType::PhantomConst(_) => TargetStateInner::Decomposed { + subtargets: HashMap::default(), + }, CanonicalType::Array(ty) => TargetStateInner::Decomposed { subtargets: (0..ty.len()) .map(|index| { @@ -1512,7 +1524,8 @@ impl TargetState { | CanonicalType::Clock(_) | CanonicalType::AsyncReset(_) | CanonicalType::SyncReset(_) - | CanonicalType::Reset(_) => TargetStateInner::Single { + | CanonicalType::Reset(_) + | CanonicalType::DynSimOnly(_) => TargetStateInner::Single { declared_in_block, written_in_blocks: RefCell::default(), }, @@ -1758,6 +1771,7 @@ impl AssertValidityState { ModuleBody::Extern(ExternModuleBody { verilog_name: _, parameters: _, + simulation: _, }) => {} ModuleBody::Normal(NormalModuleBody { body }) => { let body = self.make_block_index(body); @@ -1779,12 +1793,49 @@ impl Module { pub fn new_unchecked( name_id: NameId, source_location: SourceLocation, - body: ModuleBody, + mut body: ModuleBody, module_io: impl IntoIterator, module_annotations: impl IntoAnnotations, ) -> Module { let module_io: Interned<[_]> = module_io.into_iter().collect(); let module_annotations = module_annotations.into_annotations().into_iter().collect(); + match &mut body { + ModuleBody::Normal(_) => {} + ModuleBody::Extern(ExternModuleBody { + simulation: Some(simulation), + .. + }) => { + if module_io.iter().any(|io| { + !simulation + .sim_io_to_generator_map + .contains_key(&io.module_io.intern()) + }) { + let mut sim_io_to_generator_map = + BTreeMap::clone(&simulation.sim_io_to_generator_map); + for io in module_io.iter() { + let io = io.module_io.intern(); + sim_io_to_generator_map.entry(io).or_insert(io); + } + simulation.sim_io_to_generator_map = sim_io_to_generator_map.intern_sized(); + } + if simulation.sim_io_to_generator_map.len() > module_io.len() { + // if sim_io_to_generator_map is bigger, then there must be a key that's not in module_io + let module_io_set = HashSet::from_iter(module_io.iter().map(|v| v.module_io)); + for (sim_io, generator_io) in simulation.sim_io_to_generator_map.iter() { + if !module_io_set.contains(&**sim_io) { + panic!( + "extern module has invalid `sim_io_to_generator_map`: key is not in containing module's `module_io`:\n\ + key={sim_io:?}\nvalue={generator_io:?}\nmodule location: {source_location}" + ); + } + } + unreachable!(); + } + } + ModuleBody::Extern(ExternModuleBody { + simulation: None, .. + }) => {} + } let retval = Module { name: name_id, source_location, @@ -1853,7 +1904,7 @@ impl Module { AssertValidityState { module: self.canonical(), blocks: vec![], - target_states: HashMap::with_capacity(64), + target_states: HashMap::with_capacity_and_hasher(64, Default::default()), } .assert_validity(); } @@ -2105,6 +2156,7 @@ impl ModuleBuilder { ModuleKind::Extern => ModuleBody::Extern(ExternModuleBody { verilog_name: name.0, parameters: vec![], + simulation: None, }), ModuleKind::Normal => ModuleBody::Normal(NormalModuleBody { body: BuilderModuleBody { @@ -2113,8 +2165,8 @@ impl ModuleBuilder { incomplete_declarations: vec![], stmts: vec![], }], - annotations_map: HashMap::new(), - memory_map: HashMap::new(), + annotations_map: HashMap::default(), + memory_map: HashMap::default(), }, }), }; @@ -2124,7 +2176,7 @@ impl ModuleBuilder { impl_: RefCell::new(ModuleBuilderImpl { body, io: vec![], - io_indexes: HashMap::new(), + io_indexes: HashMap::default(), module_annotations: vec![], }), }; @@ -2171,6 +2223,7 @@ impl ModuleBuilder { .builder_extern_body() .verilog_name = name.intern(); } + #[track_caller] pub fn parameter(&self, name: impl AsRef, value: ExternModuleParameterValue) { let name = name.as_ref(); self.impl_ @@ -2183,6 +2236,7 @@ impl ModuleBuilder { value, }); } + #[track_caller] pub fn parameter_int(&self, name: impl AsRef, value: impl Into) { let name = name.as_ref(); let value = value.into(); @@ -2196,6 +2250,7 @@ impl ModuleBuilder { value: ExternModuleParameterValue::Integer(value), }); } + #[track_caller] pub fn parameter_str(&self, name: impl AsRef, value: impl AsRef) { let name = name.as_ref(); let value = value.as_ref(); @@ -2209,6 +2264,7 @@ impl ModuleBuilder { value: ExternModuleParameterValue::String(value.intern()), }); } + #[track_caller] pub fn parameter_raw_verilog(&self, name: impl AsRef, raw_verilog: impl AsRef) { let name = name.as_ref(); let raw_verilog = raw_verilog.as_ref(); @@ -2222,6 +2278,26 @@ impl ModuleBuilder { value: ExternModuleParameterValue::RawVerilog(raw_verilog.intern()), }); } + #[track_caller] + pub fn extern_module_simulation(&self, generator: G) { + let mut impl_ = self.impl_.borrow_mut(); + let simulation = &mut impl_.body.builder_extern_body().simulation; + if simulation.is_some() { + panic!("already added an extern module simulation"); + } + *simulation = Some(ExternModuleSimulation::new(generator)); + } + #[track_caller] + pub fn extern_module_simulation_fn< + Args: fmt::Debug + Clone + Hash + Eq + Send + Sync + 'static, + Fut: IntoFuture + 'static, + >( + &self, + args: Args, + f: fn(Args, crate::sim::ExternModuleSimulationState) -> Fut, + ) { + self.extern_module_simulation(crate::sim::SimGeneratorFn { args, f }); + } } #[track_caller] @@ -2254,14 +2330,12 @@ pub fn annotate(target: Expr, annotations: impl IntoAnnotations) { } TargetBase::MemPort(v) => { ModuleBuilder::with(|m| { - RefCell::borrow_mut(unwrap!(unwrap!(m - .impl_ - .borrow_mut() - .body - .builder_normal_body_opt()) - .body - .memory_map - .get_mut(&v.mem_name()))) + RefCell::borrow_mut(unwrap!( + unwrap!(m.impl_.borrow_mut().body.builder_normal_body_opt()) + .body + .memory_map + .get_mut(&v.mem_name()) + )) .port_annotations .extend(annotations) }); diff --git a/crates/fayalite/src/module/transform/deduce_resets.rs b/crates/fayalite/src/module/transform/deduce_resets.rs index fe518a5..57197ad 100644 --- a/crates/fayalite/src/module/transform/deduce_resets.rs +++ b/crates/fayalite/src/module/transform/deduce_resets.rs @@ -6,29 +6,32 @@ use crate::{ bundle::{BundleField, BundleType}, enum_::{EnumType, EnumVariant}, expr::{ + ExprEnum, ops::{self, ArrayLiteral}, target::{ Target, TargetBase, TargetChild, TargetPathArrayElement, TargetPathBundleField, TargetPathDynArrayElement, TargetPathElement, }, - ExprEnum, }, formal::FormalKind, int::{SIntValue, UIntValue}, intern::{Intern, Interned, Memoize}, memory::{DynPortType, MemPort}, module::{ - AnnotatedModuleIO, Block, ExprInInstantiatedModule, ExternModuleBody, InstantiatedModule, - ModuleBody, ModuleIO, NameId, NormalModuleBody, Stmt, StmtConnect, StmtDeclaration, - StmtFormal, StmtIf, StmtInstance, StmtMatch, StmtReg, StmtWire, + AnnotatedModuleIO, Block, ExprInInstantiatedModule, ExternModuleBody, + ExternModuleParameter, InstantiatedModule, ModuleBody, ModuleIO, NameId, NormalModuleBody, + Stmt, StmtConnect, StmtDeclaration, StmtFormal, StmtIf, StmtInstance, StmtMatch, StmtReg, + StmtWire, }, prelude::*, reset::{ResetType, ResetTypeDispatch}, + sim::ExternModuleSimulation, + util::{HashMap, HashSet}, }; -use hashbrown::{hash_map::Entry, HashMap, HashSet}; +use hashbrown::hash_map::Entry; use num_bigint::BigInt; use petgraph::unionfind::UnionFind; -use std::{fmt, marker::PhantomData}; +use std::{collections::BTreeMap, fmt, marker::PhantomData}; #[derive(Debug)] pub enum DeduceResetsError { @@ -40,10 +43,16 @@ impl fmt::Display for DeduceResetsError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { DeduceResetsError::ResetIsNotDrivenByAsyncOrSync { source_location } => { - write!(f, "deduce_reset failed: Reset signal is not driven by any AsyncReset or SyncReset signals: {source_location}") + write!( + f, + "deduce_reset failed: Reset signal is not driven by any AsyncReset or SyncReset signals: {source_location}" + ) } DeduceResetsError::ResetIsDrivenByBothAsyncAndSync { source_location } => { - write!(f, "deduce_reset failed: Reset signal is driven by both AsyncReset and SyncReset signals: {source_location}") + write!( + f, + "deduce_reset failed: Reset signal is driven by both AsyncReset and SyncReset signals: {source_location}" + ) } } } @@ -155,6 +164,8 @@ impl ResetsLayout { CanonicalType::SyncReset(_) => ResetsLayout::SyncReset, CanonicalType::Reset(_) => ResetsLayout::Reset, CanonicalType::Clock(_) => ResetsLayout::NoResets, + CanonicalType::PhantomConst(_) => ResetsLayout::NoResets, + CanonicalType::DynSimOnly(_) => ResetsLayout::NoResets, } } } @@ -407,7 +418,9 @@ impl Resets { | CanonicalType::Bool(_) | CanonicalType::AsyncReset(_) | CanonicalType::SyncReset(_) - | CanonicalType::Clock(_) => Ok(self.ty), + | CanonicalType::Clock(_) + | CanonicalType::PhantomConst(_) + | CanonicalType::DynSimOnly(_) => Ok(self.ty), CanonicalType::Array(ty) => Ok(CanonicalType::Array(Array::new_dyn( self.array_elements().substituted_type( reset_graph, @@ -998,7 +1011,9 @@ fn cast_bit_op( CanonicalType::Array(_) | CanonicalType::Enum(_) | CanonicalType::Bundle(_) - | CanonicalType::Reset(_) => unreachable!(), + | CanonicalType::Reset(_) + | CanonicalType::PhantomConst(_) + | CanonicalType::DynSimOnly(_) => unreachable!(), $(CanonicalType::$Variant(ty) => Expr::expr_enum($arg.cast_to(ty)),)* } }; @@ -1010,6 +1025,8 @@ fn cast_bit_op( | CanonicalType::Enum(_) | CanonicalType::Bundle(_) | CanonicalType::Reset(_) => unreachable!(), + CanonicalType::PhantomConst(_) | + CanonicalType::DynSimOnly(_) => Expr::expr_enum(arg), $(CanonicalType::$Variant(_) => { let arg = Expr::<$Variant>::from_canonical(arg); match_expr_ty!(arg, UInt, SInt, Bool, AsyncReset, SyncReset, Clock) @@ -1040,6 +1057,7 @@ impl RunPass

for ExprEnum { ExprEnum::UIntLiteral(expr) => Ok(expr.run_pass(pass_args)?.map(ExprEnum::from)), ExprEnum::SIntLiteral(expr) => Ok(expr.run_pass(pass_args)?.map(ExprEnum::from)), ExprEnum::BoolLiteral(expr) => Ok(expr.run_pass(pass_args)?.map(ExprEnum::from)), + ExprEnum::PhantomConst(expr) => Ok(expr.run_pass(pass_args)?.map(ExprEnum::from)), ExprEnum::BundleLiteral(expr) => Ok(expr.run_pass(pass_args)?.map(ExprEnum::from)), ExprEnum::ArrayLiteral(expr) => Ok(expr.run_pass(pass_args)?.map(ExprEnum::from)), ExprEnum::EnumLiteral(expr) => Ok(expr.run_pass(pass_args)?.map(ExprEnum::from)), @@ -1670,7 +1688,9 @@ impl RunPassDispatch for AnyReg { | CanonicalType::Enum(_) | CanonicalType::Bundle(_) | CanonicalType::Reset(_) - | CanonicalType::Clock(_) => unreachable!(), + | CanonicalType::Clock(_) + | CanonicalType::PhantomConst(_) + | CanonicalType::DynSimOnly(_) => unreachable!(), } }) } @@ -1724,6 +1744,33 @@ impl RunPassDispatch for Instance { } } +impl RunPass

for ExternModuleSimulation { + fn run_pass( + &self, + mut pass_args: PassArgs<'_, P>, + ) -> Result, DeduceResetsError> { + let Self { + generator, + sim_io_to_generator_map, + source_location, + } = *self; + let sim_io_to_generator_map = Result::, P>, _>::from_iter( + sim_io_to_generator_map + .iter() + .map(|(sim_io, generator_io)| { + Ok(sim_io + .run_pass(pass_args.as_mut())? + .map(|v| (v, *generator_io))) + }), + )?; + Ok(sim_io_to_generator_map.map(|sim_io_to_generator_map| Self { + generator, + sim_io_to_generator_map: sim_io_to_generator_map.intern_sized(), + source_location, + })) + } +} + macro_rules! impl_run_pass_copy { ([$($generics:tt)*] $ty:ty) => { impl RunPass

for $ty { @@ -1751,6 +1798,7 @@ macro_rules! impl_run_pass_clone { } impl_run_pass_clone!([] BigInt); +impl_run_pass_clone!([] ExternModuleParameter); impl_run_pass_clone!([] SIntValue); impl_run_pass_clone!([] std::ops::Range); impl_run_pass_clone!([] UIntValue); @@ -1760,7 +1808,6 @@ impl_run_pass_copy!([] bool); impl_run_pass_copy!([] CustomFirrtlAnnotation); impl_run_pass_copy!([] DocStringAnnotation); impl_run_pass_copy!([] DontTouchAnnotation); -impl_run_pass_copy!([] ExternModuleBody); impl_run_pass_copy!([] Interned); impl_run_pass_copy!([] NameId); impl_run_pass_copy!([] SInt); @@ -1769,6 +1816,7 @@ impl_run_pass_copy!([] SVAttributeAnnotation); impl_run_pass_copy!([] UInt); impl_run_pass_copy!([] usize); impl_run_pass_copy!([] FormalKind); +impl_run_pass_copy!([] PhantomConst); macro_rules! impl_run_pass_for_struct { ( @@ -2020,6 +2068,14 @@ impl_run_pass_for_struct! { } } +impl_run_pass_for_struct! { + impl[] RunPass for ExternModuleBody { + verilog_name: _, + parameters: _, + simulation: _, + } +} + impl_run_pass_copy!([] MemPort); // Mem can't contain any `Reset` types impl_run_pass_copy!([] Mem); // Mem can't contain any `Reset` types @@ -2091,7 +2147,7 @@ impl RunPass

for StmtDeclaration { ) -> Result, DeduceResetsError> { let (annotations, reg) = match self { StmtDeclaration::Wire(v) => { - return Ok(v.run_pass(pass_args)?.map(StmtDeclaration::Wire)) + return Ok(v.run_pass(pass_args)?.map(StmtDeclaration::Wire)); } &StmtDeclaration::Reg(StmtReg { annotations, reg }) => (annotations, AnyReg::from(reg)), &StmtDeclaration::RegSync(StmtReg { annotations, reg }) => { @@ -2101,7 +2157,7 @@ impl RunPass

for StmtDeclaration { (annotations, AnyReg::from(reg)) } StmtDeclaration::Instance(v) => { - return Ok(v.run_pass(pass_args)?.map(StmtDeclaration::Instance)) + return Ok(v.run_pass(pass_args)?.map(StmtDeclaration::Instance)); } }; let annotations = annotations.run_pass(pass_args.as_mut())?; @@ -2244,9 +2300,9 @@ pub fn deduce_resets( fallback_to_sync_reset: bool, ) -> Result>, DeduceResetsError> { let mut state = State { - modules_added_to_graph: HashSet::new(), - substituted_modules: HashMap::new(), - expr_resets: HashMap::new(), + modules_added_to_graph: HashSet::default(), + substituted_modules: HashMap::default(), + expr_resets: HashMap::default(), reset_graph: ResetGraph::default(), fallback_to_sync_reset, }; diff --git a/crates/fayalite/src/module/transform/simplify_enums.rs b/crates/fayalite/src/module/transform/simplify_enums.rs index 4eb0d0c..ccdecf6 100644 --- a/crates/fayalite/src/module/transform/simplify_enums.rs +++ b/crates/fayalite/src/module/transform/simplify_enums.rs @@ -5,23 +5,23 @@ use crate::{ bundle::{Bundle, BundleField, BundleType}, enum_::{Enum, EnumType, EnumVariant}, expr::{ - ops::{self, EnumLiteral}, CastBitsTo, CastTo, CastToBits, Expr, ExprEnum, HdlPartialEq, ToExpr, + ops::{self, EnumLiteral}, }, hdl, int::UInt, intern::{Intern, Interned, Memoize}, memory::{DynPortType, Mem, MemPort}, module::{ - transform::visit::{Fold, Folder}, Block, Id, Module, NameId, ScopedNameId, Stmt, StmtConnect, StmtIf, StmtMatch, StmtWire, + transform::visit::{Fold, Folder}, }, source_location::SourceLocation, ty::{CanonicalType, Type}, + util::HashMap, wire::Wire, }; use core::fmt; -use hashbrown::HashMap; #[derive(Debug)] pub enum SimplifyEnumsError { @@ -69,7 +69,9 @@ fn contains_any_enum_types(ty: CanonicalType) -> bool { | CanonicalType::AsyncReset(_) | CanonicalType::SyncReset(_) | CanonicalType::Reset(_) - | CanonicalType::Clock(_) => false, + | CanonicalType::Clock(_) + | CanonicalType::PhantomConst(_) + | CanonicalType::DynSimOnly(_) => false, } } } @@ -512,7 +514,9 @@ impl State { | CanonicalType::AsyncReset(_) | CanonicalType::SyncReset(_) | CanonicalType::Reset(_) - | CanonicalType::Clock(_) => unreachable!(), + | CanonicalType::Clock(_) + | CanonicalType::PhantomConst(_) + | CanonicalType::DynSimOnly(_) => unreachable!(), } } } @@ -577,7 +581,9 @@ fn connect_port( | (CanonicalType::Clock(_), _) | (CanonicalType::AsyncReset(_), _) | (CanonicalType::SyncReset(_), _) - | (CanonicalType::Reset(_), _) => unreachable!( + | (CanonicalType::Reset(_), _) + | (CanonicalType::PhantomConst(_), _) + | (CanonicalType::DynSimOnly(_), _) => unreachable!( "trying to connect memory ports:\n{:?}\n{:?}", Expr::ty(lhs), Expr::ty(rhs), @@ -665,6 +671,7 @@ impl Folder for State { ExprEnum::UIntLiteral(_) | ExprEnum::SIntLiteral(_) | ExprEnum::BoolLiteral(_) + | ExprEnum::PhantomConst(_) | ExprEnum::BundleLiteral(_) | ExprEnum::ArrayLiteral(_) | ExprEnum::Uninit(_) @@ -806,7 +813,7 @@ impl Folder for State { .unwrap() .gen_name(&format!( "{}_{}", - memory.scoped_name().1 .0, + memory.scoped_name().1.0, port.port_name() )), port.source_location(), @@ -923,7 +930,9 @@ impl Folder for State { | CanonicalType::Clock(_) | CanonicalType::AsyncReset(_) | CanonicalType::SyncReset(_) - | CanonicalType::Reset(_) => canonical_type.default_fold(self), + | CanonicalType::Reset(_) + | CanonicalType::PhantomConst(_) + | CanonicalType::DynSimOnly(_) => canonical_type.default_fold(self), } } @@ -960,8 +969,8 @@ pub fn simplify_enums( kind: SimplifyEnumsKind, ) -> Result>, SimplifyEnumsError> { module.fold(&mut State { - enum_types: HashMap::new(), - replacement_mem_ports: HashMap::new(), + enum_types: HashMap::default(), + replacement_mem_ports: HashMap::default(), kind, module_state_stack: vec![], }) diff --git a/crates/fayalite/src/module/transform/simplify_memories.rs b/crates/fayalite/src/module/transform/simplify_memories.rs index e8f9cbf..35f186d 100644 --- a/crates/fayalite/src/module/transform/simplify_memories.rs +++ b/crates/fayalite/src/module/transform/simplify_memories.rs @@ -9,16 +9,15 @@ use crate::{ intern::{Intern, Interned}, memory::{Mem, MemPort, PortType}, module::{ - transform::visit::{Fold, Folder}, Block, Id, Module, NameId, ScopedNameId, Stmt, StmtConnect, StmtWire, + transform::visit::{Fold, Folder}, }, source_location::SourceLocation, ty::{CanonicalType, Type}, - util::MakeMutSlice, + util::{HashMap, MakeMutSlice}, wire::Wire, }; use bitvec::{slice::BitSlice, vec::BitVec}; -use hashbrown::HashMap; use std::{ convert::Infallible, fmt::Write, @@ -62,6 +61,7 @@ enum MemSplit { Bundle { fields: Rc<[MemSplit]>, }, + PhantomConst, Single { output_mem: Option, element_type: SingleType, @@ -76,6 +76,7 @@ impl MemSplit { fn mark_changed_element_type(self) -> Self { match self { MemSplit::Bundle { fields: _ } => self, + MemSplit::PhantomConst => self, MemSplit::Single { output_mem, element_type, @@ -97,6 +98,7 @@ impl MemSplit { .map(|field| Self::new(field.ty).mark_changed_element_type()) .collect(), }, + CanonicalType::PhantomConst(_) => MemSplit::PhantomConst, CanonicalType::Array(ty) => { let element = MemSplit::new(ty.element()); if let Self::Single { @@ -192,6 +194,7 @@ impl MemSplit { | CanonicalType::AsyncReset(_) | CanonicalType::SyncReset(_) | CanonicalType::Reset(_) => unreachable!("memory element type is a storable type"), + CanonicalType::DynSimOnly(_) => todo!("memory containing sim-only values"), } } } @@ -321,6 +324,9 @@ impl SplitMemState<'_, '_> { Expr::field(Expr::::from_canonical(e), &field.name) }, |initial_value_element| { + let Some(field_offset) = field_offset.only_bit_width() else { + todo!("memory containing sim-only values"); + }; &initial_value_element[field_offset..][..field_ty_bit_width] }, ); @@ -339,6 +345,7 @@ impl SplitMemState<'_, '_> { self.split_state_stack.pop(); } } + MemSplit::PhantomConst => {} MemSplit::Single { output_mem, element_type: single_type, @@ -538,7 +545,12 @@ impl ModuleState { }; loop { match input_element_type { - CanonicalType::Bundle(_) => unreachable!("bundle types are always split"), + CanonicalType::Bundle(_) => { + unreachable!("bundle types are always split") + } + CanonicalType::PhantomConst(_) => { + unreachable!("PhantomConst are always removed") + } CanonicalType::Enum(_) if input_array_types .first() @@ -612,6 +624,7 @@ impl ModuleState { | CanonicalType::AsyncReset(_) | CanonicalType::SyncReset(_) | CanonicalType::Reset(_) => unreachable!("memory element type is a storable type"), + CanonicalType::DynSimOnly(_) => todo!("memory containing sim-only values"), } break; } @@ -626,7 +639,7 @@ impl ModuleState { split_state: &SplitState<'_>, ) -> Mem { let mem_name = NameId( - Intern::intern_owned(format!("{}{mem_name_path}", input_mem.scoped_name().1 .0)), + Intern::intern_owned(format!("{}{mem_name_path}", input_mem.scoped_name().1.0)), Id::new(), ); let mem_name = ScopedNameId(input_mem.scoped_name().0, mem_name); @@ -743,7 +756,8 @@ impl ModuleState { .. } | MemSplit::Bundle { .. } - | MemSplit::Array { .. } => { + | MemSplit::Array { .. } + | MemSplit::PhantomConst => { let mut replacement_ports = Vec::with_capacity(input_mem.ports().len()); let mut wire_port_rdata = Vec::with_capacity(input_mem.ports().len()); let mut wire_port_wdata = Vec::with_capacity(input_mem.ports().len()); @@ -887,7 +901,7 @@ impl Folder for State { module, ModuleState { output_module: None, - memories: HashMap::new(), + memories: HashMap::default(), }, ); let mut this = PushedState::push_module(self, module); diff --git a/crates/fayalite/src/module/transform/visit.rs b/crates/fayalite/src/module/transform/visit.rs index 97de4fc..44aabc3 100644 --- a/crates/fayalite/src/module/transform/visit.rs +++ b/crates/fayalite/src/module/transform/visit.rs @@ -11,12 +11,11 @@ use crate::{ clock::Clock, enum_::{Enum, EnumType, EnumVariant}, expr::{ - ops, + Expr, ExprEnum, ops, target::{ Target, TargetBase, TargetChild, TargetPathArrayElement, TargetPathBundleField, TargetPathDynArrayElement, TargetPathElement, }, - Expr, ExprEnum, }, formal::FormalKind, int::{Bool, SIntType, SIntValue, Size, UIntType, UIntValue}, @@ -28,8 +27,10 @@ use crate::{ NormalModuleBody, ScopedNameId, Stmt, StmtConnect, StmtDeclaration, StmtFormal, StmtIf, StmtInstance, StmtMatch, StmtReg, StmtWire, }, + phantom_const::PhantomConst, reg::Reg, reset::{AsyncReset, Reset, ResetType, SyncReset}, + sim::{ExternModuleSimulation, value::DynSimOnly}, source_location::SourceLocation, ty::{CanonicalType, Type}, wire::Wire, diff --git a/crates/fayalite/src/phantom_const.rs b/crates/fayalite/src/phantom_const.rs new file mode 100644 index 0000000..b852056 --- /dev/null +++ b/crates/fayalite/src/phantom_const.rs @@ -0,0 +1,417 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// See Notices.txt for copyright information + +use crate::{ + expr::{ + Expr, ToExpr, + ops::{ExprPartialEq, ExprPartialOrd}, + }, + int::Bool, + intern::{Intern, Interned, InternedCompare, LazyInterned, LazyInternedTrait, Memoize}, + sim::value::{SimValue, SimValuePartialEq, ToSimValue, ToSimValueWithType}, + source_location::SourceLocation, + ty::{ + CanonicalType, OpaqueSimValueSlice, OpaqueSimValueWriter, OpaqueSimValueWritten, + StaticType, Type, TypeProperties, impl_match_variant_as_self, + serde_impls::{SerdeCanonicalType, SerdePhantomConst}, + }, +}; +use serde::{ + Deserialize, Deserializer, Serialize, Serializer, + de::{DeserializeOwned, Error}, +}; +use std::{ + any::Any, + fmt, + hash::{Hash, Hasher}, + marker::PhantomData, + ops::Index, +}; + +#[derive(Clone)] +pub struct PhantomConstCanonicalValue { + parsed: serde_json::Value, + serialized: Interned, +} + +impl PhantomConstCanonicalValue { + pub fn from_json_value(parsed: serde_json::Value) -> Self { + let serialized = Intern::intern_owned( + serde_json::to_string(&parsed) + .expect("conversion from json value to text shouldn't fail"), + ); + Self { parsed, serialized } + } + pub fn as_json_value(&self) -> &serde_json::Value { + &self.parsed + } + pub fn as_str(&self) -> Interned { + self.serialized + } +} + +impl fmt::Debug for PhantomConstCanonicalValue { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str(&self.serialized) + } +} + +impl fmt::Display for PhantomConstCanonicalValue { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str(&self.serialized) + } +} + +impl PartialEq for PhantomConstCanonicalValue { + fn eq(&self, other: &Self) -> bool { + self.serialized == other.serialized + } +} + +impl Eq for PhantomConstCanonicalValue {} + +impl Hash for PhantomConstCanonicalValue { + fn hash(&self, state: &mut H) { + self.serialized.hash(state); + } +} + +impl Serialize for PhantomConstCanonicalValue { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + self.parsed.serialize(serializer) + } +} + +impl<'de> Deserialize<'de> for PhantomConstCanonicalValue { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + Ok(Self::from_json_value(serde_json::Value::deserialize( + deserializer, + )?)) + } +} + +pub trait PhantomConstValue: Intern + InternedCompare + Serialize + fmt::Debug { + fn deserialize_value<'de, D>(deserializer: D) -> Result, D::Error> + where + D: serde::Deserializer<'de>; +} + +impl PhantomConstValue for T +where + T: ?Sized + Intern + InternedCompare + Serialize + fmt::Debug, + Interned: DeserializeOwned, +{ + fn deserialize_value<'de, D>(deserializer: D) -> Result, D::Error> + where + D: serde::Deserializer<'de>, + { + as Deserialize<'de>>::deserialize(deserializer) + } +} + +/// Wrapper type that allows any Rust value to be smuggled as a HDL [`Type`]. +/// This only works for values that can be [serialized][Serialize] to and [deserialized][Deserialize] from [JSON][serde_json]. +pub struct PhantomConst { + value: LazyInterned, +} + +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, Default)] +pub struct PhantomConstWithoutGenerics; + +#[allow(non_upper_case_globals)] +pub const PhantomConst: PhantomConstWithoutGenerics = PhantomConstWithoutGenerics; + +impl Index for PhantomConstWithoutGenerics { + type Output = PhantomConst; + + fn index(&self, value: T) -> &Self::Output { + Interned::into_inner(PhantomConst::new(value.intern()).intern_sized()) + } +} + +impl fmt::Debug for PhantomConst { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_tuple("PhantomConst").field(&self.get()).finish() + } +} + +impl Clone for PhantomConst { + fn clone(&self) -> Self { + *self + } +} + +impl Copy for PhantomConst {} + +impl PartialEq for PhantomConst { + fn eq(&self, other: &Self) -> bool { + self.get() == other.get() + } +} + +impl Eq for PhantomConst {} + +impl Hash for PhantomConst { + fn hash(&self, state: &mut H) { + self.get().hash(state); + } +} + +struct PhantomConstCanonicalMemoize(PhantomData); + +impl Copy + for PhantomConstCanonicalMemoize +{ +} + +impl Clone + for PhantomConstCanonicalMemoize +{ + fn clone(&self) -> Self { + *self + } +} + +impl Eq + for PhantomConstCanonicalMemoize +{ +} + +impl PartialEq + for PhantomConstCanonicalMemoize +{ + fn eq(&self, _other: &Self) -> bool { + true + } +} + +impl Hash + for PhantomConstCanonicalMemoize +{ + fn hash(&self, _state: &mut H) {} +} + +impl Memoize for PhantomConstCanonicalMemoize { + type Input = Interned; + type InputOwned = Interned; + type Output = Interned; + + fn inner(self, input: &Self::Input) -> Self::Output { + Intern::intern_sized(PhantomConstCanonicalValue::from_json_value( + serde_json::to_value(input) + .expect("serialization failed when constructing a canonical PhantomConst"), + )) + } +} + +impl Memoize for PhantomConstCanonicalMemoize { + type Input = Interned; + type InputOwned = Interned; + type Output = Interned; + + fn inner(self, input: &Self::Input) -> Self::Output { + PhantomConstValue::deserialize_value(input.as_json_value()) + .expect("deserialization failed ") + } +} + +impl PhantomConst { + pub fn new(value: Interned) -> Self { + Self { + value: LazyInterned::Interned(value), + } + } + pub const fn new_lazy(v: &'static dyn LazyInternedTrait) -> Self { + Self { + value: LazyInterned::new_lazy(v), + } + } + pub fn get(self) -> Interned { + self.value.interned() + } + pub fn type_properties(self) -> TypeProperties { + <()>::TYPE_PROPERTIES + } + pub fn can_connect(self, other: Self) -> bool { + self == other + } + pub fn canonical_phantom_const(self) -> PhantomConst { + if let Some(&retval) = ::downcast_ref::(&self) { + return retval; + } + ::new( + PhantomConstCanonicalMemoize::(PhantomData).get_owned(self.get()), + ) + } + pub fn from_canonical_phantom_const(canonical_type: PhantomConst) -> Self { + if let Some(&retval) = ::downcast_ref::(&canonical_type) { + return retval; + } + Self::new( + PhantomConstCanonicalMemoize::(PhantomData).get_owned(canonical_type.get()), + ) + } +} + +impl Type for PhantomConst { + type BaseType = PhantomConst; + type MaskType = (); + type SimValue = PhantomConst; + 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_opaque(&self, opaque: OpaqueSimValueSlice<'_>) -> Self::SimValue { + assert!(opaque.is_empty()); + *self + } + + fn sim_value_clone_from_opaque( + &self, + value: &mut Self::SimValue, + opaque: OpaqueSimValueSlice<'_>, + ) { + assert!(opaque.is_empty()); + assert_eq!(*value, *self); + } + + fn sim_value_to_opaque<'w>( + &self, + value: &Self::SimValue, + writer: OpaqueSimValueWriter<'w>, + ) -> OpaqueSimValueWritten<'w> { + assert_eq!(*value, *self); + writer.fill_cloned_from_slice(OpaqueSimValueSlice::empty()) + } +} + +impl Default for PhantomConst +where + Interned: Default, +{ + fn default() -> Self { + Self::TYPE + } +} + +impl StaticType for PhantomConst +where + Interned: Default, +{ + const TYPE: Self = PhantomConst { + value: LazyInterned::new_lazy(&Interned::::default), + }; + const MASK_TYPE: Self::MaskType = (); + const TYPE_PROPERTIES: TypeProperties = <()>::TYPE_PROPERTIES; + const MASK_TYPE_PROPERTIES: TypeProperties = <()>::TYPE_PROPERTIES; +} + +type SerdeType = SerdeCanonicalType>>; + +impl Serialize for PhantomConst { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + SerdeType::::PhantomConst(SerdePhantomConst(self.get())).serialize(serializer) + } +} + +impl<'de, T: ?Sized + PhantomConstValue> Deserialize<'de> for PhantomConst { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + match SerdeType::::deserialize(deserializer)? { + SerdeCanonicalType::PhantomConst(SerdePhantomConst(value)) => Ok(Self::new(value)), + ty => Err(Error::invalid_value( + serde::de::Unexpected::Other(ty.as_serde_unexpected_str()), + &"a PhantomConst", + )), + } + } +} + +impl ExprPartialEq for PhantomConst { + fn cmp_eq(lhs: Expr, rhs: Expr) -> Expr { + assert_eq!(Expr::ty(lhs), Expr::ty(rhs)); + true.to_expr() + } + + fn cmp_ne(lhs: Expr, rhs: Expr) -> Expr { + assert_eq!(Expr::ty(lhs), Expr::ty(rhs)); + false.to_expr() + } +} + +impl ExprPartialOrd for PhantomConst { + fn cmp_lt(lhs: Expr, rhs: Expr) -> Expr { + assert_eq!(Expr::ty(lhs), Expr::ty(rhs)); + false.to_expr() + } + + fn cmp_le(lhs: Expr, rhs: Expr) -> Expr { + assert_eq!(Expr::ty(lhs), Expr::ty(rhs)); + true.to_expr() + } + + fn cmp_gt(lhs: Expr, rhs: Expr) -> Expr { + assert_eq!(Expr::ty(lhs), Expr::ty(rhs)); + false.to_expr() + } + + fn cmp_ge(lhs: Expr, rhs: Expr) -> Expr { + assert_eq!(Expr::ty(lhs), Expr::ty(rhs)); + true.to_expr() + } +} + +impl SimValuePartialEq for PhantomConst { + fn sim_value_eq(this: &SimValue, other: &SimValue) -> bool { + assert_eq!(SimValue::ty(this), SimValue::ty(other)); + true + } +} + +impl ToSimValue for PhantomConst { + type Type = PhantomConst; + + fn to_sim_value(&self) -> SimValue { + SimValue::from_value(*self, *self) + } +} + +impl ToSimValueWithType> for PhantomConst { + fn to_sim_value_with_type(&self, ty: PhantomConst) -> SimValue> { + SimValue::from_value(ty, *self) + } +} + +impl ToSimValueWithType for PhantomConst { + fn to_sim_value_with_type(&self, ty: CanonicalType) -> SimValue { + SimValue::into_canonical(SimValue::from_value(Self::from_canonical(ty), *self)) + } +} diff --git a/crates/fayalite/src/prelude.rs b/crates/fayalite/src/prelude.rs index 9e7a85e..4fca2ad 100644 --- a/crates/fayalite/src/prelude.rs +++ b/crates/fayalite/src/prelude.rs @@ -1,6 +1,7 @@ // SPDX-License-Identifier: LGPL-3.0-or-later // See Notices.txt for copyright information pub use crate::{ + __, annotations::{ BlackBoxInlineAnnotation, BlackBoxPathAnnotation, CustomFirrtlAnnotation, DocStringAnnotation, DontTouchAnnotation, SVAttributeAnnotation, @@ -11,26 +12,32 @@ pub use crate::{ clock::{Clock, ClockDomain, ToClock}, enum_::{Enum, HdlNone, HdlOption, HdlSome}, expr::{ - repeat, CastBitsTo, CastTo, CastToBits, Expr, HdlPartialEq, HdlPartialOrd, MakeUninitExpr, - ReduceBits, ToExpr, + CastBitsTo, CastTo, CastToBits, Expr, HdlPartialEq, HdlPartialOrd, MakeUninitExpr, + ReduceBits, ToExpr, repeat, }, formal::{ - all_const, all_seq, any_const, any_seq, formal_global_clock, formal_reset, hdl_assert, - hdl_assert_with_enable, hdl_assume, hdl_assume_with_enable, hdl_cover, - hdl_cover_with_enable, MakeFormalExpr, + MakeFormalExpr, all_const, all_seq, any_const, any_seq, formal_global_clock, formal_reset, + hdl_assert, hdl_assert_with_enable, hdl_assume, hdl_assume_with_enable, hdl_cover, + hdl_cover_with_enable, }, hdl, hdl_module, - int::{Bool, DynSize, KnownSize, SInt, SIntType, Size, UInt, UIntType}, + int::{Bool, DynSize, KnownSize, SInt, SIntType, SIntValue, Size, UInt, UIntType, UIntValue}, memory::{Mem, MemBuilder, ReadUnderWrite}, module::{ - annotate, connect, connect_any, incomplete_wire, instance, memory, memory_array, - memory_with_init, reg_builder, wire, Instance, Module, ModuleBuilder, + Instance, Module, ModuleBuilder, annotate, connect, connect_any, incomplete_wire, instance, + memory, memory_array, memory_with_init, reg_builder, wire, }, + phantom_const::PhantomConst, reg::Reg, reset::{AsyncReset, Reset, SyncReset, ToAsyncReset, ToReset, ToSyncReset}, + sim::{ + ExternModuleSimulationState, Simulation, + time::{SimDuration, SimInstant}, + value::{SimOnly, SimOnlyValue, SimValue, ToSimValue, ToSimValueWithType}, + }, source_location::SourceLocation, ty::{AsMask, CanonicalType, Type}, util::{ConstUsize, GenericConstUsize}, wire::Wire, - __, }; +pub use bitvec::{slice::BitSlice, vec::BitVec}; diff --git a/crates/fayalite/src/reset.rs b/crates/fayalite/src/reset.rs index 9328365..5dff278 100644 --- a/crates/fayalite/src/reset.rs +++ b/crates/fayalite/src/reset.rs @@ -2,11 +2,15 @@ // See Notices.txt for copyright information use crate::{ clock::Clock, - expr::{ops, Expr, ToExpr}, + expr::{Expr, ToExpr, ops}, int::{Bool, SInt, UInt}, source_location::SourceLocation, - ty::{impl_match_variant_as_self, CanonicalType, StaticType, Type, TypeProperties}, + ty::{ + CanonicalType, OpaqueSimValueSize, OpaqueSimValueSlice, OpaqueSimValueWriter, + OpaqueSimValueWritten, StaticType, Type, TypeProperties, impl_match_variant_as_self, + }, }; +use bitvec::{bits, order::Lsb0}; mod sealed { pub trait ResetTypeSealed {} @@ -45,6 +49,7 @@ macro_rules! reset_type { impl Type for $name { type BaseType = $name; type MaskType = Bool; + type SimValue = bool; impl_match_variant_as_self!(); @@ -66,6 +71,31 @@ macro_rules! reset_type { }; retval } + + fn sim_value_from_opaque(&self, opaque: OpaqueSimValueSlice<'_>) -> Self::SimValue { + assert_eq!(opaque.size(), OpaqueSimValueSize::from_bit_width(1)); + opaque.bits()[0] + } + + fn sim_value_clone_from_opaque( + &self, + value: &mut Self::SimValue, + opaque: OpaqueSimValueSlice<'_>, + ) { + assert_eq!(opaque.size(), OpaqueSimValueSize::from_bit_width(1)); + *value = opaque.bits()[0]; + } + + fn sim_value_to_opaque<'w>( + &self, + value: &Self::SimValue, + writer: OpaqueSimValueWriter<'w>, + ) -> OpaqueSimValueWritten<'w> { + assert_eq!(writer.size(), OpaqueSimValueSize::from_bit_width(1)); + writer.fill_cloned_from_slice(OpaqueSimValueSlice::from_bitslice( + [bits![0], bits![1]][*value as usize], + )) + } } impl $name { @@ -85,6 +115,7 @@ macro_rules! reset_type { is_storable: false, is_castable_from_bits: $is_castable_from_bits, bit_width: 1, + sim_only_values_len: 0, }; const MASK_TYPE_PROPERTIES: TypeProperties = Bool::TYPE_PROPERTIES; } diff --git a/crates/fayalite/src/sim.rs b/crates/fayalite/src/sim.rs index cb6228d..1717aec 100644 --- a/crates/fayalite/src/sim.rs +++ b/crates/fayalite/src/sim.rs @@ -5,5007 +5,66 @@ use crate::{ bundle::{BundleField, BundleType}, - enum_::{EnumType, EnumVariant}, expr::{ - ops, + Flow, ToLiteralBits, target::{ - GetTarget, Target, TargetBase, TargetPathArrayElement, TargetPathBundleField, - TargetPathElement, + GetTarget, Target, TargetPathArrayElement, TargetPathBundleField, TargetPathElement, }, - ExprEnum, Flow, ToLiteralBits, }, - int::{BoolOrIntType, IntType, SIntValue, UIntValue}, - intern::{Intern, Interned, Memoize}, - memory::PortKind, + int::BoolOrIntType, + intern::{Intern, Interned, InternedCompare, PtrEqWithTypeId, SupportsPtrEqWithTypeId}, module::{ - transform::deduce_resets::deduce_resets, AnnotatedModuleIO, Block, Id, InstantiatedModule, - ModuleBody, NameId, NormalModuleBody, ScopedNameId, Stmt, StmtConnect, StmtDeclaration, - StmtFormal, StmtIf, StmtInstance, StmtMatch, StmtReg, StmtWire, TargetInInstantiatedModule, + ModuleIO, + transform::visit::{Fold, Folder, Visit, Visitor}, }, prelude::*, - reset::{ResetType, ResetTypeDispatch}, + reset::ResetType, sim::{ + compiler::{ + Compiled, CompiledBundleField, CompiledExternModule, CompiledTypeLayoutBody, + CompiledValue, + }, interpreter::{ - BreakAction, BreakpointsSet, Insn, InsnField, InsnFieldKind, InsnFieldType, - InsnOrLabel, Insns, InsnsBuilding, InsnsBuildingDone, InsnsBuildingKind, Label, - MemoryData, RunResult, SlotDebugData, SmallUInt, State, StatePartArrayIndex, - StatePartArrayIndexed, StatePartIndex, StatePartIndexRange, StatePartKind, - StatePartKindBigSlots, StatePartKindMemories, StatePartKindSmallSlots, StatePartLayout, - StatePartLen, StatePartsValue, TypeArrayIndex, TypeArrayIndexes, TypeIndex, - TypeIndexRange, TypeLayout, TypeLen, TypeParts, + BreakAction, BreakpointsSet, RunResult, SmallUInt, State, + parts::{ + StatePartIndex, StatePartKindBigSlots, StatePartKindMemories, + StatePartKindSimOnlySlots, StatePartKindSmallSlots, TypeIndexRange, TypeLenSingle, + }, }, time::{SimDuration, SimInstant}, + value::{DynSimOnly, DynSimOnlyValue, SimValue}, }, - ty::StaticType, - util::{BitSliceWriteWithBase, DebugAsDisplay}, + ty::{ + OpaqueSimValue, OpaqueSimValueSize, OpaqueSimValueSizeRange, OpaqueSimValueSlice, + OpaqueSimValueWriter, + }, + util::{BitSliceWriteWithBase, DebugAsDisplay, HashMap, HashSet}, }; use bitvec::{bits, order::Lsb0, slice::BitSlice, vec::BitVec, view::BitView}; -use hashbrown::{HashMap, HashSet}; use num_bigint::BigInt; use num_traits::{Signed, Zero}; -use petgraph::{ - data::FromElements, - visit::{ - EdgeRef, GraphBase, IntoEdgeReferences, IntoNeighbors, IntoNeighborsDirected, - IntoNodeIdentifiers, IntoNodeReferences, NodeRef, VisitMap, Visitable, - }, -}; use std::{ - borrow::Cow, collections::BTreeSet, fmt, marker::PhantomData, mem, ops::IndexMut, sync::Arc, + any::Any, + borrow::Cow, + cell::RefCell, + collections::BTreeMap, + fmt, + future::{Future, IntoFuture}, + hash::Hash, + mem, + pin::Pin, + ptr, + rc::Rc, + sync::Arc, + task::Poll, }; +pub mod compiler; mod interpreter; pub mod time; +pub mod value; pub mod vcd; -#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)] -enum CondBody { - IfTrue { - cond: CompiledValue, - }, - IfFalse { - cond: CompiledValue, - }, - MatchArm { - discriminant: StatePartIndex, - variant_index: usize, - }, -} - -#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)] -struct Cond { - body: CondBody, - source_location: SourceLocation, -} - -#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)] -struct CompiledBundleField { - offset: TypeIndex, - ty: CompiledTypeLayout, -} - -#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)] -enum CompiledTypeLayoutBody { - Scalar, - Array { - /// debug names are ignored, use parent's layout instead - element: Interned>, - }, - Bundle { - /// debug names are ignored, use parent's layout instead - fields: Interned<[CompiledBundleField]>, - }, -} - -#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)] -struct CompiledTypeLayout { - ty: T, - layout: TypeLayout, - body: CompiledTypeLayoutBody, -} - -impl CompiledTypeLayout { - fn with_prefixed_debug_names(self, prefix: &str) -> Self { - let Self { ty, layout, body } = self; - Self { - ty, - layout: layout.with_prefixed_debug_names(prefix), - body, - } - } - fn with_anonymized_debug_info(self) -> Self { - let Self { ty, layout, body } = self; - Self { - ty, - layout: layout.with_anonymized_debug_info(), - body, - } - } - fn get(ty: T) -> Self { - #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)] - struct MyMemoize; - impl Memoize for MyMemoize { - type Input = CanonicalType; - type InputOwned = CanonicalType; - type Output = CompiledTypeLayout; - - fn inner(self, input: &Self::Input) -> Self::Output { - match input { - CanonicalType::UInt(_) - | CanonicalType::SInt(_) - | CanonicalType::Bool(_) - | CanonicalType::Enum(_) - | CanonicalType::AsyncReset(_) - | CanonicalType::SyncReset(_) - | CanonicalType::Reset(_) - | CanonicalType::Clock(_) => { - let mut layout = TypeLayout::empty(); - let debug_data = SlotDebugData { - name: Interned::default(), - ty: *input, - }; - layout.big_slots = StatePartLayout::scalar(debug_data, ()); - CompiledTypeLayout { - ty: *input, - layout: layout.into(), - body: CompiledTypeLayoutBody::Scalar, - } - } - CanonicalType::Array(array) => { - let mut layout = TypeLayout::empty(); - let element = CompiledTypeLayout::get(array.element()).intern_sized(); - for index in 0..array.len() { - layout.allocate( - &element - .layout - .with_prefixed_debug_names(&format!("[{index}]")), - ); - } - CompiledTypeLayout { - ty: *input, - layout: layout.into(), - body: CompiledTypeLayoutBody::Array { element }, - } - } - CanonicalType::Bundle(bundle) => { - let mut layout = TypeLayout::empty(); - let fields = bundle - .fields() - .iter() - .map( - |BundleField { - name, - flipped: _, - ty, - }| { - let ty = CompiledTypeLayout::get(*ty); - let offset = layout - .allocate( - &ty.layout - .with_prefixed_debug_names(&format!(".{name}")), - ) - .start(); - CompiledBundleField { offset, ty } - }, - ) - .collect(); - CompiledTypeLayout { - ty: *input, - layout: layout.into(), - body: CompiledTypeLayoutBody::Bundle { fields }, - } - } - } - } - } - let CompiledTypeLayout { - ty: _, - layout, - body, - } = MyMemoize.get_owned(ty.canonical()); - Self { ty, layout, body } - } -} - -#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)] -struct CompiledValue { - layout: CompiledTypeLayout, - range: TypeIndexRange, - write: Option<(CompiledTypeLayout, TypeIndexRange)>, -} - -impl CompiledValue { - fn write(self) -> (CompiledTypeLayout, TypeIndexRange) { - self.write.unwrap_or((self.layout, self.range)) - } - fn write_value(self) -> Self { - let (layout, range) = self.write(); - Self { - layout, - range, - write: None, - } - } - fn map( - self, - mut f: impl FnMut( - CompiledTypeLayout, - TypeIndexRange, - ) -> (CompiledTypeLayout, TypeIndexRange), - ) -> CompiledValue { - let (layout, range) = f(self.layout, self.range); - CompiledValue { - layout, - range, - write: self.write.map(|(layout, range)| f(layout, range)), - } - } - fn map_ty(self, mut f: impl FnMut(T) -> U) -> CompiledValue { - self.map(|CompiledTypeLayout { ty, layout, body }, range| { - ( - CompiledTypeLayout { - ty: f(ty), - layout, - body, - }, - range, - ) - }) - } -} - -impl CompiledValue { - fn field_by_index(self, field_index: usize) -> CompiledValue { - self.map(|layout, range| { - let CompiledTypeLayout { - ty: _, - layout: _, - body: CompiledTypeLayoutBody::Bundle { fields }, - } = layout - else { - unreachable!(); - }; - ( - fields[field_index].ty, - range.slice(TypeIndexRange::new( - fields[field_index].offset, - fields[field_index].ty.layout.len(), - )), - ) - }) - } - fn field_by_name(self, name: Interned) -> CompiledValue { - self.field_by_index(self.layout.ty.name_indexes()[&name]) - } -} - -impl CompiledValue { - fn element(self, index: usize) -> CompiledValue { - self.map(|layout, range| { - let CompiledTypeLayoutBody::Array { element } = layout.body else { - unreachable!(); - }; - (*element, range.index_array(element.layout.len(), index)) - }) - } - fn element_dyn( - self, - index_slot: StatePartIndex, - ) -> CompiledExpr { - CompiledExpr::from(self).element_dyn(index_slot) - } -} - -#[derive(Debug, PartialEq, Eq, Hash, Copy, Clone)] -struct CompiledExpr { - static_part: CompiledValue, - indexes: TypeArrayIndexes, -} - -impl From> for CompiledExpr { - fn from(static_part: CompiledValue) -> Self { - Self { - static_part, - indexes: TypeArrayIndexes::default(), - } - } -} - -impl CompiledExpr { - fn map_ty(self, f: impl FnMut(T) -> U) -> CompiledExpr { - let Self { - static_part, - indexes, - } = self; - CompiledExpr { - static_part: static_part.map_ty(f), - indexes, - } - } - fn add_target_without_indexes_to_set(self, inputs: &mut SlotSet) { - let Self { - static_part, - indexes, - } = self; - indexes.as_ref().for_each_offset(|offset| { - inputs.extend([static_part.range.offset(offset)]); - }); - } - fn add_target_and_indexes_to_set(self, inputs: &mut SlotSet) { - let Self { - static_part: _, - indexes, - } = self; - self.add_target_without_indexes_to_set(inputs); - inputs.extend(indexes.as_ref().iter()); - } -} - -impl CompiledExpr { - fn field_by_index(self, field_index: usize) -> CompiledExpr { - CompiledExpr { - static_part: self.static_part.field_by_index(field_index), - indexes: self.indexes, - } - } - fn field_by_name(self, name: Interned) -> CompiledExpr { - CompiledExpr { - static_part: self.static_part.field_by_name(name), - indexes: self.indexes, - } - } -} - -impl CompiledExpr { - fn element(self, index: usize) -> CompiledExpr { - CompiledExpr { - static_part: self.static_part.element(index), - indexes: self.indexes, - } - } - fn element_dyn( - self, - index_slot: StatePartIndex, - ) -> CompiledExpr { - let CompiledTypeLayoutBody::Array { element } = self.static_part.layout.body else { - unreachable!(); - }; - let stride = element.layout.len(); - let indexes = self.indexes.join(TypeArrayIndex::from_parts( - index_slot, - self.static_part.layout.ty.len(), - stride, - )); - CompiledExpr { - static_part: self.static_part.map(|layout, range| { - let CompiledTypeLayoutBody::Array { element } = layout.body else { - unreachable!(); - }; - (*element, range.index_array(stride, 0)) - }), - indexes, - } - } -} - -#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] -enum AssignmentOrSlotIndex { - AssignmentIndex(usize), - SmallSlot(StatePartIndex), - BigSlot(StatePartIndex), -} - -#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] -enum AssignmentIO { - BigInput { - assignment_index: usize, - slot: StatePartIndex, - }, - SmallInput { - assignment_index: usize, - slot: StatePartIndex, - }, - BigOutput { - assignment_index: usize, - slot: StatePartIndex, - }, - SmallOutput { - assignment_index: usize, - slot: StatePartIndex, - }, -} - -#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] -enum AssignmentsEdge { - IO(AssignmentIO), - AssignmentImmediatePredecessor { - predecessor_assignment_index: usize, - assignment_index: usize, - }, -} - -#[derive(Debug)] -enum Assignments { - Accumulating { - assignments: Vec, - }, - Finalized { - assignments: Box<[Assignment]>, - slots_layout: TypeLayout, - slot_readers: SlotToAssignmentIndexFullMap, - slot_writers: SlotToAssignmentIndexFullMap, - assignment_immediate_predecessors: Box<[Box<[usize]>]>, - assignment_immediate_successors: Box<[Box<[usize]>]>, - }, -} - -impl Default for Assignments { - fn default() -> Self { - Self::Accumulating { - assignments: Vec::new(), - } - } -} - -impl Assignments { - fn finalize(&mut self, slots_layout: TypeLayout) { - let Self::Accumulating { assignments } = self else { - unreachable!("already finalized"); - }; - let assignments = mem::take(assignments).into_boxed_slice(); - let mut slot_readers = SlotToAssignmentIndexFullMap::new(slots_layout.len()); - let mut slot_writers = SlotToAssignmentIndexFullMap::new(slots_layout.len()); - let mut assignment_immediate_predecessors = vec![BTreeSet::new(); assignments.len()]; - let mut assignment_immediate_successors = vec![BTreeSet::new(); assignments.len()]; - for (assignment_index, assignment) in assignments.iter().enumerate() { - slot_readers - .keys_for_assignment(assignment_index) - .extend([&assignment.inputs]); - slot_readers - .keys_for_assignment(assignment_index) - .extend(&assignment.conditions); - let SlotSet(TypeParts { - small_slots, - big_slots, - }) = &assignment.outputs; - for &slot in small_slots { - if let Some(&pred) = slot_writers[slot].last() { - assignment_immediate_predecessors[assignment_index].insert(pred); - assignment_immediate_successors[pred].insert(assignment_index); - } - slot_writers[slot].push(assignment_index); - } - for &slot in big_slots { - if let Some(&pred) = slot_writers[slot].last() { - assignment_immediate_predecessors[assignment_index].insert(pred); - assignment_immediate_successors[pred].insert(assignment_index); - } - slot_writers[slot].push(assignment_index); - } - } - *self = Self::Finalized { - assignments, - slots_layout, - slot_readers, - slot_writers, - assignment_immediate_predecessors: assignment_immediate_predecessors - .into_iter() - .map(Box::from_iter) - .collect(), - assignment_immediate_successors: assignment_immediate_successors - .into_iter() - .map(Box::from_iter) - .collect(), - }; - } - fn push(&mut self, v: Assignment) { - let Self::Accumulating { assignments } = self else { - unreachable!("already finalized"); - }; - assignments.push(v); - } - fn assignments(&self) -> &[Assignment] { - let Self::Finalized { assignments, .. } = self else { - unreachable!("Assignments::finalize should have been called"); - }; - assignments - } - fn slots_layout(&self) -> TypeLayout { - let Self::Finalized { slots_layout, .. } = self else { - unreachable!("Assignments::finalize should have been called"); - }; - *slots_layout - } - fn slot_readers(&self) -> &SlotToAssignmentIndexFullMap { - let Self::Finalized { slot_readers, .. } = self else { - unreachable!("Assignments::finalize should have been called"); - }; - slot_readers - } - fn slot_writers(&self) -> &SlotToAssignmentIndexFullMap { - let Self::Finalized { slot_writers, .. } = self else { - unreachable!("Assignments::finalize should have been called"); - }; - slot_writers - } - fn assignment_immediate_predecessors(&self) -> &[Box<[usize]>] { - let Self::Finalized { - assignment_immediate_predecessors, - .. - } = self - else { - unreachable!("Assignments::finalize should have been called"); - }; - assignment_immediate_predecessors - } - fn assignment_immediate_successors(&self) -> &[Box<[usize]>] { - let Self::Finalized { - assignment_immediate_successors, - .. - } = self - else { - unreachable!("Assignments::finalize should have been called"); - }; - assignment_immediate_successors - } - fn elements(&self) -> AssignmentsElements<'_> { - let SlotToAssignmentIndexFullMap(TypeParts { - small_slots, - big_slots, - }) = self.slot_readers(); - AssignmentsElements { - node_indexes: HashMap::with_capacity( - self.assignments().len() + small_slots.len() + big_slots.len(), - ), - nodes: self.node_references(), - edges: self.edge_references(), - } - } -} - -impl GraphBase for Assignments { - type EdgeId = AssignmentsEdge; - type NodeId = AssignmentOrSlotIndex; -} - -#[derive(Debug, Clone, Copy)] -enum AssignmentsNodeRef<'a> { - Assignment { - index: usize, - assignment: &'a Assignment, - }, - SmallSlot(StatePartIndex, SlotDebugData), - BigSlot(StatePartIndex, SlotDebugData), -} - -impl<'a> NodeRef for AssignmentsNodeRef<'a> { - type NodeId = AssignmentOrSlotIndex; - type Weight = AssignmentsNodeRef<'a>; - - fn id(&self) -> Self::NodeId { - match *self { - AssignmentsNodeRef::Assignment { - index, - assignment: _, - } => AssignmentOrSlotIndex::AssignmentIndex(index), - AssignmentsNodeRef::SmallSlot(slot, _) => AssignmentOrSlotIndex::SmallSlot(slot), - AssignmentsNodeRef::BigSlot(slot, _) => AssignmentOrSlotIndex::BigSlot(slot), - } - } - - fn weight(&self) -> &Self::Weight { - self - } -} - -impl<'a> petgraph::visit::Data for &'a Assignments { - type NodeWeight = AssignmentsNodeRef<'a>; - type EdgeWeight = AssignmentsEdge; -} - -struct AssignmentsElements<'a> { - node_indexes: HashMap, - nodes: AssignmentsNodes<'a>, - edges: AssignmentsEdges<'a>, -} - -impl<'a> Iterator for AssignmentsElements<'a> { - type Item = petgraph::data::Element< - <&'a Assignments as petgraph::visit::Data>::NodeWeight, - <&'a Assignments as petgraph::visit::Data>::EdgeWeight, - >; - - fn next(&mut self) -> Option { - let Self { - node_indexes, - nodes, - edges, - } = self; - if let Some(node) = nodes.next() { - node_indexes.insert(node.id(), node_indexes.len()); - return Some(petgraph::data::Element::Node { weight: node }); - } - let edge = edges.next()?; - Some(petgraph::data::Element::Edge { - source: node_indexes[&edge.source()], - target: node_indexes[&edge.target()], - weight: *edge.weight(), - }) - } -} - -#[derive(Clone)] -struct AssignmentsNodeIdentifiers { - assignment_indexes: std::ops::Range, - small_slots: std::ops::Range, - big_slots: std::ops::Range, -} - -impl AssignmentsNodeIdentifiers { - fn internal_iter<'a>(&'a mut self) -> impl Iterator + 'a { - let Self { - assignment_indexes, - small_slots, - big_slots, - } = self; - assignment_indexes - .map(AssignmentOrSlotIndex::AssignmentIndex) - .chain(small_slots.map(|value| { - AssignmentOrSlotIndex::SmallSlot(StatePartIndex { - value, - _phantom: PhantomData, - }) - })) - .chain(big_slots.map(|value| { - AssignmentOrSlotIndex::BigSlot(StatePartIndex { - value, - _phantom: PhantomData, - }) - })) - } -} - -impl Iterator for AssignmentsNodeIdentifiers { - type Item = AssignmentOrSlotIndex; - fn next(&mut self) -> Option { - self.internal_iter().next() - } - - fn nth(&mut self, n: usize) -> Option { - self.internal_iter().nth(n) - } -} - -impl<'a> IntoNodeIdentifiers for &'a Assignments { - type NodeIdentifiers = AssignmentsNodeIdentifiers; - - fn node_identifiers(self) -> Self::NodeIdentifiers { - let TypeLen { - small_slots, - big_slots, - } = self.slot_readers().len(); - AssignmentsNodeIdentifiers { - assignment_indexes: 0..self.assignments().len(), - small_slots: 0..small_slots.value, - big_slots: 0..big_slots.value, - } - } -} - -struct AssignmentsNodes<'a> { - assignments: &'a Assignments, - nodes: AssignmentsNodeIdentifiers, -} - -impl<'a> Iterator for AssignmentsNodes<'a> { - type Item = AssignmentsNodeRef<'a>; - - fn next(&mut self) -> Option { - self.nodes.next().map(|node| match node { - AssignmentOrSlotIndex::AssignmentIndex(index) => AssignmentsNodeRef::Assignment { - index, - assignment: &self.assignments.assignments()[index], - }, - AssignmentOrSlotIndex::SmallSlot(slot) => AssignmentsNodeRef::SmallSlot( - slot, - *self.assignments.slots_layout().small_slots.debug_data(slot), - ), - AssignmentOrSlotIndex::BigSlot(slot) => AssignmentsNodeRef::BigSlot( - slot, - *self.assignments.slots_layout().big_slots.debug_data(slot), - ), - }) - } -} - -impl<'a> IntoNodeReferences for &'a Assignments { - type NodeRef = AssignmentsNodeRef<'a>; - type NodeReferences = AssignmentsNodes<'a>; - - fn node_references(self) -> Self::NodeReferences { - AssignmentsNodes { - assignments: self, - nodes: self.node_identifiers(), - } - } -} - -struct AssignmentsNeighborsDirected<'a> { - assignment_indexes: std::slice::Iter<'a, usize>, - small_slots: std::collections::btree_set::Iter<'a, StatePartIndex>, - big_slots: std::collections::btree_set::Iter<'a, StatePartIndex>, -} - -impl Iterator for AssignmentsNeighborsDirected<'_> { - type Item = AssignmentOrSlotIndex; - fn next(&mut self) -> Option { - let Self { - assignment_indexes, - small_slots, - big_slots, - } = self; - if let retval @ Some(_) = assignment_indexes - .next() - .copied() - .map(AssignmentOrSlotIndex::AssignmentIndex) - { - retval - } else if let retval @ Some(_) = small_slots - .next() - .copied() - .map(AssignmentOrSlotIndex::SmallSlot) - { - retval - } else if let retval @ Some(_) = big_slots - .next() - .copied() - .map(AssignmentOrSlotIndex::BigSlot) - { - retval - } else { - None - } - } -} - -impl<'a> IntoNeighbors for &'a Assignments { - type Neighbors = AssignmentsNeighborsDirected<'a>; - - fn neighbors(self, n: Self::NodeId) -> Self::Neighbors { - self.neighbors_directed(n, petgraph::Direction::Outgoing) - } -} - -impl<'a> IntoNeighborsDirected for &'a Assignments { - type NeighborsDirected = AssignmentsNeighborsDirected<'a>; - - fn neighbors_directed( - self, - n: Self::NodeId, - d: petgraph::Direction, - ) -> Self::NeighborsDirected { - use petgraph::Direction::*; - let slot_map = match d { - Outgoing => self.slot_readers(), - Incoming => self.slot_writers(), - }; - match n { - AssignmentOrSlotIndex::AssignmentIndex(assignment_index) => { - let assignment = &self.assignments()[assignment_index]; - let ( - assignment_indexes, - SlotSet(TypeParts { - small_slots, - big_slots, - }), - ) = match d { - Outgoing => ( - &self.assignment_immediate_successors()[assignment_index], - &assignment.outputs, - ), - Incoming => ( - &self.assignment_immediate_predecessors()[assignment_index], - &assignment.inputs, - ), - }; - AssignmentsNeighborsDirected { - assignment_indexes: assignment_indexes.iter(), - small_slots: small_slots.iter(), - big_slots: big_slots.iter(), - } - } - AssignmentOrSlotIndex::SmallSlot(slot) => AssignmentsNeighborsDirected { - assignment_indexes: slot_map[slot].iter(), - small_slots: Default::default(), - big_slots: Default::default(), - }, - AssignmentOrSlotIndex::BigSlot(slot) => AssignmentsNeighborsDirected { - assignment_indexes: slot_map[slot].iter(), - small_slots: Default::default(), - big_slots: Default::default(), - }, - } - } -} - -impl EdgeRef for AssignmentsEdge { - type NodeId = AssignmentOrSlotIndex; - type EdgeId = AssignmentsEdge; - type Weight = AssignmentsEdge; - - fn source(&self) -> Self::NodeId { - match *self { - AssignmentsEdge::IO(AssignmentIO::BigInput { - assignment_index: _, - slot, - }) => AssignmentOrSlotIndex::BigSlot(slot), - AssignmentsEdge::IO(AssignmentIO::SmallInput { - assignment_index: _, - slot, - }) => AssignmentOrSlotIndex::SmallSlot(slot), - AssignmentsEdge::IO(AssignmentIO::BigOutput { - assignment_index, - slot: _, - }) => AssignmentOrSlotIndex::AssignmentIndex(assignment_index), - AssignmentsEdge::IO(AssignmentIO::SmallOutput { - assignment_index, - slot: _, - }) => AssignmentOrSlotIndex::AssignmentIndex(assignment_index), - AssignmentsEdge::AssignmentImmediatePredecessor { - predecessor_assignment_index, - assignment_index: _, - } => AssignmentOrSlotIndex::AssignmentIndex(predecessor_assignment_index), - } - } - - fn target(&self) -> Self::NodeId { - match *self { - AssignmentsEdge::IO(AssignmentIO::BigInput { - assignment_index, - slot: _, - }) => AssignmentOrSlotIndex::AssignmentIndex(assignment_index), - AssignmentsEdge::IO(AssignmentIO::SmallInput { - assignment_index, - slot: _, - }) => AssignmentOrSlotIndex::AssignmentIndex(assignment_index), - AssignmentsEdge::IO(AssignmentIO::BigOutput { - assignment_index: _, - slot, - }) => AssignmentOrSlotIndex::BigSlot(slot), - AssignmentsEdge::IO(AssignmentIO::SmallOutput { - assignment_index: _, - slot, - }) => AssignmentOrSlotIndex::SmallSlot(slot), - AssignmentsEdge::AssignmentImmediatePredecessor { - predecessor_assignment_index: _, - assignment_index, - } => AssignmentOrSlotIndex::AssignmentIndex(assignment_index), - } - } - - fn weight(&self) -> &Self::Weight { - self - } - - fn id(&self) -> Self::EdgeId { - *self - } -} - -struct AssignmentsEdges<'a> { - assignments: &'a Assignments, - nodes: AssignmentsNodeIdentifiers, - outgoing_neighbors: Option<(AssignmentOrSlotIndex, AssignmentsNeighborsDirected<'a>)>, -} - -impl Iterator for AssignmentsEdges<'_> { - type Item = AssignmentsEdge; - - fn next(&mut self) -> Option { - loop { - if let Some((node, outgoing_neighbors)) = &mut self.outgoing_neighbors { - if let Some(outgoing_neighbor) = outgoing_neighbors.next() { - return Some(match (*node, outgoing_neighbor) { - ( - AssignmentOrSlotIndex::SmallSlot(_) | AssignmentOrSlotIndex::BigSlot(_), - AssignmentOrSlotIndex::SmallSlot(_) | AssignmentOrSlotIndex::BigSlot(_), - ) => unreachable!(), - ( - AssignmentOrSlotIndex::AssignmentIndex(predecessor_assignment_index), - AssignmentOrSlotIndex::AssignmentIndex(assignment_index), - ) => AssignmentsEdge::AssignmentImmediatePredecessor { - predecessor_assignment_index, - assignment_index, - }, - ( - AssignmentOrSlotIndex::AssignmentIndex(assignment_index), - AssignmentOrSlotIndex::SmallSlot(slot), - ) => AssignmentsEdge::IO(AssignmentIO::SmallOutput { - assignment_index, - slot, - }), - ( - AssignmentOrSlotIndex::AssignmentIndex(assignment_index), - AssignmentOrSlotIndex::BigSlot(slot), - ) => AssignmentsEdge::IO(AssignmentIO::BigOutput { - assignment_index, - slot, - }), - ( - AssignmentOrSlotIndex::SmallSlot(slot), - AssignmentOrSlotIndex::AssignmentIndex(assignment_index), - ) => AssignmentsEdge::IO(AssignmentIO::SmallInput { - assignment_index, - slot, - }), - ( - AssignmentOrSlotIndex::BigSlot(slot), - AssignmentOrSlotIndex::AssignmentIndex(assignment_index), - ) => AssignmentsEdge::IO(AssignmentIO::BigInput { - assignment_index, - slot, - }), - }); - } - } - let node = self.nodes.next()?; - self.outgoing_neighbors = Some(( - node, - self.assignments - .neighbors_directed(node, petgraph::Direction::Outgoing), - )); - } - } -} - -impl<'a> IntoEdgeReferences for &'a Assignments { - type EdgeRef = AssignmentsEdge; - type EdgeReferences = AssignmentsEdges<'a>; - - fn edge_references(self) -> Self::EdgeReferences { - AssignmentsEdges { - assignments: self, - nodes: self.node_identifiers(), - outgoing_neighbors: None, - } - } -} - -struct AssignmentsVisitMap { - assignments: Vec, - slots: DenseSlotSet, -} - -impl VisitMap for AssignmentsVisitMap { - fn visit(&mut self, n: AssignmentOrSlotIndex) -> bool { - match n { - AssignmentOrSlotIndex::AssignmentIndex(assignment_index) => { - !mem::replace(&mut self.assignments[assignment_index], true) - } - AssignmentOrSlotIndex::SmallSlot(slot) => self.slots.insert(slot), - AssignmentOrSlotIndex::BigSlot(slot) => self.slots.insert(slot), - } - } - - fn is_visited(&self, n: &AssignmentOrSlotIndex) -> bool { - match *n { - AssignmentOrSlotIndex::AssignmentIndex(assignment_index) => { - self.assignments[assignment_index] - } - AssignmentOrSlotIndex::SmallSlot(slot) => self.slots.contains(slot), - AssignmentOrSlotIndex::BigSlot(slot) => self.slots.contains(slot), - } - } -} - -impl Visitable for Assignments { - type Map = AssignmentsVisitMap; - - fn visit_map(self: &Self) -> Self::Map { - AssignmentsVisitMap { - assignments: vec![false; self.assignments().len()], - slots: DenseSlotSet::new(self.slot_readers().len()), - } - } - - fn reset_map(self: &Self, map: &mut Self::Map) { - let AssignmentsVisitMap { assignments, slots } = map; - assignments.clear(); - assignments.resize(self.assignments().len(), false); - if slots.len() != self.slot_readers().len() { - *slots = DenseSlotSet::new(self.slot_readers().len()); - } else { - slots.clear(); - } - } -} - -#[derive(Clone, Debug, PartialEq, Eq, Hash)] -struct DenseSlotSet(TypeParts); - -impl DenseSlotSet { - fn new(len: TypeLen) -> Self { - let TypeLen { - small_slots, - big_slots, - } = len; - Self(TypeParts { - small_slots: vec![false; small_slots.value.try_into().expect("length too big")] - .into_boxed_slice(), - big_slots: vec![false; big_slots.value.try_into().expect("length too big")] - .into_boxed_slice(), - }) - } - fn len(&self) -> TypeLen { - TypeLen { - small_slots: StatePartLen { - value: self.0.small_slots.len() as _, - _phantom: PhantomData, - }, - big_slots: StatePartLen { - value: self.0.big_slots.len() as _, - _phantom: PhantomData, - }, - } - } - fn clear(&mut self) { - let Self(TypeParts { - small_slots, - big_slots, - }) = self; - small_slots.fill(false); - big_slots.fill(false); - } -} - -impl StatePartsValue for DenseSlotSet { - type Value = Box<[bool]>; -} - -trait DenseSlotSetMethods: Extend> { - fn contains(&self, k: StatePartIndex) -> bool; - fn remove(&mut self, k: StatePartIndex) -> bool { - self.take(k).is_some() - } - fn take(&mut self, k: StatePartIndex) -> Option>; - fn replace(&mut self, k: StatePartIndex) -> Option>; - fn insert(&mut self, k: StatePartIndex) -> bool { - self.replace(k).is_none() - } -} - -impl Extend> for DenseSlotSet -where - Self: DenseSlotSetMethods, -{ - fn extend>>(&mut self, iter: T) { - iter.into_iter().for_each(|v| { - self.insert(v); - }); - } -} - -impl DenseSlotSetMethods for DenseSlotSet { - fn contains(&self, k: StatePartIndex) -> bool { - self.0.small_slots[k.as_usize()] - } - - fn take( - &mut self, - k: StatePartIndex, - ) -> Option> { - mem::replace(self.0.small_slots.get_mut(k.as_usize())?, false).then_some(k) - } - - fn replace( - &mut self, - k: StatePartIndex, - ) -> Option> { - mem::replace(&mut self.0.small_slots[k.as_usize()], true).then_some(k) - } -} - -impl DenseSlotSetMethods for DenseSlotSet { - fn contains(&self, k: StatePartIndex) -> bool { - self.0.big_slots[k.as_usize()] - } - - fn take( - &mut self, - k: StatePartIndex, - ) -> Option> { - mem::replace(self.0.big_slots.get_mut(k.as_usize())?, false).then_some(k) - } - - fn replace( - &mut self, - k: StatePartIndex, - ) -> Option> { - mem::replace(&mut self.0.big_slots[k.as_usize()], true).then_some(k) - } -} - -#[derive(Clone, Debug, Default, PartialEq, Eq, Hash)] -struct SlotVec(TypeParts); - -impl SlotVec { - fn is_empty(&self) -> bool { - let Self(TypeParts { - small_slots, - big_slots, - }) = self; - small_slots.is_empty() && big_slots.is_empty() - } -} - -impl StatePartsValue for SlotVec { - type Value = Vec>; -} - -#[derive(Clone, Debug, Default, PartialEq, Eq, Hash)] -struct SlotSet(TypeParts); - -impl SlotSet { - fn is_empty(&self) -> bool { - let Self(TypeParts { - small_slots, - big_slots, - }) = self; - small_slots.is_empty() && big_slots.is_empty() - } - fn for_each( - &self, - small_slots_fn: impl FnMut(StatePartIndex), - big_slots_fn: impl FnMut(StatePartIndex), - ) { - let Self(TypeParts { - small_slots, - big_slots, - }) = self; - small_slots.iter().copied().for_each(small_slots_fn); - big_slots.iter().copied().for_each(big_slots_fn); - } - fn all( - &self, - small_slots_fn: impl FnMut(StatePartIndex) -> bool, - big_slots_fn: impl FnMut(StatePartIndex) -> bool, - ) -> bool { - let Self(TypeParts { - small_slots, - big_slots, - }) = self; - small_slots.iter().copied().all(small_slots_fn) - && big_slots.iter().copied().all(big_slots_fn) - } -} - -impl StatePartsValue for SlotSet { - type Value = BTreeSet>; -} - -impl Extend> for SlotSet { - fn extend>>(&mut self, iter: T) { - self.0.small_slots.extend(iter); - } -} - -impl Extend> for SlotSet { - fn extend>>(&mut self, iter: T) { - self.0.big_slots.extend(iter); - } -} - -impl Extend> for SlotSet -where - Self: Extend>, -{ - fn extend>>(&mut self, iter: T) { - self.extend(iter.into_iter().flat_map(|v| v.iter())); - } -} - -impl Extend for SlotSet { - fn extend>(&mut self, iter: T) { - iter.into_iter().for_each( - |TypeIndexRange { - small_slots, - big_slots, - }| { - self.extend(small_slots.iter()); - self.extend(big_slots.iter()); - }, - ) - } -} - -impl Extend for SlotSet { - fn extend>(&mut self, iter: T) { - iter.into_iter().for_each( - |TypeArrayIndex { - small_slots, - big_slots, - }| { - self.extend([small_slots]); - self.extend([big_slots]); - }, - ) - } -} - -impl Extend> for SlotSet { - fn extend>>(&mut self, iter: T) { - self.extend(iter.into_iter().map(|v| v.index)); - } -} - -impl Extend for SlotSet { - fn extend>(&mut self, iter: T) { - iter.into_iter().for_each(|cond_body| match cond_body { - CondBody::IfTrue { cond } | CondBody::IfFalse { cond } => { - self.extend([cond.range]); - } - CondBody::MatchArm { - discriminant, - variant_index: _, - } => self.extend([discriminant]), - }) - } -} - -impl Extend for SlotSet { - fn extend>(&mut self, iter: T) { - self.extend(iter.into_iter().map(|v| v.body)) - } -} - -#[derive(Debug)] -struct Assignment { - inputs: SlotSet, - outputs: SlotSet, - conditions: Interned<[Cond]>, - insns: Vec, - source_location: SourceLocation, -} - -#[derive(Debug)] -struct SlotToAssignmentIndexFullMap(TypeParts); - -impl StatePartsValue for SlotToAssignmentIndexFullMap { - type Value = Box<[Vec]>; -} - -impl SlotToAssignmentIndexFullMap { - fn new(len: TypeLen) -> Self { - let TypeLen { - small_slots, - big_slots, - } = len; - Self(TypeParts { - small_slots: vec![Vec::new(); small_slots.value.try_into().expect("length too big")] - .into_boxed_slice(), - big_slots: vec![Vec::new(); big_slots.value.try_into().expect("length too big")] - .into_boxed_slice(), - }) - } - fn len(&self) -> TypeLen { - TypeLen { - small_slots: StatePartLen { - value: self.0.small_slots.len() as _, - _phantom: PhantomData, - }, - big_slots: StatePartLen { - value: self.0.big_slots.len() as _, - _phantom: PhantomData, - }, - } - } - fn keys_for_assignment( - &mut self, - assignment_index: usize, - ) -> SlotToAssignmentIndexFullMapKeysForAssignment<'_> { - SlotToAssignmentIndexFullMapKeysForAssignment { - map: self, - assignment_index, - } - } - fn for_each( - &self, - mut small_slots_fn: impl FnMut(StatePartIndex, &[usize]), - mut big_slots_fn: impl FnMut(StatePartIndex, &[usize]), - ) { - let Self(TypeParts { - small_slots, - big_slots, - }) = self; - small_slots.iter().enumerate().for_each(|(k, v)| { - small_slots_fn( - StatePartIndex { - value: k as _, - _phantom: PhantomData, - }, - v, - ) - }); - big_slots.iter().enumerate().for_each(|(k, v)| { - big_slots_fn( - StatePartIndex { - value: k as _, - _phantom: PhantomData, - }, - v, - ) - }); - } -} - -impl std::ops::Index> for SlotToAssignmentIndexFullMap { - type Output = Vec; - - fn index(&self, index: StatePartIndex) -> &Self::Output { - &self.0.small_slots[index.as_usize()] - } -} - -impl std::ops::IndexMut> for SlotToAssignmentIndexFullMap { - fn index_mut(&mut self, index: StatePartIndex) -> &mut Self::Output { - &mut self.0.small_slots[index.as_usize()] - } -} - -impl std::ops::Index> for SlotToAssignmentIndexFullMap { - type Output = Vec; - - fn index(&self, index: StatePartIndex) -> &Self::Output { - &self.0.big_slots[index.as_usize()] - } -} - -impl std::ops::IndexMut> for SlotToAssignmentIndexFullMap { - fn index_mut(&mut self, index: StatePartIndex) -> &mut Self::Output { - &mut self.0.big_slots[index.as_usize()] - } -} - -struct SlotToAssignmentIndexFullMapKeysForAssignment<'a> { - map: &'a mut SlotToAssignmentIndexFullMap, - assignment_index: usize, -} - -impl<'a, K: StatePartKind> Extend<&'a StatePartIndex> - for SlotToAssignmentIndexFullMapKeysForAssignment<'_> -where - Self: Extend>, -{ - fn extend>>(&mut self, iter: T) { - self.extend(iter.into_iter().copied()); - } -} - -impl Extend> - for SlotToAssignmentIndexFullMapKeysForAssignment<'_> -where - SlotToAssignmentIndexFullMap: IndexMut, Output = Vec>, -{ - fn extend>>(&mut self, iter: T) { - iter.into_iter() - .for_each(|slot| self.map[slot].push(self.assignment_index)); - } -} - -impl<'a> Extend<&'a SlotSet> for SlotToAssignmentIndexFullMapKeysForAssignment<'_> { - fn extend>(&mut self, iter: T) { - iter.into_iter().for_each( - |SlotSet(TypeParts { - small_slots, - big_slots, - })| { - self.extend(small_slots); - self.extend(big_slots); - }, - ); - } -} - -impl<'a> Extend<&'a Cond> for SlotToAssignmentIndexFullMapKeysForAssignment<'_> { - fn extend>(&mut self, iter: T) { - iter.into_iter().for_each(|cond| match cond.body { - CondBody::IfTrue { cond } | CondBody::IfFalse { cond } => { - let CompiledValue { - range: - TypeIndexRange { - small_slots, - big_slots, - }, - layout: _, - write: _, - } = cond; - self.extend(small_slots.iter()); - self.extend(big_slots.iter()); - } - CondBody::MatchArm { - discriminant, - variant_index: _, - } => self.extend([discriminant]), - }); - } -} - -impl Assignment { - fn new( - conditions: Interned<[Cond]>, - insns: Vec, - source_location: SourceLocation, - ) -> Self { - let mut inputs = SlotSet::default(); - let mut outputs = SlotSet::default(); - for insn in &insns { - let insn = match insn { - InsnOrLabel::Insn(insn) => insn, - InsnOrLabel::Label(_) => continue, - }; - for InsnField { ty, kind } in insn.fields() { - match (kind, ty) { - (InsnFieldKind::Input, InsnFieldType::SmallSlot(&slot)) => { - inputs.extend([slot]); - } - (InsnFieldKind::Input, InsnFieldType::BigSlot(&slot)) => { - inputs.extend([slot]); - } - ( - InsnFieldKind::Input, - InsnFieldType::SmallSlotArrayIndexed(&array_indexed), - ) => { - array_indexed.for_each_target(|slot| inputs.extend([slot])); - inputs.extend(array_indexed.indexes); - } - (InsnFieldKind::Input, InsnFieldType::BigSlotArrayIndexed(&array_indexed)) => { - array_indexed.for_each_target(|slot| inputs.extend([slot])); - inputs.extend(array_indexed.indexes); - } - (InsnFieldKind::Output, InsnFieldType::SmallSlot(&slot)) => { - outputs.extend([slot]); - } - (InsnFieldKind::Output, InsnFieldType::BigSlot(&slot)) => { - outputs.extend([slot]); - } - ( - InsnFieldKind::Output, - InsnFieldType::SmallSlotArrayIndexed(&array_indexed), - ) => { - array_indexed.for_each_target(|slot| { - outputs.extend([slot]); - }); - inputs.extend(array_indexed.indexes); - } - (InsnFieldKind::Output, InsnFieldType::BigSlotArrayIndexed(&array_indexed)) => { - array_indexed.for_each_target(|slot| { - outputs.extend([slot]); - }); - inputs.extend(array_indexed.indexes); - } - ( - _, - InsnFieldType::Memory(_) - | InsnFieldType::SmallUInt(_) - | InsnFieldType::SmallSInt(_) - | InsnFieldType::InternedBigInt(_) - | InsnFieldType::U8(_) - | InsnFieldType::USize(_) - | InsnFieldType::Empty(_), - ) - | ( - InsnFieldKind::Immediate - | InsnFieldKind::Memory - | InsnFieldKind::BranchTarget, - _, - ) => {} - } - } - } - Self { - inputs, - outputs, - conditions, - insns, - source_location, - } - } -} - -#[derive(Debug)] -struct RegisterReset { - is_async: bool, - init: CompiledValue, - rst: StatePartIndex, -} - -#[derive(Debug, Clone, Copy)] -struct ClockTrigger { - last_clk_was_low: StatePartIndex, - clk: StatePartIndex, - clk_triggered: StatePartIndex, - source_location: SourceLocation, -} - -#[derive(Debug)] -struct Register { - value: CompiledValue, - clk_triggered: StatePartIndex, - reset: Option, - source_location: SourceLocation, -} - -#[derive(Debug)] - -struct MemoryPort { - clk_triggered: StatePartIndex, - addr_delayed: Vec>, - en_delayed: Vec>, - data_layout: CompiledTypeLayout, - read_data_delayed: Vec, - write_data_delayed: Vec, - write_mask_delayed: Vec, - write_mode_delayed: Vec>, - write_insns: Vec, -} - -struct MemoryPortReadInsns<'a> { - addr: StatePartIndex, - en: StatePartIndex, - write_mode: Option>, - data: TypeIndexRange, - insns: &'a mut Vec, -} - -struct MemoryPortWriteInsns<'a> { - addr: StatePartIndex, - en: StatePartIndex, - write_mode: Option>, - data: TypeIndexRange, - mask: TypeIndexRange, - insns: &'a mut Vec, -} - -#[derive(Debug)] -struct Memory { - mem: Mem, - memory: StatePartIndex, - trace: TraceMem, - ports: Vec, -} - -#[derive(Copy, Clone)] -enum MakeTraceDeclTarget { - Expr(Expr), - Memory { - id: TraceMemoryId, - depth: usize, - stride: usize, - start: usize, - ty: CanonicalType, - }, -} - -impl MakeTraceDeclTarget { - fn flow(self) -> Flow { - match self { - MakeTraceDeclTarget::Expr(expr) => Expr::flow(expr), - MakeTraceDeclTarget::Memory { .. } => Flow::Duplex, - } - } - fn ty(self) -> CanonicalType { - match self { - MakeTraceDeclTarget::Expr(expr) => Expr::ty(expr), - MakeTraceDeclTarget::Memory { ty, .. } => ty, - } - } -} - -struct DebugOpaque(T); - -impl fmt::Debug for DebugOpaque { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_str("<...>") - } -} - -#[derive(Debug)] -pub struct Compiler { - insns: Insns, - original_base_module: Interned>, - base_module: Interned>, - modules: HashMap, - compiled_values: HashMap>, - compiled_exprs: HashMap, CompiledExpr>, - compiled_exprs_to_values: HashMap, CompiledValue>, - decl_conditions: HashMap>, - compiled_values_to_dyn_array_indexes: - HashMap, StatePartIndex>, - compiled_value_bool_dest_is_small_map: - HashMap, StatePartIndex>, - assignments: Assignments, - clock_triggers: Vec, - compiled_value_to_clock_trigger_map: HashMap, ClockTrigger>, - enum_discriminants: HashMap, StatePartIndex>, - registers: Vec, - traces: SimTraces>>, - memories: Vec, - dump_assignments_dot: Option>>, -} - -impl Compiler { - pub fn new(base_module: Interned>) -> Self { - let original_base_module = base_module; - let base_module = deduce_resets(base_module, true) - .unwrap_or_else(|e| panic!("failed to deduce reset types: {e}")); - Self { - insns: Insns::new(), - original_base_module, - base_module, - modules: HashMap::new(), - compiled_values: HashMap::new(), - compiled_exprs: HashMap::new(), - compiled_exprs_to_values: HashMap::new(), - decl_conditions: HashMap::new(), - compiled_values_to_dyn_array_indexes: HashMap::new(), - compiled_value_bool_dest_is_small_map: HashMap::new(), - assignments: Assignments::default(), - clock_triggers: Vec::new(), - compiled_value_to_clock_trigger_map: HashMap::new(), - enum_discriminants: HashMap::new(), - registers: Vec::new(), - traces: SimTraces(Vec::new()), - memories: Vec::new(), - dump_assignments_dot: None, - } - } - #[doc(hidden)] - /// This is explicitly unstable and may be changed/removed at any time - pub fn dump_assignments_dot(&mut self, callback: Box) { - self.dump_assignments_dot = Some(DebugOpaque(callback)); - } - fn new_sim_trace(&mut self, kind: SimTraceKind) -> TraceScalarId { - let id = TraceScalarId(self.traces.0.len()); - self.traces.0.push(SimTrace { - kind, - state: (), - last_state: (), - }); - id - } - fn make_trace_scalar_helper( - &mut self, - instantiated_module: InstantiatedModule, - target: MakeTraceDeclTarget, - source_location: SourceLocation, - small_kind: impl FnOnce(StatePartIndex) -> SimTraceKind, - big_kind: impl FnOnce(StatePartIndex) -> SimTraceKind, - ) -> TraceLocation { - match target { - MakeTraceDeclTarget::Expr(target) => { - let compiled_value = self.compile_expr(instantiated_module, target); - let compiled_value = self.compiled_expr_to_value(compiled_value, source_location); - TraceLocation::Scalar(self.new_sim_trace(match compiled_value.range.len() { - TypeLen::A_SMALL_SLOT => small_kind(compiled_value.range.small_slots.start), - TypeLen::A_BIG_SLOT => big_kind(compiled_value.range.big_slots.start), - _ => unreachable!(), - })) - } - MakeTraceDeclTarget::Memory { - id, - depth, - stride, - start, - ty, - } => TraceLocation::Memory(TraceMemoryLocation { - id, - depth, - stride, - start, - len: ty.bit_width(), - }), - } - } - fn make_trace_scalar( - &mut self, - instantiated_module: InstantiatedModule, - target: MakeTraceDeclTarget, - name: Interned, - source_location: SourceLocation, - ) -> TraceDecl { - let flow = target.flow(); - match target.ty() { - CanonicalType::UInt(ty) => TraceUInt { - location: self.make_trace_scalar_helper( - instantiated_module, - target, - source_location, - |index| SimTraceKind::SmallUInt { index, ty }, - |index| SimTraceKind::BigUInt { index, ty }, - ), - name, - ty, - flow, - } - .into(), - CanonicalType::SInt(ty) => TraceSInt { - location: self.make_trace_scalar_helper( - instantiated_module, - target, - source_location, - |index| SimTraceKind::SmallSInt { index, ty }, - |index| SimTraceKind::BigSInt { index, ty }, - ), - name, - ty, - flow, - } - .into(), - CanonicalType::Bool(_) => TraceBool { - location: self.make_trace_scalar_helper( - instantiated_module, - target, - source_location, - |index| SimTraceKind::SmallBool { index }, - |index| SimTraceKind::BigBool { index }, - ), - name, - flow, - } - .into(), - CanonicalType::Array(_) => unreachable!(), - CanonicalType::Enum(ty) => { - assert_eq!(ty.discriminant_bit_width(), ty.type_properties().bit_width); - let location = match target { - MakeTraceDeclTarget::Expr(target) => { - let compiled_value = self.compile_expr(instantiated_module, target); - let compiled_value = - self.compiled_expr_to_value(compiled_value, source_location); - let discriminant = self.compile_enum_discriminant( - compiled_value.map_ty(Enum::from_canonical), - source_location, - ); - TraceLocation::Scalar(self.new_sim_trace(SimTraceKind::EnumDiscriminant { - index: discriminant, - ty, - })) - } - MakeTraceDeclTarget::Memory { - id, - depth, - stride, - start, - ty: _, - } => TraceLocation::Memory(TraceMemoryLocation { - id, - depth, - stride, - start, - len: ty.type_properties().bit_width, - }), - }; - TraceFieldlessEnum { - location, - name, - ty, - flow, - } - .into() - } - CanonicalType::Bundle(_) => unreachable!(), - CanonicalType::AsyncReset(_) => TraceAsyncReset { - location: self.make_trace_scalar_helper( - instantiated_module, - target, - source_location, - |index| SimTraceKind::SmallAsyncReset { index }, - |index| SimTraceKind::BigAsyncReset { index }, - ), - name, - flow, - } - .into(), - CanonicalType::SyncReset(_) => TraceSyncReset { - location: self.make_trace_scalar_helper( - instantiated_module, - target, - source_location, - |index| SimTraceKind::SmallSyncReset { index }, - |index| SimTraceKind::BigSyncReset { index }, - ), - name, - flow, - } - .into(), - CanonicalType::Reset(_) => unreachable!(), - CanonicalType::Clock(_) => TraceClock { - location: self.make_trace_scalar_helper( - instantiated_module, - target, - source_location, - |index| SimTraceKind::SmallClock { index }, - |index| SimTraceKind::BigClock { index }, - ), - name, - flow, - } - .into(), - } - } - fn make_trace_decl_child( - &mut self, - instantiated_module: InstantiatedModule, - target: MakeTraceDeclTarget, - name: Interned, - source_location: SourceLocation, - ) -> TraceDecl { - match target.ty() { - CanonicalType::Array(ty) => { - let elements = Interned::from_iter((0..ty.len()).map(|index| { - self.make_trace_decl_child( - instantiated_module, - match target { - MakeTraceDeclTarget::Expr(target) => MakeTraceDeclTarget::Expr( - Expr::::from_canonical(target)[index], - ), - MakeTraceDeclTarget::Memory { - id, - depth, - stride, - start, - ty: _, - } => MakeTraceDeclTarget::Memory { - id, - depth, - stride, - start: start + ty.element().bit_width() * index, - ty: ty.element(), - }, - }, - Intern::intern_owned(format!("[{index}]")), - source_location, - ) - })); - TraceArray { - name, - elements, - ty, - flow: target.flow(), - } - .into() - } - CanonicalType::Enum(ty) => { - if ty.variants().iter().all(|v| v.ty.is_none()) { - self.make_trace_scalar(instantiated_module, target, name, source_location) - } else { - let flow = target.flow(); - let location = match target { - MakeTraceDeclTarget::Expr(target) => { - let compiled_value = self.compile_expr(instantiated_module, target); - let compiled_value = - self.compiled_expr_to_value(compiled_value, source_location); - let discriminant = self.compile_enum_discriminant( - compiled_value.map_ty(Enum::from_canonical), - source_location, - ); - TraceLocation::Scalar(self.new_sim_trace( - SimTraceKind::EnumDiscriminant { - index: discriminant, - ty, - }, - )) - } - MakeTraceDeclTarget::Memory { - id, - depth, - stride, - start, - ty: _, - } => TraceLocation::Memory(TraceMemoryLocation { - id, - depth, - stride, - start, - len: ty.discriminant_bit_width(), - }), - }; - let discriminant = TraceEnumDiscriminant { - location, - name: "$tag".intern(), - ty, - flow, - }; - let non_empty_fields = - Interned::from_iter(ty.variants().into_iter().enumerate().flat_map( - |(variant_index, variant)| { - variant.ty.map(|variant_ty| { - self.make_trace_decl_child( - instantiated_module, - match target { - MakeTraceDeclTarget::Expr(target) => { - MakeTraceDeclTarget::Expr( - ops::VariantAccess::new_by_index( - Expr::::from_canonical(target), - variant_index, - ) - .to_expr(), - ) - } - MakeTraceDeclTarget::Memory { - id, - depth, - stride, - start, - ty: _, - } => MakeTraceDeclTarget::Memory { - id, - depth, - stride, - start: start + ty.discriminant_bit_width(), - ty: variant_ty, - }, - }, - variant.name, - source_location, - ) - }) - }, - )); - TraceEnumWithFields { - name, - discriminant, - non_empty_fields, - ty, - flow, - } - .into() - } - } - CanonicalType::Bundle(ty) => { - let fields = Interned::from_iter(ty.fields().iter().zip(ty.field_offsets()).map( - |(field, field_offset)| { - self.make_trace_decl_child( - instantiated_module, - match target { - MakeTraceDeclTarget::Expr(target) => { - MakeTraceDeclTarget::Expr(Expr::field( - Expr::::from_canonical(target), - &field.name, - )) - } - MakeTraceDeclTarget::Memory { - id, - depth, - stride, - start, - ty: _, - } => MakeTraceDeclTarget::Memory { - id, - depth, - stride, - start: start + field_offset, - ty: field.ty, - }, - }, - field.name, - source_location, - ) - }, - )); - TraceBundle { - name, - fields, - ty, - flow: target.flow(), - } - .into() - } - CanonicalType::UInt(_) - | CanonicalType::SInt(_) - | CanonicalType::Bool(_) - | CanonicalType::AsyncReset(_) - | CanonicalType::SyncReset(_) - | CanonicalType::Reset(_) - | CanonicalType::Clock(_) => { - self.make_trace_scalar(instantiated_module, target, name, source_location) - } - } - } - fn make_trace_decl( - &mut self, - instantiated_module: InstantiatedModule, - target_base: TargetBase, - ) -> TraceDecl { - let target = MakeTraceDeclTarget::Expr(target_base.to_expr()); - match target_base { - TargetBase::ModuleIO(module_io) => TraceModuleIO { - name: module_io.name(), - child: self - .make_trace_decl_child( - instantiated_module, - target, - module_io.name(), - module_io.source_location(), - ) - .intern(), - ty: module_io.ty(), - flow: module_io.flow(), - } - .into(), - TargetBase::MemPort(mem_port) => { - let name = Intern::intern_owned(mem_port.port_name().to_string()); - let TraceDecl::Scope(TraceScope::Bundle(bundle)) = self.make_trace_decl_child( - instantiated_module, - target, - name, - mem_port.source_location(), - ) else { - unreachable!() - }; - TraceMemPort { - name, - bundle, - ty: mem_port.ty(), - } - .into() - } - TargetBase::Reg(reg) => TraceReg { - name: reg.name(), - child: self - .make_trace_decl_child( - instantiated_module, - target, - reg.name(), - reg.source_location(), - ) - .intern(), - ty: reg.ty(), - } - .into(), - TargetBase::RegSync(reg) => TraceReg { - name: reg.name(), - child: self - .make_trace_decl_child( - instantiated_module, - target, - reg.name(), - reg.source_location(), - ) - .intern(), - ty: reg.ty(), - } - .into(), - TargetBase::RegAsync(reg) => TraceReg { - name: reg.name(), - child: self - .make_trace_decl_child( - instantiated_module, - target, - reg.name(), - reg.source_location(), - ) - .intern(), - ty: reg.ty(), - } - .into(), - TargetBase::Wire(wire) => TraceWire { - name: wire.name(), - child: self - .make_trace_decl_child( - instantiated_module, - target, - wire.name(), - wire.source_location(), - ) - .intern(), - ty: wire.ty(), - } - .into(), - TargetBase::Instance(instance) => { - let TraceDecl::Scope(TraceScope::Bundle(instance_io)) = self.make_trace_decl_child( - instantiated_module, - target, - instance.name(), - instance.source_location(), - ) else { - unreachable!() - }; - let compiled_module = &self.modules[&InstantiatedModule::Child { - parent: instantiated_module.intern(), - instance: instance.intern(), - }]; - TraceInstance { - name: instance.name(), - instance_io, - module: compiled_module.trace_decls, - ty: instance.ty(), - } - .into() - } - } - } - fn compile_value( - &mut self, - target: TargetInInstantiatedModule, - ) -> CompiledValue { - if let Some(&retval) = self.compiled_values.get(&target) { - return retval; - } - let retval = match target.target { - Target::Base(base) => { - let unprefixed_layout = CompiledTypeLayout::get(base.canonical_ty()); - let layout = unprefixed_layout.with_prefixed_debug_names(&format!( - "{:?}.{:?}", - target.instantiated_module, - base.target_name() - )); - let range = self.insns.allocate_variable(&layout.layout); - let write = match *base { - TargetBase::ModuleIO(_) - | TargetBase::MemPort(_) - | TargetBase::Wire(_) - | TargetBase::Instance(_) => None, - TargetBase::Reg(_) | TargetBase::RegSync(_) | TargetBase::RegAsync(_) => { - let write_layout = unprefixed_layout.with_prefixed_debug_names(&format!( - "{:?}.{:?}$next", - target.instantiated_module, - base.target_name() - )); - Some(( - write_layout, - self.insns.allocate_variable(&write_layout.layout), - )) - } - }; - CompiledValue { - range, - layout, - write, - } - } - Target::Child(target_child) => { - let parent = self.compile_value(TargetInInstantiatedModule { - instantiated_module: target.instantiated_module, - target: *target_child.parent(), - }); - match *target_child.path_element() { - TargetPathElement::BundleField(TargetPathBundleField { name }) => { - parent.map_ty(Bundle::from_canonical).field_by_name(name) - } - TargetPathElement::ArrayElement(TargetPathArrayElement { index }) => { - parent.map_ty(Array::from_canonical).element(index) - } - TargetPathElement::DynArrayElement(_) => unreachable!(), - } - } - }; - self.compiled_values.insert(target, retval); - retval - } - fn compiled_expr_to_value( - &mut self, - expr: CompiledExpr, - source_location: SourceLocation, - ) -> CompiledValue { - if let Some(&retval) = self.compiled_exprs_to_values.get(&expr) { - return retval; - } - assert!( - expr.static_part.layout.ty.is_passive(), - "invalid expression passed to compiled_expr_to_value -- type must be passive", - ); - let CompiledExpr { - static_part, - indexes, - } = expr; - let retval = if indexes.as_ref().is_empty() { - CompiledValue { - layout: static_part.layout, - range: static_part.range, - write: None, - } - } else { - let layout = static_part.layout.with_anonymized_debug_info(); - let retval = CompiledValue { - layout, - range: self.insns.allocate_variable(&layout.layout), - write: None, - }; - let TypeIndexRange { - small_slots, - big_slots, - } = retval.range; - self.add_assignment( - Interned::default(), - small_slots - .iter() - .zip(static_part.range.small_slots.iter()) - .map(|(dest, base)| Insn::ReadSmallIndexed { - dest, - src: StatePartArrayIndexed { - base, - indexes: indexes.small_slots, - }, - }) - .chain( - big_slots - .iter() - .zip(static_part.range.big_slots.iter()) - .map(|(dest, base)| Insn::ReadIndexed { - dest, - src: StatePartArrayIndexed { - base, - indexes: indexes.big_slots, - }, - }), - ), - source_location, - ); - retval - }; - self.compiled_exprs_to_values.insert(expr, retval); - retval - } - fn add_assignment>( - &mut self, - conditions: Interned<[Cond]>, - insns: impl IntoIterator, - source_location: SourceLocation, - ) { - let insns = Vec::from_iter(insns.into_iter().map(Into::into)); - self.assignments - .push(Assignment::new(conditions, insns, source_location)); - } - fn simple_big_expr_input( - &mut self, - instantiated_module: InstantiatedModule, - input: Expr, - ) -> StatePartIndex { - let input = self.compile_expr(instantiated_module, input); - let input = - self.compiled_expr_to_value(input, instantiated_module.leaf_module().source_location()); - assert_eq!(input.range.len(), TypeLen::A_BIG_SLOT); - input.range.big_slots.start - } - fn compile_expr_helper( - &mut self, - instantiated_module: InstantiatedModule, - dest_ty: CanonicalType, - make_insns: impl FnOnce(&mut Self, TypeIndexRange) -> Vec, - ) -> CompiledValue { - let layout = CompiledTypeLayout::get(dest_ty); - let range = self.insns.allocate_variable(&layout.layout); - let retval = CompiledValue { - layout, - range, - write: None, - }; - let insns = make_insns(self, range); - self.add_assignment( - Interned::default(), - insns, - instantiated_module.leaf_module().source_location(), - ); - retval - } - fn simple_nary_big_expr_helper( - &mut self, - instantiated_module: InstantiatedModule, - dest_ty: CanonicalType, - make_insns: impl FnOnce(StatePartIndex) -> Vec, - ) -> CompiledValue { - self.compile_expr_helper(instantiated_module, dest_ty, |_, dest| { - assert_eq!(dest.len(), TypeLen::A_BIG_SLOT); - make_insns(dest.big_slots.start) - }) - } - fn simple_nary_big_expr( - &mut self, - instantiated_module: InstantiatedModule, - dest_ty: CanonicalType, - inputs: [Expr; N], - make_insns: impl FnOnce( - StatePartIndex, - [StatePartIndex; N], - ) -> Vec, - ) -> CompiledValue { - let inputs = inputs.map(|input| self.simple_big_expr_input(instantiated_module, input)); - self.simple_nary_big_expr_helper(instantiated_module, dest_ty, |dest| { - make_insns(dest, inputs) - }) - } - fn compiled_value_to_dyn_array_index( - &mut self, - compiled_value: CompiledValue, - source_location: SourceLocation, - ) -> StatePartIndex { - if let Some(&retval) = self - .compiled_values_to_dyn_array_indexes - .get(&compiled_value) - { - return retval; - } - let mut ty = compiled_value.layout.ty; - ty.width = ty.width.min(SmallUInt::BITS as usize); - let retval = match compiled_value.range.len() { - TypeLen::A_SMALL_SLOT => compiled_value.range.small_slots.start, - TypeLen::A_BIG_SLOT => { - let debug_data = SlotDebugData { - name: Interned::default(), - ty: ty.canonical(), - }; - let dest = self - .insns - .allocate_variable(&TypeLayout { - small_slots: StatePartLayout::scalar(debug_data, ()), - big_slots: StatePartLayout::empty(), - }) - .small_slots - .start; - self.add_assignment( - Interned::default(), - vec![Insn::CastBigToArrayIndex { - dest, - src: compiled_value.range.big_slots.start, - }], - source_location, - ); - dest - } - _ => unreachable!(), - }; - self.compiled_values_to_dyn_array_indexes - .insert(compiled_value, retval); - retval - } - fn compiled_value_bool_dest_is_small( - &mut self, - compiled_value: CompiledValue, - source_location: SourceLocation, - ) -> StatePartIndex { - if let Some(&retval) = self - .compiled_value_bool_dest_is_small_map - .get(&compiled_value) - { - return retval; - } - let retval = match compiled_value.range.len() { - TypeLen::A_SMALL_SLOT => compiled_value.range.small_slots.start, - TypeLen::A_BIG_SLOT => { - let debug_data = SlotDebugData { - name: Interned::default(), - ty: Bool.canonical(), - }; - let dest = self - .insns - .allocate_variable(&TypeLayout { - small_slots: StatePartLayout::scalar(debug_data, ()), - big_slots: StatePartLayout::empty(), - }) - .small_slots - .start; - self.add_assignment( - Interned::default(), - vec![Insn::IsNonZeroDestIsSmall { - dest, - src: compiled_value.range.big_slots.start, - }], - source_location, - ); - dest - } - _ => unreachable!(), - }; - self.compiled_value_bool_dest_is_small_map - .insert(compiled_value, retval); - retval - } - fn compile_cast_scalar_to_bits( - &mut self, - instantiated_module: InstantiatedModule, - arg: Expr, - cast_fn: impl FnOnce(Expr) -> Expr, - ) -> CompiledValue { - let arg = Expr::::from_canonical(arg); - let retval = cast_fn(arg); - let retval = self.compile_expr(instantiated_module, Expr::canonical(retval)); - let retval = self - .compiled_expr_to_value(retval, instantiated_module.leaf_module().source_location()); - retval.map_ty(UInt::from_canonical) - } - fn compile_cast_aggregate_to_bits( - &mut self, - instantiated_module: InstantiatedModule, - parts: impl IntoIterator>, - ) -> CompiledValue { - let retval = parts - .into_iter() - .map(|part| part.cast_to_bits()) - .reduce(|accumulator, part| accumulator | (part << Expr::ty(accumulator).width)) - .unwrap_or_else(|| UInt[0].zero().to_expr()); - let retval = self.compile_expr(instantiated_module, Expr::canonical(retval)); - let retval = self - .compiled_expr_to_value(retval, instantiated_module.leaf_module().source_location()); - retval.map_ty(UInt::from_canonical) - } - fn compile_cast_to_bits( - &mut self, - instantiated_module: InstantiatedModule, - expr: ops::CastToBits, - ) -> CompiledValue { - match Expr::ty(expr.arg()) { - CanonicalType::UInt(_) => { - self.compile_cast_scalar_to_bits(instantiated_module, expr.arg(), |arg| arg) - } - CanonicalType::SInt(ty) => self.compile_cast_scalar_to_bits( - instantiated_module, - expr.arg(), - |arg: Expr| arg.cast_to(ty.as_same_width_uint()), - ), - CanonicalType::Bool(_) - | CanonicalType::AsyncReset(_) - | CanonicalType::SyncReset(_) - | CanonicalType::Reset(_) - | CanonicalType::Clock(_) => self.compile_cast_scalar_to_bits( - instantiated_module, - expr.arg(), - |arg: Expr| arg.cast_to(UInt[1]), - ), - CanonicalType::Array(ty) => self.compile_cast_aggregate_to_bits( - instantiated_module, - (0..ty.len()).map(|index| Expr::::from_canonical(expr.arg())[index]), - ), - CanonicalType::Enum(ty) => self - .simple_nary_big_expr( - instantiated_module, - UInt[ty.type_properties().bit_width].canonical(), - [Expr::canonical(expr.arg())], - |dest, [src]| vec![Insn::Copy { dest, src }], - ) - .map_ty(UInt::from_canonical), - CanonicalType::Bundle(ty) => self.compile_cast_aggregate_to_bits( - instantiated_module, - ty.fields().iter().map(|field| { - Expr::field(Expr::::from_canonical(expr.arg()), &field.name) - }), - ), - } - } - fn compile_cast_bits_to( - &mut self, - instantiated_module: InstantiatedModule, - expr: ops::CastBitsTo, - ) -> CompiledValue { - let retval = match expr.ty() { - CanonicalType::UInt(_) => Expr::canonical(expr.arg()), - CanonicalType::SInt(ty) => Expr::canonical(expr.arg().cast_to(ty)), - CanonicalType::Bool(ty) => Expr::canonical(expr.arg().cast_to(ty)), - CanonicalType::Array(ty) => { - let stride = ty.element().bit_width(); - Expr::::canonical( - ops::ArrayLiteral::new( - ty.element(), - Interned::from_iter((0..ty.len()).map(|index| { - let start = stride * index; - let end = start + stride; - expr.arg()[start..end].cast_bits_to(ty.element()) - })), - ) - .to_expr(), - ) - } - ty @ CanonicalType::Enum(_) => { - return self.simple_nary_big_expr( - instantiated_module, - ty, - [Expr::canonical(expr.arg())], - |dest, [src]| vec![Insn::Copy { dest, src }], - ); - } - CanonicalType::Bundle(ty) => Expr::canonical( - ops::BundleLiteral::new( - ty, - Interned::from_iter(ty.field_offsets().iter().zip(&ty.fields()).map( - |(&offset, &field)| { - let end = offset + field.ty.bit_width(); - expr.arg()[offset..end].cast_bits_to(field.ty) - }, - )), - ) - .to_expr(), - ), - CanonicalType::AsyncReset(ty) => Expr::canonical(expr.arg().cast_to(ty)), - CanonicalType::SyncReset(ty) => Expr::canonical(expr.arg().cast_to(ty)), - CanonicalType::Reset(_) => unreachable!(), - CanonicalType::Clock(ty) => Expr::canonical(expr.arg().cast_to(ty)), - }; - let retval = self.compile_expr(instantiated_module, Expr::canonical(retval)); - self.compiled_expr_to_value(retval, instantiated_module.leaf_module().source_location()) - } - fn compile_aggregate_literal( - &mut self, - instantiated_module: InstantiatedModule, - dest_ty: CanonicalType, - inputs: Interned<[Expr]>, - ) -> CompiledValue { - self.compile_expr_helper(instantiated_module, dest_ty, |this, dest| { - let mut insns = Vec::new(); - let mut offset = TypeIndex::ZERO; - for input in inputs { - let input = this.compile_expr(instantiated_module, input); - let input = this - .compiled_expr_to_value( - input, - instantiated_module.leaf_module().source_location(), - ) - .range; - insns.extend( - input.insns_for_copy_to(dest.slice(TypeIndexRange::new(offset, input.len()))), - ); - offset = offset.offset(input.len().as_index()); - } - insns - }) - } - fn compile_expr( - &mut self, - instantiated_module: InstantiatedModule, - expr: Expr, - ) -> CompiledExpr { - if let Some(&retval) = self.compiled_exprs.get(&expr) { - return retval; - } - let mut cast_bit = |arg: Expr| { - let src_signed = match Expr::ty(arg) { - CanonicalType::UInt(_) => false, - CanonicalType::SInt(_) => true, - CanonicalType::Bool(_) => false, - CanonicalType::Array(_) => unreachable!(), - CanonicalType::Enum(_) => unreachable!(), - CanonicalType::Bundle(_) => unreachable!(), - CanonicalType::AsyncReset(_) => false, - CanonicalType::SyncReset(_) => false, - CanonicalType::Reset(_) => false, - CanonicalType::Clock(_) => false, - }; - let dest_signed = match Expr::ty(expr) { - CanonicalType::UInt(_) => false, - CanonicalType::SInt(_) => true, - CanonicalType::Bool(_) => false, - CanonicalType::Array(_) => unreachable!(), - CanonicalType::Enum(_) => unreachable!(), - CanonicalType::Bundle(_) => unreachable!(), - CanonicalType::AsyncReset(_) => false, - CanonicalType::SyncReset(_) => false, - CanonicalType::Reset(_) => false, - CanonicalType::Clock(_) => false, - }; - self.simple_nary_big_expr(instantiated_module, Expr::ty(expr), [arg], |dest, [src]| { - match (src_signed, dest_signed) { - (false, false) | (true, true) => { - vec![Insn::Copy { dest, src }] - } - (false, true) => vec![Insn::CastToSInt { - dest, - src, - dest_width: 1, - }], - (true, false) => vec![Insn::CastToUInt { - dest, - src, - dest_width: 1, - }], - } - }) - .into() - }; - let retval: CompiledExpr<_> = match *Expr::expr_enum(expr) { - ExprEnum::UIntLiteral(expr) => self - .simple_nary_big_expr( - instantiated_module, - expr.ty().canonical(), - [], - |dest, []| { - vec![Insn::Const { - dest, - value: expr.to_bigint().intern_sized(), - }] - }, - ) - .into(), - ExprEnum::SIntLiteral(expr) => self - .simple_nary_big_expr( - instantiated_module, - expr.ty().canonical(), - [], - |dest, []| { - vec![Insn::Const { - dest, - value: expr.to_bigint().intern_sized(), - }] - }, - ) - .into(), - ExprEnum::BoolLiteral(expr) => self - .simple_nary_big_expr(instantiated_module, Bool.canonical(), [], |dest, []| { - vec![Insn::Const { - dest, - value: BigInt::from(expr).intern_sized(), - }] - }) - .into(), - ExprEnum::BundleLiteral(literal) => self - .compile_aggregate_literal( - instantiated_module, - Expr::ty(expr), - literal.field_values(), - ) - .into(), - ExprEnum::ArrayLiteral(literal) => self - .compile_aggregate_literal( - instantiated_module, - Expr::ty(expr), - literal.element_values(), - ) - .into(), - ExprEnum::EnumLiteral(expr) => { - let enum_bits_ty = UInt[expr.ty().type_properties().bit_width]; - let enum_bits = if let Some(variant_value) = expr.variant_value() { - ( - UInt[expr.ty().discriminant_bit_width()] - .from_int_wrapping(expr.variant_index()), - variant_value, - ) - .cast_to_bits() - .cast_to(enum_bits_ty) - } else { - enum_bits_ty - .from_int_wrapping(expr.variant_index()) - .to_expr() - }; - self.compile_expr( - instantiated_module, - enum_bits.cast_bits_to(expr.ty().canonical()), - ) - } - ExprEnum::Uninit(expr) => self.compile_expr( - instantiated_module, - UInt[expr.ty().bit_width()].zero().cast_bits_to(expr.ty()), - ), - ExprEnum::NotU(expr) => self - .simple_nary_big_expr( - instantiated_module, - Expr::ty(expr.arg()).canonical(), - [Expr::canonical(expr.arg())], - |dest, [src]| { - vec![Insn::NotU { - dest, - src, - width: Expr::ty(expr.arg()).width(), - }] - }, - ) - .into(), - ExprEnum::NotS(expr) => self - .simple_nary_big_expr( - instantiated_module, - Expr::ty(expr.arg()).canonical(), - [Expr::canonical(expr.arg())], - |dest, [src]| vec![Insn::NotS { dest, src }], - ) - .into(), - ExprEnum::NotB(expr) => self - .simple_nary_big_expr( - instantiated_module, - Expr::ty(expr.arg()).canonical(), - [Expr::canonical(expr.arg())], - |dest, [src]| { - vec![Insn::NotU { - dest, - src, - width: 1, - }] - }, - ) - .into(), - ExprEnum::Neg(expr) => self - .simple_nary_big_expr( - instantiated_module, - expr.ty().canonical(), - [Expr::canonical(expr.arg())], - |dest, [src]| vec![Insn::Neg { dest, src }], - ) - .into(), - ExprEnum::BitAndU(expr) => self - .simple_nary_big_expr( - instantiated_module, - expr.ty().canonical(), - [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], - |dest, [lhs, rhs]| vec![Insn::And { dest, lhs, rhs }], - ) - .into(), - ExprEnum::BitAndS(expr) => self - .simple_nary_big_expr( - instantiated_module, - expr.ty().canonical(), - [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], - |dest, [lhs, rhs]| vec![Insn::And { dest, lhs, rhs }], - ) - .into(), - ExprEnum::BitAndB(expr) => self - .simple_nary_big_expr( - instantiated_module, - Bool.canonical(), - [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], - |dest, [lhs, rhs]| vec![Insn::And { dest, lhs, rhs }], - ) - .into(), - ExprEnum::BitOrU(expr) => self - .simple_nary_big_expr( - instantiated_module, - expr.ty().canonical(), - [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], - |dest, [lhs, rhs]| vec![Insn::Or { dest, lhs, rhs }], - ) - .into(), - ExprEnum::BitOrS(expr) => self - .simple_nary_big_expr( - instantiated_module, - expr.ty().canonical(), - [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], - |dest, [lhs, rhs]| vec![Insn::Or { dest, lhs, rhs }], - ) - .into(), - ExprEnum::BitOrB(expr) => self - .simple_nary_big_expr( - instantiated_module, - Bool.canonical(), - [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], - |dest, [lhs, rhs]| vec![Insn::Or { dest, lhs, rhs }], - ) - .into(), - ExprEnum::BitXorU(expr) => self - .simple_nary_big_expr( - instantiated_module, - expr.ty().canonical(), - [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], - |dest, [lhs, rhs]| vec![Insn::Xor { dest, lhs, rhs }], - ) - .into(), - ExprEnum::BitXorS(expr) => self - .simple_nary_big_expr( - instantiated_module, - expr.ty().canonical(), - [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], - |dest, [lhs, rhs]| vec![Insn::Xor { dest, lhs, rhs }], - ) - .into(), - ExprEnum::BitXorB(expr) => self - .simple_nary_big_expr( - instantiated_module, - Bool.canonical(), - [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], - |dest, [lhs, rhs]| vec![Insn::Xor { dest, lhs, rhs }], - ) - .into(), - ExprEnum::AddU(expr) => self - .simple_nary_big_expr( - instantiated_module, - expr.ty().canonical(), - [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], - |dest, [lhs, rhs]| vec![Insn::Add { dest, lhs, rhs }], - ) - .into(), - ExprEnum::AddS(expr) => self - .simple_nary_big_expr( - instantiated_module, - expr.ty().canonical(), - [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], - |dest, [lhs, rhs]| vec![Insn::Add { dest, lhs, rhs }], - ) - .into(), - ExprEnum::SubU(expr) => self - .simple_nary_big_expr( - instantiated_module, - expr.ty().canonical(), - [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], - |dest, [lhs, rhs]| { - vec![Insn::SubU { - dest, - lhs, - rhs, - dest_width: expr.ty().width(), - }] - }, - ) - .into(), - ExprEnum::SubS(expr) => self - .simple_nary_big_expr( - instantiated_module, - expr.ty().canonical(), - [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], - |dest, [lhs, rhs]| vec![Insn::SubS { dest, lhs, rhs }], - ) - .into(), - ExprEnum::MulU(expr) => self - .simple_nary_big_expr( - instantiated_module, - expr.ty().canonical(), - [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], - |dest, [lhs, rhs]| vec![Insn::Mul { dest, lhs, rhs }], - ) - .into(), - ExprEnum::MulS(expr) => self - .simple_nary_big_expr( - instantiated_module, - expr.ty().canonical(), - [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], - |dest, [lhs, rhs]| vec![Insn::Mul { dest, lhs, rhs }], - ) - .into(), - ExprEnum::DivU(expr) => self - .simple_nary_big_expr( - instantiated_module, - expr.ty().canonical(), - [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], - |dest, [lhs, rhs]| vec![Insn::Div { dest, lhs, rhs }], - ) - .into(), - ExprEnum::DivS(expr) => self - .simple_nary_big_expr( - instantiated_module, - expr.ty().canonical(), - [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], - |dest, [lhs, rhs]| vec![Insn::Div { dest, lhs, rhs }], - ) - .into(), - ExprEnum::RemU(expr) => self - .simple_nary_big_expr( - instantiated_module, - expr.ty().canonical(), - [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], - |dest, [lhs, rhs]| vec![Insn::Rem { dest, lhs, rhs }], - ) - .into(), - ExprEnum::RemS(expr) => self - .simple_nary_big_expr( - instantiated_module, - expr.ty().canonical(), - [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], - |dest, [lhs, rhs]| vec![Insn::Rem { dest, lhs, rhs }], - ) - .into(), - ExprEnum::DynShlU(expr) => self - .simple_nary_big_expr( - instantiated_module, - expr.ty().canonical(), - [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], - |dest, [lhs, rhs]| vec![Insn::DynShl { dest, lhs, rhs }], - ) - .into(), - ExprEnum::DynShlS(expr) => self - .simple_nary_big_expr( - instantiated_module, - expr.ty().canonical(), - [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], - |dest, [lhs, rhs]| vec![Insn::DynShl { dest, lhs, rhs }], - ) - .into(), - ExprEnum::DynShrU(expr) => self - .simple_nary_big_expr( - instantiated_module, - expr.ty().canonical(), - [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], - |dest, [lhs, rhs]| vec![Insn::DynShr { dest, lhs, rhs }], - ) - .into(), - ExprEnum::DynShrS(expr) => self - .simple_nary_big_expr( - instantiated_module, - expr.ty().canonical(), - [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], - |dest, [lhs, rhs]| vec![Insn::DynShr { dest, lhs, rhs }], - ) - .into(), - ExprEnum::FixedShlU(expr) => self - .simple_nary_big_expr( - instantiated_module, - expr.ty().canonical(), - [Expr::canonical(expr.lhs())], - |dest, [lhs]| { - vec![Insn::Shl { - dest, - lhs, - rhs: expr.rhs(), - }] - }, - ) - .into(), - ExprEnum::FixedShlS(expr) => self - .simple_nary_big_expr( - instantiated_module, - expr.ty().canonical(), - [Expr::canonical(expr.lhs())], - |dest, [lhs]| { - vec![Insn::Shl { - dest, - lhs, - rhs: expr.rhs(), - }] - }, - ) - .into(), - ExprEnum::FixedShrU(expr) => self - .simple_nary_big_expr( - instantiated_module, - expr.ty().canonical(), - [Expr::canonical(expr.lhs())], - |dest, [lhs]| { - vec![Insn::Shr { - dest, - lhs, - rhs: expr.rhs(), - }] - }, - ) - .into(), - ExprEnum::FixedShrS(expr) => self - .simple_nary_big_expr( - instantiated_module, - expr.ty().canonical(), - [Expr::canonical(expr.lhs())], - |dest, [lhs]| { - vec![Insn::Shr { - dest, - lhs, - rhs: expr.rhs(), - }] - }, - ) - .into(), - ExprEnum::CmpLtB(expr) => self - .simple_nary_big_expr( - instantiated_module, - Bool.canonical(), - [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], - |dest, [lhs, rhs]| vec![Insn::CmpLt { dest, lhs, rhs }], - ) - .into(), - ExprEnum::CmpLeB(expr) => self - .simple_nary_big_expr( - instantiated_module, - Bool.canonical(), - [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], - |dest, [lhs, rhs]| vec![Insn::CmpLe { dest, lhs, rhs }], - ) - .into(), - ExprEnum::CmpGtB(expr) => self - .simple_nary_big_expr( - instantiated_module, - Bool.canonical(), - // swap both comparison direction and lhs/rhs - [Expr::canonical(expr.rhs()), Expr::canonical(expr.lhs())], - |dest, [lhs, rhs]| vec![Insn::CmpLt { dest, lhs, rhs }], - ) - .into(), - ExprEnum::CmpGeB(expr) => self - .simple_nary_big_expr( - instantiated_module, - Bool.canonical(), - // swap both comparison direction and lhs/rhs - [Expr::canonical(expr.rhs()), Expr::canonical(expr.lhs())], - |dest, [lhs, rhs]| vec![Insn::CmpLe { dest, lhs, rhs }], - ) - .into(), - ExprEnum::CmpEqB(expr) => self - .simple_nary_big_expr( - instantiated_module, - Bool.canonical(), - [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], - |dest, [lhs, rhs]| vec![Insn::CmpEq { dest, lhs, rhs }], - ) - .into(), - ExprEnum::CmpNeB(expr) => self - .simple_nary_big_expr( - instantiated_module, - Bool.canonical(), - [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], - |dest, [lhs, rhs]| vec![Insn::CmpNe { dest, lhs, rhs }], - ) - .into(), - ExprEnum::CmpLtU(expr) => self - .simple_nary_big_expr( - instantiated_module, - Bool.canonical(), - [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], - |dest, [lhs, rhs]| vec![Insn::CmpLt { dest, lhs, rhs }], - ) - .into(), - ExprEnum::CmpLeU(expr) => self - .simple_nary_big_expr( - instantiated_module, - Bool.canonical(), - [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], - |dest, [lhs, rhs]| vec![Insn::CmpLe { dest, lhs, rhs }], - ) - .into(), - ExprEnum::CmpGtU(expr) => self - .simple_nary_big_expr( - instantiated_module, - Bool.canonical(), - // swap both comparison direction and lhs/rhs - [Expr::canonical(expr.rhs()), Expr::canonical(expr.lhs())], - |dest, [lhs, rhs]| vec![Insn::CmpLt { dest, lhs, rhs }], - ) - .into(), - ExprEnum::CmpGeU(expr) => self - .simple_nary_big_expr( - instantiated_module, - Bool.canonical(), - // swap both comparison direction and lhs/rhs - [Expr::canonical(expr.rhs()), Expr::canonical(expr.lhs())], - |dest, [lhs, rhs]| vec![Insn::CmpLe { dest, lhs, rhs }], - ) - .into(), - ExprEnum::CmpEqU(expr) => self - .simple_nary_big_expr( - instantiated_module, - Bool.canonical(), - [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], - |dest, [lhs, rhs]| vec![Insn::CmpEq { dest, lhs, rhs }], - ) - .into(), - ExprEnum::CmpNeU(expr) => self - .simple_nary_big_expr( - instantiated_module, - Bool.canonical(), - [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], - |dest, [lhs, rhs]| vec![Insn::CmpNe { dest, lhs, rhs }], - ) - .into(), - ExprEnum::CmpLtS(expr) => self - .simple_nary_big_expr( - instantiated_module, - Bool.canonical(), - [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], - |dest, [lhs, rhs]| vec![Insn::CmpLt { dest, lhs, rhs }], - ) - .into(), - ExprEnum::CmpLeS(expr) => self - .simple_nary_big_expr( - instantiated_module, - Bool.canonical(), - [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], - |dest, [lhs, rhs]| vec![Insn::CmpLe { dest, lhs, rhs }], - ) - .into(), - ExprEnum::CmpGtS(expr) => self - .simple_nary_big_expr( - instantiated_module, - Bool.canonical(), - // swap both comparison direction and lhs/rhs - [Expr::canonical(expr.rhs()), Expr::canonical(expr.lhs())], - |dest, [lhs, rhs]| vec![Insn::CmpLt { dest, lhs, rhs }], - ) - .into(), - ExprEnum::CmpGeS(expr) => self - .simple_nary_big_expr( - instantiated_module, - Bool.canonical(), - // swap both comparison direction and lhs/rhs - [Expr::canonical(expr.rhs()), Expr::canonical(expr.lhs())], - |dest, [lhs, rhs]| vec![Insn::CmpLe { dest, lhs, rhs }], - ) - .into(), - ExprEnum::CmpEqS(expr) => self - .simple_nary_big_expr( - instantiated_module, - Bool.canonical(), - [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], - |dest, [lhs, rhs]| vec![Insn::CmpEq { dest, lhs, rhs }], - ) - .into(), - ExprEnum::CmpNeS(expr) => self - .simple_nary_big_expr( - instantiated_module, - Bool.canonical(), - [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], - |dest, [lhs, rhs]| vec![Insn::CmpNe { dest, lhs, rhs }], - ) - .into(), - ExprEnum::CastUIntToUInt(expr) => self - .simple_nary_big_expr( - instantiated_module, - expr.ty().canonical(), - [Expr::canonical(expr.arg())], - |dest, [src]| { - vec![Insn::CastToUInt { - dest, - src, - dest_width: expr.ty().width(), - }] - }, - ) - .into(), - ExprEnum::CastUIntToSInt(expr) => self - .simple_nary_big_expr( - instantiated_module, - expr.ty().canonical(), - [Expr::canonical(expr.arg())], - |dest, [src]| { - vec![Insn::CastToSInt { - dest, - src, - dest_width: expr.ty().width(), - }] - }, - ) - .into(), - ExprEnum::CastSIntToUInt(expr) => self - .simple_nary_big_expr( - instantiated_module, - expr.ty().canonical(), - [Expr::canonical(expr.arg())], - |dest, [src]| { - vec![Insn::CastToUInt { - dest, - src, - dest_width: expr.ty().width(), - }] - }, - ) - .into(), - ExprEnum::CastSIntToSInt(expr) => self - .simple_nary_big_expr( - instantiated_module, - expr.ty().canonical(), - [Expr::canonical(expr.arg())], - |dest, [src]| { - vec![Insn::CastToSInt { - dest, - src, - dest_width: expr.ty().width(), - }] - }, - ) - .into(), - ExprEnum::CastBoolToUInt(expr) => cast_bit(Expr::canonical(expr.arg())), - ExprEnum::CastBoolToSInt(expr) => cast_bit(Expr::canonical(expr.arg())), - ExprEnum::CastUIntToBool(expr) => cast_bit(Expr::canonical(expr.arg())), - ExprEnum::CastSIntToBool(expr) => cast_bit(Expr::canonical(expr.arg())), - ExprEnum::CastBoolToSyncReset(expr) => cast_bit(Expr::canonical(expr.arg())), - ExprEnum::CastUIntToSyncReset(expr) => cast_bit(Expr::canonical(expr.arg())), - ExprEnum::CastSIntToSyncReset(expr) => cast_bit(Expr::canonical(expr.arg())), - ExprEnum::CastBoolToAsyncReset(expr) => cast_bit(Expr::canonical(expr.arg())), - ExprEnum::CastUIntToAsyncReset(expr) => cast_bit(Expr::canonical(expr.arg())), - ExprEnum::CastSIntToAsyncReset(expr) => cast_bit(Expr::canonical(expr.arg())), - ExprEnum::CastSyncResetToBool(expr) => cast_bit(Expr::canonical(expr.arg())), - ExprEnum::CastSyncResetToUInt(expr) => cast_bit(Expr::canonical(expr.arg())), - ExprEnum::CastSyncResetToSInt(expr) => cast_bit(Expr::canonical(expr.arg())), - ExprEnum::CastSyncResetToReset(expr) => cast_bit(Expr::canonical(expr.arg())), - ExprEnum::CastAsyncResetToBool(expr) => cast_bit(Expr::canonical(expr.arg())), - ExprEnum::CastAsyncResetToUInt(expr) => cast_bit(Expr::canonical(expr.arg())), - ExprEnum::CastAsyncResetToSInt(expr) => cast_bit(Expr::canonical(expr.arg())), - ExprEnum::CastAsyncResetToReset(expr) => cast_bit(Expr::canonical(expr.arg())), - ExprEnum::CastResetToBool(expr) => cast_bit(Expr::canonical(expr.arg())), - ExprEnum::CastResetToUInt(expr) => cast_bit(Expr::canonical(expr.arg())), - ExprEnum::CastResetToSInt(expr) => cast_bit(Expr::canonical(expr.arg())), - ExprEnum::CastBoolToClock(expr) => cast_bit(Expr::canonical(expr.arg())), - ExprEnum::CastUIntToClock(expr) => cast_bit(Expr::canonical(expr.arg())), - ExprEnum::CastSIntToClock(expr) => cast_bit(Expr::canonical(expr.arg())), - ExprEnum::CastClockToBool(expr) => cast_bit(Expr::canonical(expr.arg())), - ExprEnum::CastClockToUInt(expr) => cast_bit(Expr::canonical(expr.arg())), - ExprEnum::CastClockToSInt(expr) => cast_bit(Expr::canonical(expr.arg())), - ExprEnum::FieldAccess(expr) => self - .compile_expr(instantiated_module, Expr::canonical(expr.base())) - .map_ty(Bundle::from_canonical) - .field_by_index(expr.field_index()), - ExprEnum::VariantAccess(variant_access) => { - let start = Expr::ty(variant_access.base()).discriminant_bit_width(); - let len = Expr::ty(expr).bit_width(); - self.compile_expr( - instantiated_module, - variant_access.base().cast_to_bits()[start..start + len] - .cast_bits_to(Expr::ty(expr)), - ) - } - ExprEnum::ArrayIndex(expr) => self - .compile_expr(instantiated_module, Expr::canonical(expr.base())) - .map_ty(Array::from_canonical) - .element(expr.element_index()), - ExprEnum::DynArrayIndex(expr) => { - let element_index = - self.compile_expr(instantiated_module, Expr::canonical(expr.element_index())); - let element_index = self.compiled_expr_to_value( - element_index, - instantiated_module.leaf_module().source_location(), - ); - let index_slot = self.compiled_value_to_dyn_array_index( - element_index.map_ty(UInt::from_canonical), - instantiated_module.leaf_module().source_location(), - ); - self.compile_expr(instantiated_module, Expr::canonical(expr.base())) - .map_ty(Array::from_canonical) - .element_dyn(index_slot) - } - ExprEnum::ReduceBitAndU(expr) => if Expr::ty(expr.arg()).width() == 0 { - self.compile_expr(instantiated_module, Expr::canonical(true.to_expr())) - } else { - self.compile_expr( - instantiated_module, - Expr::canonical( - expr.arg() - .cmp_eq(Expr::ty(expr.arg()).from_int_wrapping(-1)), - ), - ) - } - .into(), - ExprEnum::ReduceBitAndS(expr) => if Expr::ty(expr.arg()).width() == 0 { - self.compile_expr(instantiated_module, Expr::canonical(true.to_expr())) - } else { - self.compile_expr( - instantiated_module, - Expr::canonical( - expr.arg() - .cmp_eq(Expr::ty(expr.arg()).from_int_wrapping(-1)), - ), - ) - } - .into(), - ExprEnum::ReduceBitOrU(expr) => if Expr::ty(expr.arg()).width() == 0 { - self.compile_expr(instantiated_module, Expr::canonical(false.to_expr())) - } else { - self.compile_expr( - instantiated_module, - Expr::canonical(expr.arg().cmp_ne(Expr::ty(expr.arg()).from_int_wrapping(0))), - ) - } - .into(), - ExprEnum::ReduceBitOrS(expr) => if Expr::ty(expr.arg()).width() == 0 { - self.compile_expr(instantiated_module, Expr::canonical(false.to_expr())) - } else { - self.compile_expr( - instantiated_module, - Expr::canonical(expr.arg().cmp_ne(Expr::ty(expr.arg()).from_int_wrapping(0))), - ) - } - .into(), - ExprEnum::ReduceBitXorU(expr) => self - .simple_nary_big_expr( - instantiated_module, - UInt::<1>::TYPE.canonical(), - [Expr::canonical(expr.arg())], - |dest, [src]| { - vec![Insn::ReduceBitXor { - dest, - src, - input_width: Expr::ty(expr.arg()).width(), - }] - }, - ) - .into(), - ExprEnum::ReduceBitXorS(expr) => self - .simple_nary_big_expr( - instantiated_module, - UInt::<1>::TYPE.canonical(), - [Expr::canonical(expr.arg())], - |dest, [src]| { - vec![Insn::ReduceBitXor { - dest, - src, - input_width: Expr::ty(expr.arg()).width(), - }] - }, - ) - .into(), - ExprEnum::SliceUInt(expr) => self - .simple_nary_big_expr( - instantiated_module, - UInt::new_dyn(expr.range().len()).canonical(), - [Expr::canonical(expr.base())], - |dest, [src]| { - vec![Insn::SliceInt { - dest, - src, - start: expr.range().start, - len: expr.range().len(), - }] - }, - ) - .into(), - ExprEnum::SliceSInt(expr) => self - .simple_nary_big_expr( - instantiated_module, - UInt::new_dyn(expr.range().len()).canonical(), - [Expr::canonical(expr.base())], - |dest, [src]| { - vec![Insn::SliceInt { - dest, - src, - start: expr.range().start, - len: expr.range().len(), - }] - }, - ) - .into(), - ExprEnum::CastToBits(expr) => self - .compile_cast_to_bits(instantiated_module, expr) - .map_ty(CanonicalType::UInt) - .into(), - ExprEnum::CastBitsTo(expr) => { - self.compile_cast_bits_to(instantiated_module, expr).into() - } - ExprEnum::ModuleIO(expr) => self - .compile_value(TargetInInstantiatedModule { - instantiated_module, - target: expr.into(), - }) - .into(), - ExprEnum::Instance(expr) => self - .compile_value(TargetInInstantiatedModule { - instantiated_module, - target: expr.into(), - }) - .into(), - ExprEnum::Wire(expr) => self - .compile_value(TargetInInstantiatedModule { - instantiated_module, - target: expr.into(), - }) - .into(), - ExprEnum::Reg(expr) => self - .compile_value(TargetInInstantiatedModule { - instantiated_module, - target: expr.into(), - }) - .into(), - ExprEnum::RegSync(expr) => self - .compile_value(TargetInInstantiatedModule { - instantiated_module, - target: expr.into(), - }) - .into(), - ExprEnum::RegAsync(expr) => self - .compile_value(TargetInInstantiatedModule { - instantiated_module, - target: expr.into(), - }) - .into(), - ExprEnum::MemPort(expr) => self - .compile_value(TargetInInstantiatedModule { - instantiated_module, - target: expr.into(), - }) - .into(), - }; - self.compiled_exprs.insert(expr, retval); - retval - } - fn compile_simple_connect( - &mut self, - conditions: Interned<[Cond]>, - lhs: CompiledExpr, - rhs: CompiledValue, - source_location: SourceLocation, - ) { - let CompiledExpr { - static_part: lhs_static_part, - indexes, - } = lhs; - let (lhs_layout, lhs_range) = lhs_static_part.write(); - assert!( - lhs_layout.ty.is_passive(), - "invalid expression passed to compile_simple_connect -- type must be passive", - ); - let TypeIndexRange { - small_slots, - big_slots, - } = lhs_range; - self.add_assignment( - conditions, - small_slots - .iter() - .zip(rhs.range.small_slots.iter()) - .map(|(base, src)| { - if indexes.small_slots.is_empty() { - Insn::CopySmall { dest: base, src } - } else { - Insn::WriteSmallIndexed { - dest: StatePartArrayIndexed { - base, - indexes: indexes.small_slots, - }, - src, - } - } - }) - .chain( - big_slots - .iter() - .zip(rhs.range.big_slots.iter()) - .map(|(base, src)| { - if indexes.big_slots.is_empty() { - Insn::Copy { dest: base, src } - } else { - Insn::WriteIndexed { - dest: StatePartArrayIndexed { - base, - indexes: indexes.big_slots, - }, - src, - } - } - }), - ), - source_location, - ); - } - fn compile_connect( - &mut self, - lhs_instantiated_module: InstantiatedModule, - lhs_conditions: Interned<[Cond]>, - lhs: Expr, - rhs_instantiated_module: InstantiatedModule, - rhs_conditions: Interned<[Cond]>, - mut rhs: Expr, - source_location: SourceLocation, - ) { - if Expr::ty(lhs) != Expr::ty(rhs) || !Expr::ty(lhs).is_passive() { - match Expr::ty(lhs) { - CanonicalType::UInt(lhs_ty) => { - rhs = Expr::canonical(Expr::::from_canonical(rhs).cast_to(lhs_ty)); - } - CanonicalType::SInt(lhs_ty) => { - rhs = Expr::canonical(Expr::::from_canonical(rhs).cast_to(lhs_ty)); - } - CanonicalType::Bool(_) => unreachable!(), - CanonicalType::Array(lhs_ty) => { - let CanonicalType::Array(rhs_ty) = Expr::ty(rhs) else { - unreachable!(); - }; - assert_eq!(lhs_ty.len(), rhs_ty.len()); - let lhs = Expr::::from_canonical(lhs); - let rhs = Expr::::from_canonical(rhs); - for index in 0..lhs_ty.len() { - self.compile_connect( - lhs_instantiated_module, - lhs_conditions, - lhs[index], - rhs_instantiated_module, - rhs_conditions, - rhs[index], - source_location, - ); - } - return; - } - CanonicalType::Enum(lhs_ty) => { - let CanonicalType::Enum(rhs_ty) = Expr::ty(rhs) else { - unreachable!(); - }; - todo!("handle connect with different enum types"); - } - CanonicalType::Bundle(lhs_ty) => { - let CanonicalType::Bundle(rhs_ty) = Expr::ty(rhs) else { - unreachable!(); - }; - assert_eq!(lhs_ty.fields().len(), rhs_ty.fields().len()); - let lhs = Expr::::from_canonical(lhs); - let rhs = Expr::::from_canonical(rhs); - for ( - field_index, - ( - BundleField { - name, - flipped, - ty: _, - }, - rhs_field, - ), - ) in lhs_ty.fields().into_iter().zip(rhs_ty.fields()).enumerate() - { - assert_eq!(name, rhs_field.name); - assert_eq!(flipped, rhs_field.flipped); - let lhs_expr = ops::FieldAccess::new_by_index(lhs, field_index).to_expr(); - let rhs_expr = ops::FieldAccess::new_by_index(rhs, field_index).to_expr(); - if flipped { - // swap lhs/rhs - self.compile_connect( - rhs_instantiated_module, - rhs_conditions, - rhs_expr, - lhs_instantiated_module, - lhs_conditions, - lhs_expr, - source_location, - ); - } else { - self.compile_connect( - lhs_instantiated_module, - lhs_conditions, - lhs_expr, - rhs_instantiated_module, - rhs_conditions, - rhs_expr, - source_location, - ); - } - } - return; - } - CanonicalType::AsyncReset(_) => unreachable!(), - CanonicalType::SyncReset(_) => unreachable!(), - CanonicalType::Reset(_) => unreachable!(), - CanonicalType::Clock(_) => unreachable!(), - } - } - let Some(target) = lhs.target() else { - unreachable!("connect lhs must have target"); - }; - let lhs_decl_conditions = self.decl_conditions[&TargetInInstantiatedModule { - instantiated_module: lhs_instantiated_module, - target: target.base().into(), - }]; - let lhs = self.compile_expr(lhs_instantiated_module, lhs); - let rhs = self.compile_expr(rhs_instantiated_module, rhs); - let rhs = self.compiled_expr_to_value(rhs, source_location); - self.compile_simple_connect( - lhs_conditions[lhs_decl_conditions.len()..].intern(), - lhs, - rhs, - source_location, - ); - } - fn compile_clock( - &mut self, - clk: CompiledValue, - source_location: SourceLocation, - ) -> ClockTrigger { - if let Some(&retval) = self.compiled_value_to_clock_trigger_map.get(&clk) { - return retval; - } - let mut alloc_small_slot = |part_name: &str| { - self.insns - .state_layout - .ty - .small_slots - .allocate(&StatePartLayout::scalar( - SlotDebugData { - name: Interned::default(), - ty: Bool.canonical(), - }, - (), - )) - .start - }; - let last_clk_was_low = alloc_small_slot("last_clk_was_low"); - let clk_triggered = alloc_small_slot("clk_triggered"); - let retval = ClockTrigger { - last_clk_was_low, - clk: self.compiled_value_bool_dest_is_small( - clk.map_ty(CanonicalType::Clock), - source_location, - ), - clk_triggered, - source_location, - }; - self.add_assignment( - Interned::default(), - [Insn::AndSmall { - dest: clk_triggered, - lhs: retval.clk, - rhs: last_clk_was_low, - }], - source_location, - ); - self.clock_triggers.push(retval); - self.compiled_value_to_clock_trigger_map.insert(clk, retval); - retval - } - fn compile_enum_discriminant( - &mut self, - enum_value: CompiledValue, - source_location: SourceLocation, - ) -> StatePartIndex { - if let Some(&retval) = self.enum_discriminants.get(&enum_value) { - return retval; - } - let retval_ty = Enum::new( - enum_value - .layout - .ty - .variants() - .iter() - .map(|variant| EnumVariant { - name: variant.name, - ty: None, - }) - .collect(), - ); - let retval = if retval_ty == enum_value.layout.ty - && enum_value.range.len() == TypeLen::A_SMALL_SLOT - { - enum_value.range.small_slots.start - } else { - let retval = self - .insns - .state_layout - .ty - .small_slots - .allocate(&StatePartLayout::scalar( - SlotDebugData { - name: Interned::default(), - ty: retval_ty.canonical(), - }, - (), - )) - .start; - let discriminant_bit_width = enum_value.layout.ty.discriminant_bit_width(); - let discriminant_mask = !(!0u64 << discriminant_bit_width); - let insn = match enum_value.range.len() { - TypeLen::A_BIG_SLOT => Insn::AndBigWithSmallImmediate { - dest: retval, - lhs: enum_value.range.big_slots.start, - rhs: discriminant_mask, - }, - TypeLen::A_SMALL_SLOT => { - if discriminant_bit_width == enum_value.layout.ty.type_properties().bit_width { - Insn::CopySmall { - dest: retval, - src: enum_value.range.small_slots.start, - } - } else { - Insn::AndSmallImmediate { - dest: retval, - lhs: enum_value.range.small_slots.start, - rhs: discriminant_mask, - } - } - } - _ => unreachable!(), - }; - self.add_assignment(Interned::default(), [insn], source_location); - retval - }; - self.enum_discriminants.insert(enum_value, retval); - retval - } - fn compile_stmt_reg( - &mut self, - stmt_reg: StmtReg, - instantiated_module: InstantiatedModule, - value: CompiledValue, - ) { - let StmtReg { annotations, reg } = stmt_reg; - let clk = self.compile_expr(instantiated_module, Expr::canonical(reg.clock_domain().clk)); - let clk = self - .compiled_expr_to_value(clk, reg.source_location()) - .map_ty(Clock::from_canonical); - let clk = self.compile_clock(clk, reg.source_location()); - struct Dispatch; - impl ResetTypeDispatch for Dispatch { - type Input = (); - - type Output = bool; - - fn reset(self, _input: Self::Input) -> Self::Output { - unreachable!() - } - - fn sync_reset(self, _input: Self::Input) -> Self::Output { - false - } - - fn async_reset(self, _input: Self::Input) -> Self::Output { - true - } - } - let reset = if let Some(init) = reg.init() { - let init = self.compile_expr(instantiated_module, init); - let init = self.compiled_expr_to_value(init, reg.source_location()); - let rst = - self.compile_expr(instantiated_module, Expr::canonical(reg.clock_domain().rst)); - let rst = self.compiled_expr_to_value(rst, reg.source_location()); - let rst = self.compiled_value_bool_dest_is_small(rst, reg.source_location()); - let is_async = R::dispatch((), Dispatch); - if is_async { - let cond = Expr::canonical(reg.clock_domain().rst.cast_to(Bool)); - let cond = self.compile_expr(instantiated_module, cond); - let cond = self.compiled_expr_to_value(cond, reg.source_location()); - let cond = cond.map_ty(Bool::from_canonical); - // write to the register's current value since asynchronous reset is combinational - let lhs = CompiledValue { - layout: value.layout, - range: value.range, - write: None, - } - .into(); - self.compile_simple_connect( - [Cond { - body: CondBody::IfTrue { cond }, - source_location: reg.source_location(), - }][..] - .intern(), - lhs, - init, - reg.source_location(), - ); - } - Some(RegisterReset { - is_async, - init, - rst, - }) - } else { - None - }; - self.registers.push(Register { - value, - clk_triggered: clk.clk_triggered, - reset, - source_location: reg.source_location(), - }); - } - fn compile_declaration( - &mut self, - declaration: StmtDeclaration, - parent_module: Interned, - conditions: Interned<[Cond]>, - ) -> TraceDecl { - let target_base: TargetBase = match &declaration { - StmtDeclaration::Wire(v) => v.wire.into(), - StmtDeclaration::Reg(v) => v.reg.into(), - StmtDeclaration::RegSync(v) => v.reg.into(), - StmtDeclaration::RegAsync(v) => v.reg.into(), - StmtDeclaration::Instance(v) => v.instance.into(), - }; - let target = TargetInInstantiatedModule { - instantiated_module: *parent_module, - target: target_base.into(), - }; - self.decl_conditions.insert(target, conditions); - let compiled_value = self.compile_value(target); - match declaration { - StmtDeclaration::Wire(StmtWire { annotations, wire }) => {} - StmtDeclaration::Reg(_) => { - unreachable!("Reset types were already replaced by SyncReset or AsyncReset"); - } - StmtDeclaration::RegSync(stmt_reg) => { - self.compile_stmt_reg(stmt_reg, *parent_module, compiled_value) - } - StmtDeclaration::RegAsync(stmt_reg) => { - self.compile_stmt_reg(stmt_reg, *parent_module, compiled_value) - } - StmtDeclaration::Instance(StmtInstance { - annotations, - instance, - }) => { - let inner_instantiated_module = InstantiatedModule::Child { - parent: parent_module, - instance: instance.intern_sized(), - } - .intern_sized(); - let instance_expr = instance.to_expr(); - self.compile_module(inner_instantiated_module); - for (field_index, module_io) in - instance.instantiated().module_io().into_iter().enumerate() - { - let instance_field = - ops::FieldAccess::new_by_index(instance_expr, field_index).to_expr(); - match Expr::flow(instance_field) { - Flow::Source => { - // we need to supply the value to the instance since the - // parent module expects to read from the instance - self.compile_connect( - *parent_module, - conditions, - instance_field, - *inner_instantiated_module, - Interned::default(), - module_io.module_io.to_expr(), - instance.source_location(), - ); - } - Flow::Sink => { - // we need to take the value from the instance since the - // parent module expects to write to the instance - self.compile_connect( - *inner_instantiated_module, - Interned::default(), - module_io.module_io.to_expr(), - *parent_module, - conditions, - instance_field, - instance.source_location(), - ); - } - Flow::Duplex => unreachable!(), - } - } - } - } - self.make_trace_decl(*parent_module, target_base) - } - fn allocate_delay_chain( - &mut self, - len: usize, - layout: &TypeLayout, - first: Option, - last: Option, - mut from_allocation: impl FnMut(TypeIndexRange) -> T, - ) -> Vec { - match (len, first, last) { - (0, _, _) => Vec::new(), - (1, Some(v), _) | (1, None, Some(v)) => vec![v], - (2, Some(first), Some(last)) => vec![first, last], - (len, first, last) => { - let inner_len = len - first.is_some() as usize - last.is_some() as usize; - first - .into_iter() - .chain( - (0..inner_len) - .map(|_| from_allocation(self.insns.allocate_variable(layout))), - ) - .chain(last) - .collect() - } - } - } - fn allocate_delay_chain_small( - &mut self, - len: usize, - ty: CanonicalType, - first: Option>, - last: Option>, - ) -> Vec> { - self.allocate_delay_chain( - len, - &TypeLayout { - small_slots: StatePartLayout::scalar( - SlotDebugData { - name: Interned::default(), - ty, - }, - (), - ), - big_slots: StatePartLayout::empty(), - }, - first, - last, - |range| range.small_slots.start, - ) - } - fn compile_memory_port_rw_helper( - &mut self, - memory: StatePartIndex, - stride: usize, - mut start: usize, - data_layout: CompiledTypeLayout, - mask_layout: CompiledTypeLayout, - mut read: Option>, - mut write: Option>, - ) { - match data_layout.body { - CompiledTypeLayoutBody::Scalar => { - let CompiledTypeLayoutBody::Scalar = mask_layout.body else { - unreachable!(); - }; - let signed = match data_layout.ty { - CanonicalType::UInt(_) => false, - CanonicalType::SInt(_) => true, - CanonicalType::Bool(_) => false, - CanonicalType::Array(_) => unreachable!(), - CanonicalType::Enum(_) => false, - CanonicalType::Bundle(_) => unreachable!(), - CanonicalType::AsyncReset(_) => false, - CanonicalType::SyncReset(_) => false, - CanonicalType::Reset(_) => false, - CanonicalType::Clock(_) => false, - }; - let width = data_layout.ty.bit_width(); - if let Some(MemoryPortReadInsns { - addr, - en: _, - write_mode: _, - data, - insns, - }) = read - { - insns.push( - match data.len() { - TypeLen::A_BIG_SLOT => { - let dest = data.big_slots.start; - if signed { - Insn::MemoryReadSInt { - dest, - memory, - addr, - stride, - start, - width, - } - } else { - Insn::MemoryReadUInt { - dest, - memory, - addr, - stride, - start, - width, - } - } - } - TypeLen::A_SMALL_SLOT => { - let _dest = data.small_slots.start; - todo!("memory ports' data are always big for now"); - } - _ => unreachable!(), - } - .into(), - ); - } - if let Some(MemoryPortWriteInsns { - addr, - en: _, - write_mode: _, - data, - mask, - insns, - }) = write - { - let end_label = self.insns.new_label(); - insns.push( - match mask.len() { - TypeLen::A_BIG_SLOT => Insn::BranchIfZero { - target: end_label.0, - value: mask.big_slots.start, - }, - TypeLen::A_SMALL_SLOT => Insn::BranchIfSmallZero { - target: end_label.0, - value: mask.small_slots.start, - }, - _ => unreachable!(), - } - .into(), - ); - insns.push( - match data.len() { - TypeLen::A_BIG_SLOT => { - let value = data.big_slots.start; - if signed { - Insn::MemoryWriteSInt { - value, - memory, - addr, - stride, - start, - width, - } - } else { - Insn::MemoryWriteUInt { - value, - memory, - addr, - stride, - start, - width, - } - } - } - TypeLen::A_SMALL_SLOT => { - let _value = data.small_slots.start; - todo!("memory ports' data are always big for now"); - } - _ => unreachable!(), - } - .into(), - ); - insns.push(end_label.into()); - } - } - CompiledTypeLayoutBody::Array { element } => { - let CompiledTypeLayoutBody::Array { - element: mask_element, - } = mask_layout.body - else { - unreachable!(); - }; - let ty = ::from_canonical(data_layout.ty); - let element_bit_width = ty.element().bit_width(); - let element_size = element.layout.len(); - let mask_element_size = mask_element.layout.len(); - for element_index in 0..ty.len() { - self.compile_memory_port_rw_helper( - memory, - stride, - start, - *element, - *mask_element, - read.as_mut().map( - |MemoryPortReadInsns { - addr, - en, - write_mode, - data, - insns, - }| MemoryPortReadInsns { - addr: *addr, - en: *en, - write_mode: *write_mode, - data: data.index_array(element_size, element_index), - insns, - }, - ), - write.as_mut().map( - |MemoryPortWriteInsns { - addr, - en, - write_mode, - data, - mask, - insns, - }| { - MemoryPortWriteInsns { - addr: *addr, - en: *en, - write_mode: *write_mode, - data: data.index_array(element_size, element_index), - mask: mask.index_array(mask_element_size, element_index), - insns, - } - }, - ), - ); - start += element_bit_width; - } - } - CompiledTypeLayoutBody::Bundle { fields } => { - let CompiledTypeLayoutBody::Bundle { - fields: mask_fields, - } = mask_layout.body - else { - unreachable!(); - }; - assert_eq!(fields.len(), mask_fields.len()); - for (field, mask_field) in fields.into_iter().zip(mask_fields) { - let field_index_range = - TypeIndexRange::new(field.offset, field.ty.layout.len()); - let mask_field_index_range = - TypeIndexRange::new(mask_field.offset, mask_field.ty.layout.len()); - self.compile_memory_port_rw_helper( - memory, - stride, - start, - field.ty, - mask_field.ty, - read.as_mut().map( - |MemoryPortReadInsns { - addr, - en, - write_mode, - data, - insns, - }| MemoryPortReadInsns { - addr: *addr, - en: *en, - write_mode: *write_mode, - data: data.slice(field_index_range), - insns, - }, - ), - write.as_mut().map( - |MemoryPortWriteInsns { - addr, - en, - write_mode, - data, - mask, - insns, - }| { - MemoryPortWriteInsns { - addr: *addr, - en: *en, - write_mode: *write_mode, - data: data.slice(field_index_range), - mask: mask.slice(mask_field_index_range), - insns, - } - }, - ), - ); - start = start + field.ty.ty.bit_width(); - } - } - } - } - fn compile_memory_port_rw( - &mut self, - memory: StatePartIndex, - data_layout: CompiledTypeLayout, - mask_layout: CompiledTypeLayout, - mut read: Option>, - mut write: Option>, - ) { - let read_else_label = read.as_mut().map( - |MemoryPortReadInsns { - addr: _, - en, - write_mode, - data: _, - insns, - }| { - let else_label = self.insns.new_label(); - insns.push( - Insn::BranchIfSmallZero { - target: else_label.0, - value: *en, - } - .into(), - ); - if let Some(write_mode) = *write_mode { - insns.push( - Insn::BranchIfSmallNonZero { - target: else_label.0, - value: write_mode, - } - .into(), - ); - } - else_label - }, - ); - let write_end_label = write.as_mut().map( - |MemoryPortWriteInsns { - addr: _, - en, - write_mode, - data: _, - mask: _, - insns, - }| { - let end_label = self.insns.new_label(); - insns.push( - Insn::BranchIfSmallZero { - target: end_label.0, - value: *en, - } - .into(), - ); - if let Some(write_mode) = *write_mode { - insns.push( - Insn::BranchIfSmallZero { - target: end_label.0, - value: write_mode, - } - .into(), - ); - } - end_label - }, - ); - self.compile_memory_port_rw_helper( - memory, - data_layout.ty.bit_width(), - 0, - data_layout, - mask_layout, - read.as_mut().map( - |MemoryPortReadInsns { - addr, - en, - write_mode, - data, - insns, - }| MemoryPortReadInsns { - addr: *addr, - en: *en, - write_mode: *write_mode, - data: *data, - insns: *insns, - }, - ), - write.as_mut().map( - |MemoryPortWriteInsns { - addr, - en, - write_mode, - data, - mask, - insns, - }| MemoryPortWriteInsns { - addr: *addr, - en: *en, - write_mode: *write_mode, - data: *data, - mask: *mask, - insns: *insns, - }, - ), - ); - if let ( - Some(else_label), - Some(MemoryPortReadInsns { - addr: _, - en: _, - write_mode: _, - data, - insns, - }), - ) = (read_else_label, read) - { - let end_label = self.insns.new_label(); - insns.push( - Insn::Branch { - target: end_label.0, - } - .into(), - ); - insns.push(else_label.into()); - let TypeIndexRange { - small_slots, - big_slots, - } = data; - for dest in small_slots.iter() { - insns.push(Insn::ConstSmall { dest, value: 0 }.into()); - } - for dest in big_slots.iter() { - insns.push( - Insn::Const { - dest, - value: BigInt::ZERO.intern_sized(), - } - .into(), - ); - } - insns.push(end_label.into()); - } - if let (Some(end_label), Some(write)) = (write_end_label, write) { - write.insns.push(end_label.into()); - } - } - fn compile_memory( - &mut self, - mem: Mem, - instantiated_module: InstantiatedModule, - conditions: Interned<[Cond]>, - trace_decls: &mut Vec, - ) { - let data_layout = CompiledTypeLayout::get(mem.array_type().element()); - let mask_layout = CompiledTypeLayout::get(mem.array_type().element().mask_type()); - let read_latency_plus_1 = mem - .read_latency() - .checked_add(1) - .expect("read latency too big"); - let write_latency_plus_1 = mem - .write_latency() - .get() - .checked_add(1) - .expect("write latency too big"); - let read_cycle = match mem.read_under_write() { - ReadUnderWrite::Old => 0, - ReadUnderWrite::New => mem.read_latency(), - ReadUnderWrite::Undefined => mem.read_latency() / 2, // something other than Old or New - }; - let memory = self - .insns - .state_layout - .memories - .allocate(&StatePartLayout::scalar( - (), - MemoryData { - array_type: mem.array_type(), - data: mem.initial_value().unwrap_or_else(|| { - Intern::intern_owned(BitVec::repeat( - false, - mem.array_type().type_properties().bit_width, - )) - }), - }, - )) - .start; - let (ports, trace_ports) = mem - .ports() - .iter() - .map(|&port| { - let target_base = TargetBase::MemPort(port); - let target = TargetInInstantiatedModule { - instantiated_module, - target: target_base.into(), - }; - self.decl_conditions.insert(target, conditions); - let TraceDecl::Scope(TraceScope::MemPort(trace_port)) = - self.make_trace_decl(instantiated_module, target_base) - else { - unreachable!(); - }; - let clk = Expr::field(port.to_expr(), "clk"); - let clk = self.compile_expr(instantiated_module, clk); - let clk = self.compiled_expr_to_value(clk, mem.source_location()); - let clk_triggered = self - .compile_clock(clk.map_ty(Clock::from_canonical), mem.source_location()) - .clk_triggered; - let en = Expr::field(port.to_expr(), "en"); - let en = self.compile_expr(instantiated_module, en); - let en = self.compiled_expr_to_value(en, mem.source_location()); - let en = self.compiled_value_bool_dest_is_small(en, mem.source_location()); - let addr = Expr::field(port.to_expr(), "addr"); - let addr = self.compile_expr(instantiated_module, addr); - let addr = self.compiled_expr_to_value(addr, mem.source_location()); - let addr_ty = addr.layout.ty; - let addr = self.compiled_value_to_dyn_array_index( - addr.map_ty(UInt::from_canonical), - mem.source_location(), - ); - let read_data = port.port_kind().rdata_name().map(|name| { - let read_data = - self.compile_expr(instantiated_module, Expr::field(port.to_expr(), name)); - let read_data = self.compiled_expr_to_value(read_data, mem.source_location()); - read_data.range - }); - let write_data = port.port_kind().wdata_name().map(|name| { - let write_data = - self.compile_expr(instantiated_module, Expr::field(port.to_expr(), name)); - let write_data = self.compiled_expr_to_value(write_data, mem.source_location()); - write_data.range - }); - let write_mask = port.port_kind().wmask_name().map(|name| { - let write_mask = - self.compile_expr(instantiated_module, Expr::field(port.to_expr(), name)); - let write_mask = self.compiled_expr_to_value(write_mask, mem.source_location()); - write_mask.range - }); - let write_mode = port.port_kind().wmode_name().map(|name| { - let write_mode = - self.compile_expr(instantiated_module, Expr::field(port.to_expr(), name)); - let write_mode = self.compiled_expr_to_value(write_mode, mem.source_location()); - self.compiled_value_bool_dest_is_small(write_mode, mem.source_location()) - }); - struct PortParts { - en_delayed_len: usize, - addr_delayed_len: usize, - read_data_delayed_len: usize, - write_data_delayed_len: usize, - write_mask_delayed_len: usize, - write_mode_delayed_len: usize, - read_cycle: Option, - write_cycle: Option, - } - let PortParts { - en_delayed_len, - addr_delayed_len, - read_data_delayed_len, - write_data_delayed_len, - write_mask_delayed_len, - write_mode_delayed_len, - read_cycle, - write_cycle, - } = match port.port_kind() { - PortKind::ReadOnly => PortParts { - en_delayed_len: read_cycle + 1, - addr_delayed_len: read_cycle + 1, - read_data_delayed_len: read_latency_plus_1 - read_cycle, - write_data_delayed_len: 0, - write_mask_delayed_len: 0, - write_mode_delayed_len: 0, - read_cycle: Some(read_cycle), - write_cycle: None, - }, - PortKind::WriteOnly => PortParts { - en_delayed_len: write_latency_plus_1, - addr_delayed_len: write_latency_plus_1, - read_data_delayed_len: 0, - write_data_delayed_len: write_latency_plus_1, - write_mask_delayed_len: write_latency_plus_1, - write_mode_delayed_len: 0, - read_cycle: None, - write_cycle: Some(mem.write_latency().get()), - }, - PortKind::ReadWrite => { - let can_rw_at_end = match mem.read_under_write() { - ReadUnderWrite::Old => false, - ReadUnderWrite::New | ReadUnderWrite::Undefined => true, - }; - let latency_plus_1 = read_latency_plus_1; - if latency_plus_1 != write_latency_plus_1 || !can_rw_at_end { - todo!( - "not sure what to do, issue: \ - https://github.com/chipsalliance/firrtl-spec/issues/263" - ); - } - PortParts { - en_delayed_len: latency_plus_1, - addr_delayed_len: latency_plus_1, - read_data_delayed_len: 1, - write_data_delayed_len: latency_plus_1, - write_mask_delayed_len: latency_plus_1, - write_mode_delayed_len: latency_plus_1, - read_cycle: Some(latency_plus_1 - 1), - write_cycle: Some(latency_plus_1 - 1), - } - } - }; - let addr_delayed = self.allocate_delay_chain_small( - addr_delayed_len, - addr_ty.canonical(), - Some(addr), - None, - ); - let en_delayed = self.allocate_delay_chain_small( - en_delayed_len, - Bool.canonical(), - Some(en), - None, - ); - let read_data_delayed = self.allocate_delay_chain( - read_data_delayed_len, - &data_layout.layout, - None, - read_data, - |v| v, - ); - let write_data_delayed = self.allocate_delay_chain( - write_data_delayed_len, - &data_layout.layout, - write_data, - None, - |v| v, - ); - let write_mask_delayed = self.allocate_delay_chain( - write_mask_delayed_len, - &mask_layout.layout, - write_mask, - None, - |v| v, - ); - let write_mode_delayed = self.allocate_delay_chain_small( - write_mode_delayed_len, - Bool.canonical(), - write_mode, - None, - ); - let mut read_insns = Vec::new(); - let mut write_insns = Vec::new(); - self.compile_memory_port_rw( - memory, - data_layout, - mask_layout, - read_cycle.map(|read_cycle| MemoryPortReadInsns { - addr: addr_delayed[read_cycle], - en: en_delayed[read_cycle], - write_mode: write_mode_delayed.get(read_cycle).copied(), - data: read_data_delayed[0], - insns: &mut read_insns, - }), - write_cycle.map(|write_cycle| MemoryPortWriteInsns { - addr: addr_delayed[write_cycle], - en: en_delayed[write_cycle], - write_mode: write_mode_delayed.get(write_cycle).copied(), - data: write_data_delayed[write_cycle], - mask: write_mask_delayed[write_cycle], - insns: &mut write_insns, - }), - ); - self.add_assignment(Interned::default(), read_insns, mem.source_location()); - ( - MemoryPort { - clk_triggered, - addr_delayed, - en_delayed, - data_layout, - read_data_delayed, - write_data_delayed, - write_mask_delayed, - write_mode_delayed, - write_insns, - }, - trace_port, - ) - }) - .unzip(); - let name = mem.scoped_name().1 .0; - let id = TraceMemoryId(self.memories.len()); - let stride = mem.array_type().element().bit_width(); - let trace = TraceMem { - id, - name, - stride, - element_type: self - .make_trace_decl_child( - instantiated_module, - MakeTraceDeclTarget::Memory { - id, - depth: mem.array_type().len(), - stride, - start: 0, - ty: mem.array_type().element(), - }, - name, - mem.source_location(), - ) - .intern_sized(), - ports: Intern::intern_owned(trace_ports), - array_type: mem.array_type(), - }; - trace_decls.push(trace.into()); - self.memories.push(Memory { - mem, - memory, - trace, - ports, - }); - } - fn compile_block( - &mut self, - parent_module: Interned, - block: Block, - conditions: Interned<[Cond]>, - trace_decls: &mut Vec, - ) { - let Block { memories, stmts } = block; - for memory in memories { - self.compile_memory(memory, *parent_module, conditions, trace_decls); - } - for stmt in stmts { - match stmt { - Stmt::Connect(StmtConnect { - lhs, - rhs, - source_location, - }) => self.compile_connect( - *parent_module, - conditions, - lhs, - *parent_module, - conditions, - rhs, - source_location, - ), - Stmt::Formal(StmtFormal { .. }) => todo!("implement simulating formal statements"), - Stmt::If(StmtIf { - cond, - source_location, - blocks: [then_block, else_block], - }) => { - let cond = self.compile_expr(*parent_module, Expr::canonical(cond)); - let cond = self.compiled_expr_to_value(cond, source_location); - let cond = cond.map_ty(Bool::from_canonical); - self.compile_block( - parent_module, - then_block, - Interned::from_iter(conditions.iter().copied().chain([Cond { - body: CondBody::IfTrue { cond }, - source_location, - }])), - trace_decls, - ); - self.compile_block( - parent_module, - else_block, - Interned::from_iter(conditions.iter().copied().chain([Cond { - body: CondBody::IfFalse { cond }, - source_location, - }])), - trace_decls, - ); - } - Stmt::Match(StmtMatch { - expr, - source_location, - blocks, - }) => { - let enum_expr = self.compile_expr(*parent_module, Expr::canonical(expr)); - let enum_expr = self.compiled_expr_to_value(enum_expr, source_location); - let enum_expr = enum_expr.map_ty(Enum::from_canonical); - let discriminant = self.compile_enum_discriminant(enum_expr, source_location); - for (variant_index, block) in blocks.into_iter().enumerate() { - self.compile_block( - parent_module, - block, - Interned::from_iter(conditions.iter().copied().chain([Cond { - body: CondBody::MatchArm { - discriminant, - variant_index, - }, - source_location, - }])), - trace_decls, - ); - } - } - Stmt::Declaration(declaration) => { - trace_decls.push(self.compile_declaration( - declaration, - parent_module, - conditions, - )); - } - } - } - } - fn compile_module(&mut self, module: Interned) -> &CompiledModule { - let mut trace_decls = Vec::new(); - let module_io = module - .leaf_module() - .module_io() - .iter() - .map( - |&AnnotatedModuleIO { - annotations: _, - module_io, - }| { - let target = TargetInInstantiatedModule { - instantiated_module: *module, - target: Target::from(module_io), - }; - self.decl_conditions.insert(target, Interned::default()); - trace_decls.push(self.make_trace_decl(*module, module_io.into())); - self.compile_value(target) - }, - ) - .collect(); - match module.leaf_module().body() { - ModuleBody::Normal(NormalModuleBody { body }) => { - self.compile_block(module, body, Interned::default(), &mut trace_decls); - } - ModuleBody::Extern(_extern_module_body) => { - todo!("simulating extern module: {:?}", module); - } - } - let hashbrown::hash_map::Entry::Vacant(entry) = self.modules.entry(*module) else { - unreachable!("compiled same instantiated module twice"); - }; - entry.insert(CompiledModule { - module_io, - trace_decls: TraceModule { - name: module.leaf_module().name(), - children: Intern::intern_owned(trace_decls), - }, - }) - } - fn process_assignments(&mut self) { - self.assignments - .finalize(self.insns.state_layout().ty.clone().into()); - if let Some(DebugOpaque(dump_assignments_dot)) = &self.dump_assignments_dot { - let graph = - petgraph::graph::DiGraph::<_, _, usize>::from_elements(self.assignments.elements()); - dump_assignments_dot(&petgraph::dot::Dot::new(&graph)); - } - let assignments_order: Vec<_> = match petgraph::algo::toposort(&self.assignments, None) { - Ok(nodes) => nodes - .into_iter() - .filter_map(|n| match n { - AssignmentOrSlotIndex::AssignmentIndex(v) => Some(v), - _ => None, - }) - .collect(), - Err(e) => match e.node_id() { - AssignmentOrSlotIndex::AssignmentIndex(assignment_index) => panic!( - "combinatorial logic cycle detected at: {}", - self.assignments.assignments()[assignment_index].source_location, - ), - AssignmentOrSlotIndex::SmallSlot(slot) => panic!( - "combinatorial logic cycle detected through: {}", - self.insns.state_layout().ty.small_slots.debug_data[slot.as_usize()].name, - ), - AssignmentOrSlotIndex::BigSlot(slot) => panic!( - "combinatorial logic cycle detected through: {}", - self.insns.state_layout().ty.big_slots.debug_data[slot.as_usize()].name, - ), - }, - }; - struct CondStackEntry<'a> { - cond: &'a Cond, - end_label: Label, - } - let mut cond_stack = Vec::>::new(); - for assignment_index in assignments_order { - let Assignment { - inputs: _, - outputs: _, - conditions, - insns, - source_location, - } = &self.assignments.assignments()[assignment_index]; - let mut same_len = 0; - for (index, (entry, cond)) in cond_stack.iter().zip(conditions).enumerate() { - if entry.cond != cond { - break; - } - same_len = index + 1; - } - while cond_stack.len() > same_len { - let CondStackEntry { cond: _, end_label } = - cond_stack.pop().expect("just checked len"); - self.insns.define_label_at_next_insn(end_label); - } - for cond in &conditions[cond_stack.len()..] { - let end_label = self.insns.new_label(); - match cond.body { - CondBody::IfTrue { cond: cond_value } - | CondBody::IfFalse { cond: cond_value } => { - let (branch_if_zero, branch_if_non_zero) = match cond_value.range.len() { - TypeLen::A_SMALL_SLOT => ( - Insn::BranchIfSmallZero { - target: end_label.0, - value: cond_value.range.small_slots.start, - }, - Insn::BranchIfSmallNonZero { - target: end_label.0, - value: cond_value.range.small_slots.start, - }, - ), - TypeLen::A_BIG_SLOT => ( - Insn::BranchIfZero { - target: end_label.0, - value: cond_value.range.big_slots.start, - }, - Insn::BranchIfNonZero { - target: end_label.0, - value: cond_value.range.big_slots.start, - }, - ), - _ => unreachable!(), - }; - self.insns.push( - if let CondBody::IfTrue { .. } = cond.body { - branch_if_zero - } else { - branch_if_non_zero - }, - cond.source_location, - ); - } - CondBody::MatchArm { - discriminant, - variant_index, - } => { - self.insns.push( - Insn::BranchIfSmallNeImmediate { - target: end_label.0, - lhs: discriminant, - rhs: variant_index as _, - }, - cond.source_location, - ); - } - } - cond_stack.push(CondStackEntry { cond, end_label }); - } - self.insns.extend(insns.iter().copied(), *source_location); - } - } - fn process_clocks(&mut self) -> Interned<[StatePartIndex]> { - mem::take(&mut self.clock_triggers) - .into_iter() - .map( - |ClockTrigger { - last_clk_was_low, - clk, - clk_triggered, - source_location, - }| { - self.insns.push( - Insn::XorSmallImmediate { - dest: last_clk_was_low, - lhs: clk, - rhs: 1, - }, - source_location, - ); - clk_triggered - }, - ) - .collect() - } - fn process_registers(&mut self) { - for Register { - value, - clk_triggered, - reset, - source_location, - } in mem::take(&mut self.registers) - { - match reset { - Some(RegisterReset { - is_async, - init, - rst, - }) => { - let reg_end = self.insns.new_label(); - let reg_reset = self.insns.new_label(); - let branch_if_reset = Insn::BranchIfSmallNonZero { - target: reg_reset.0, - value: rst, - }; - let branch_if_not_triggered = Insn::BranchIfSmallZero { - target: reg_end.0, - value: clk_triggered, - }; - if is_async { - self.insns.push(branch_if_reset, source_location); - self.insns.push(branch_if_not_triggered, source_location); - } else { - self.insns.push(branch_if_not_triggered, source_location); - self.insns.push(branch_if_reset, source_location); - } - self.insns.extend( - value.range.insns_for_copy_from(value.write_value().range), - source_location, - ); - self.insns - .push(Insn::Branch { target: reg_end.0 }, source_location); - self.insns.define_label_at_next_insn(reg_reset); - self.insns - .extend(value.range.insns_for_copy_from(init.range), source_location); - self.insns.define_label_at_next_insn(reg_end); - } - None => { - let reg_end = self.insns.new_label(); - self.insns.push( - Insn::BranchIfSmallZero { - target: reg_end.0, - value: clk_triggered, - }, - source_location, - ); - self.insns.extend( - value.range.insns_for_copy_from(value.write_value().range), - source_location, - ); - self.insns.define_label_at_next_insn(reg_end); - } - } - } - } - fn process_memories(&mut self) { - for memory_index in 0..self.memories.len() { - let Memory { - mem, - memory: _, - trace: _, - ref mut ports, - } = self.memories[memory_index]; - for MemoryPort { - clk_triggered, - addr_delayed, - en_delayed, - data_layout: _, - read_data_delayed, - write_data_delayed, - write_mask_delayed, - write_mode_delayed, - write_insns, - } in mem::take(ports) - { - let port_end = self.insns.new_label(); - let small_shift_reg = - |this: &mut Self, values: &[StatePartIndex]| { - for pair in values.windows(2).rev() { - this.insns.push( - Insn::CopySmall { - dest: pair[1], - src: pair[0], - }, - mem.source_location(), - ); - } - }; - let shift_reg = |this: &mut Self, values: &[TypeIndexRange]| { - for pair in values.windows(2).rev() { - this.insns - .extend(pair[0].insns_for_copy_to(pair[1]), mem.source_location()); - } - }; - self.insns.push( - Insn::BranchIfSmallZero { - target: port_end.0, - value: clk_triggered, - }, - mem.source_location(), - ); - small_shift_reg(self, &addr_delayed); - small_shift_reg(self, &en_delayed); - shift_reg(self, &write_data_delayed); - shift_reg(self, &write_mask_delayed); - small_shift_reg(self, &write_mode_delayed); - shift_reg(self, &read_data_delayed); - self.insns.extend(write_insns, mem.source_location()); - self.insns.define_label_at_next_insn(port_end); - } - } - } - pub fn compile(mut self) -> Compiled { - let base_module = - *self.compile_module(InstantiatedModule::Base(self.base_module).intern_sized()); - self.process_assignments(); - self.process_registers(); - self.process_memories(); - let clocks_triggered = self.process_clocks(); - self.insns - .push(Insn::Return, self.base_module.source_location()); - Compiled { - insns: Insns::from(self.insns).intern_sized(), - base_module, - io: Instance::new_unchecked( - ScopedNameId( - NameId("".intern(), Id::new()), - self.original_base_module.name_id(), - ), - self.original_base_module, - self.original_base_module.source_location(), - ), - traces: SimTraces(Intern::intern_owned(self.traces.0)), - trace_memories: Interned::from_iter(self.memories.iter().map( - |&Memory { - mem: _, - memory, - trace, - ports: _, - }| (memory, trace), - )), - clocks_triggered, - } - } -} - -#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] -struct CompiledModule { - module_io: Interned<[CompiledValue]>, - trace_decls: TraceModule, -} - -#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] -pub struct Compiled { - insns: Interned>, - base_module: CompiledModule, - io: Instance, - traces: SimTraces]>>, - trace_memories: Interned<[(StatePartIndex, TraceMem)]>, - clocks_triggered: Interned<[StatePartIndex]>, -} - -impl Compiled { - pub fn new(module: Interned>) -> Self { - Self::from_canonical(Compiler::new(module.canonical().intern()).compile()) - } - pub fn canonical(self) -> Compiled { - let Self { - insns, - base_module, - io, - traces, - trace_memories, - clocks_triggered, - } = self; - Compiled { - insns, - base_module, - io: Instance::from_canonical(io.canonical()), - traces, - trace_memories, - clocks_triggered, - } - } - pub fn from_canonical(canonical: Compiled) -> Self { - let Compiled { - insns, - base_module, - io, - traces, - trace_memories, - clocks_triggered, - } = canonical; - Self { - insns, - base_module, - io: Instance::from_canonical(io.canonical()), - traces, - trace_memories, - clocks_triggered, - } - } -} - #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct TraceScalarId(usize); @@ -5354,6 +413,15 @@ impl_trace_decl! { name: Interned, flow: Flow, }), + SimOnly(TraceSimOnly { + fn location(self) -> _ { + self.location + } + location: TraceLocation, + name: Interned, + ty: DynSimOnly, + flow: Flow, + }), }), } @@ -5455,6 +523,11 @@ pub trait TraceWriter: fmt::Debug + 'static { variant_index: usize, ty: Enum, ) -> Result<(), Self::Error>; + fn set_signal_sim_only_value( + &mut self, + id: TraceScalarId, + value: &DynSimOnlyValue, + ) -> Result<(), Self::Error>; } pub struct DynTraceWriterDecls(Box); @@ -5502,13 +575,18 @@ trait TraceWriterDynTrait: fmt::Debug + 'static { fn set_signal_clock_dyn(&mut self, id: TraceScalarId, value: bool) -> std::io::Result<()>; fn set_signal_sync_reset_dyn(&mut self, id: TraceScalarId, value: bool) -> std::io::Result<()>; fn set_signal_async_reset_dyn(&mut self, id: TraceScalarId, value: bool) - -> std::io::Result<()>; + -> std::io::Result<()>; fn set_signal_enum_discriminant_dyn( &mut self, id: TraceScalarId, variant_index: usize, ty: Enum, ) -> std::io::Result<()>; + fn set_signal_sim_only_value_dyn( + &mut self, + id: TraceScalarId, + value: &DynSimOnlyValue, + ) -> std::io::Result<()>; } impl TraceWriterDynTrait for T { @@ -5568,6 +646,13 @@ impl TraceWriterDynTrait for T { .map_err(err_into_io)?, ) } + fn set_signal_sim_only_value_dyn( + &mut self, + id: TraceScalarId, + value: &DynSimOnlyValue, + ) -> std::io::Result<()> { + Ok(TraceWriter::set_signal_sim_only_value(self, id, value).map_err(err_into_io)?) + } } pub struct DynTraceWriter(Box); @@ -5632,6 +717,13 @@ impl TraceWriter for DynTraceWriter { self.0 .set_signal_enum_discriminant_dyn(id, variant_index, ty) } + fn set_signal_sim_only_value( + &mut self, + id: TraceScalarId, + value: &DynSimOnlyValue, + ) -> Result<(), Self::Error> { + self.0.set_signal_sim_only_value_dyn(id, value) + } } #[derive(Debug)] @@ -5675,7 +767,7 @@ where } } -impl SimTraceDebug for Box +impl SimTraceDebug for Box where T: SimTraceDebug, { @@ -5699,15 +791,15 @@ where } } -#[derive(Clone, PartialEq, Eq, Hash)] -struct SimTrace { +#[derive(Clone, PartialEq, Eq, Hash, Debug)] +pub(crate) struct SimTrace { kind: K, state: S, last_state: S, } #[derive(Copy, Clone, PartialEq, Eq, Hash)] -struct SimTraces(T); +pub(crate) struct SimTraces(T); impl fmt::Debug for SimTraces where @@ -5734,7 +826,7 @@ impl SimTraceDebug for SimTrace { } } -impl SimTraceDebug for SimTrace { +impl SimTraceDebug for SimTrace { fn fmt(&self, id: TraceScalarId, f: &mut fmt::Formatter<'_>) -> fmt::Result { let Self { kind, @@ -5744,14 +836,14 @@ impl SimTraceDebug for SimTrace { f.debug_struct("SimTrace") .field("id", &id) .field("kind", kind) - .field("state", &BitSliceWriteWithBase(state)) - .field("last_state", &BitSliceWriteWithBase(last_state)) + .field("state", state) + .field("last_state", last_state) .finish() } } #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] -enum SimTraceKind { +pub(crate) enum SimTraceKind { BigUInt { index: StatePartIndex, ty: UInt, @@ -5796,16 +888,83 @@ enum SimTraceKind { index: StatePartIndex, ty: Enum, }, + SimOnly { + index: StatePartIndex, + ty: DynSimOnly, + }, +} + +#[derive(PartialEq, Eq)] +pub(crate) enum SimTraceState { + Bits(BitVec), + SimOnly(DynSimOnlyValue), +} + +impl Clone for SimTraceState { + fn clone(&self) -> Self { + match self { + Self::Bits(v) => Self::Bits(v.clone()), + Self::SimOnly(v) => Self::SimOnly(v.clone()), + } + } + fn clone_from(&mut self, source: &Self) { + match (&mut *self, source) { + (SimTraceState::Bits(dest), SimTraceState::Bits(source)) => { + dest.clone_from_bitslice(source); + } + _ => *self = source.clone(), + } + } +} + +impl SimTraceState { + fn unwrap_bits_ref(&self) -> &BitVec { + if let SimTraceState::Bits(v) = self { + v + } else { + unreachable!() + } + } + fn unwrap_bits_mut(&mut self) -> &mut BitVec { + if let SimTraceState::Bits(v) = self { + v + } else { + unreachable!() + } + } + fn unwrap_sim_only_ref(&self) -> &DynSimOnlyValue { + if let SimTraceState::SimOnly(v) = self { + v + } else { + unreachable!() + } + } + fn unwrap_sim_only_mut(&mut self) -> &mut DynSimOnlyValue { + if let SimTraceState::SimOnly(v) = self { + v + } else { + unreachable!() + } + } +} + +impl fmt::Debug for SimTraceState { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + SimTraceState::Bits(v) => BitSliceWriteWithBase(v).fmt(f), + SimTraceState::SimOnly(v) => v.fmt(f), + } + } } impl SimTraceKind { - fn make_state(self) -> BitVec { + fn make_state(self) -> SimTraceState { match self { SimTraceKind::BigUInt { index: _, ty } | SimTraceKind::SmallUInt { index: _, ty } => { - BitVec::repeat(false, ty.width) + SimTraceState::Bits(BitVec::repeat(false, ty.width)) } SimTraceKind::BigSInt { index: _, ty } | SimTraceKind::SmallSInt { index: _, ty } => { - BitVec::repeat(false, ty.width) + SimTraceState::Bits(BitVec::repeat(false, ty.width)) } SimTraceKind::BigBool { index: _ } | SimTraceKind::BigAsyncReset { index: _ } @@ -5814,688 +973,96 @@ impl SimTraceKind { | SimTraceKind::SmallBool { index: _ } | SimTraceKind::SmallAsyncReset { index: _ } | SimTraceKind::SmallSyncReset { index: _ } - | SimTraceKind::SmallClock { index: _ } => BitVec::repeat(false, 1), + | SimTraceKind::SmallClock { index: _ } => { + SimTraceState::Bits(BitVec::repeat(false, 1)) + } SimTraceKind::EnumDiscriminant { index: _, ty } => { - BitVec::repeat(false, ty.discriminant_bit_width()) + SimTraceState::Bits(BitVec::repeat(false, ty.discriminant_bit_width())) } + SimTraceKind::SimOnly { index: _, ty } => SimTraceState::SimOnly(ty.default_value()), } } } -#[derive(Clone, PartialEq, Eq, Hash, Debug)] -pub struct SimValue { - ty: T, - bits: BitVec, +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +enum MaybeNeedsSettle { + NeedsSettle(S), + NoSettleNeeded(N), } -impl SimValue { - #[track_caller] - fn to_expr_impl(ty: CanonicalType, bits: &BitSlice) -> Expr { - match ty { - CanonicalType::UInt(_) => Expr::canonical(::bits_to_expr(Cow::Borrowed(bits))), - CanonicalType::SInt(_) => Expr::canonical(::bits_to_expr(Cow::Borrowed(bits))), - CanonicalType::Bool(_) => Expr::canonical(Bool::bits_to_expr(Cow::Borrowed(bits))), - CanonicalType::Array(ty) => { - let element_bit_width = ty.element().bit_width(); - Expr::::canonical( - crate::expr::ops::ArrayLiteral::new( - ty.element(), - (0..ty.len()) - .map(|array_index| { - let start = element_bit_width * array_index; - let end = start + element_bit_width; - Self::to_expr_impl(ty.element(), &bits[start..end]) - }) - .collect(), - ) - .to_expr(), - ) - } - CanonicalType::Enum(ty) => { - let discriminant_bit_width = ty.discriminant_bit_width(); - let mut variant_index = [0; mem::size_of::()]; - variant_index.view_bits_mut::()[0..discriminant_bit_width] - .clone_from_bitslice(&bits[..discriminant_bit_width]); - let variant_index = usize::from_le_bytes(variant_index); - if let Some(variant) = ty.variants().get(variant_index) { - let data_bit_width = variant.ty.map_or(0, CanonicalType::bit_width); - Expr::canonical( - crate::expr::ops::EnumLiteral::new_by_index( - ty, - variant_index, - variant.ty.map(|ty| { - Self::to_expr_impl( - ty, - &bits[discriminant_bit_width - ..discriminant_bit_width + data_bit_width], - ) - }), - ) - .to_expr(), - ) - } else { - Expr::canonical(::bits_to_expr(Cow::Borrowed(bits)).cast_bits_to(ty)) - } - } - CanonicalType::Bundle(ty) => Expr::canonical( - crate::expr::ops::BundleLiteral::new( - ty, - ty.fields() - .iter() - .zip(ty.field_offsets().iter()) - .map(|(field, &field_offset)| { - Self::to_expr_impl( - field.ty, - &bits[field_offset..field_offset + field.ty.bit_width()], - ) - }) - .collect(), - ) - .to_expr(), - ), - CanonicalType::AsyncReset(ty) => { - Expr::canonical(Bool::bits_to_expr(Cow::Borrowed(bits)).cast_to(ty)) - } - CanonicalType::SyncReset(ty) => { - Expr::canonical(Bool::bits_to_expr(Cow::Borrowed(bits)).cast_to(ty)) - } - CanonicalType::Reset(_) => panic!( - "can't convert SimValue to Expr -- \ - can't deduce whether reset value should be sync or async" - ), - CanonicalType::Clock(ty) => { - Expr::canonical(Bool::bits_to_expr(Cow::Borrowed(bits)).cast_to(ty)) - } +impl MaybeNeedsSettle { + fn map(self, f: impl FnOnce(T) -> U) -> MaybeNeedsSettle { + match self { + MaybeNeedsSettle::NeedsSettle(v) => MaybeNeedsSettle::NeedsSettle(f(v)), + MaybeNeedsSettle::NoSettleNeeded(v) => MaybeNeedsSettle::NoSettleNeeded(f(v)), } } } -impl ToExpr for SimValue { - type Type = T; +// workaround implementing FnOnce not being stable +trait MaybeNeedsSettleFn { + type Output; - #[track_caller] - fn to_expr(&self) -> Expr { - Expr::from_canonical(SimValue::to_expr_impl(self.ty.canonical(), &self.bits)) + fn call(self, arg: A) -> Self::Output; +} + +impl O, A, O> MaybeNeedsSettleFn for T { + type Output = O; + + fn call(self, arg: A) -> Self::Output { + self(arg) } } -impl ToSimValue for SimValue { - #[track_caller] - fn to_sim_value(&self, ty: T) -> SimValue { - assert_eq!(self.ty, ty); - self.clone() - } - - #[track_caller] - fn into_sim_value(self, ty: T) -> SimValue { - assert_eq!(self.ty, ty); - self - } - - #[track_caller] - fn box_into_sim_value(self: Box, ty: T) -> SimValue { - assert_eq!(self.ty, ty); - *self - } -} - -impl ToSimValue for BitVec { - #[track_caller] - fn to_sim_value(&self, ty: T) -> SimValue { - self.clone().into_sim_value(ty) - } - - #[track_caller] - fn into_sim_value(self, ty: T) -> SimValue { - assert_eq!(ty.canonical().bit_width(), self.len()); - SimValue { ty, bits: self } - } - - #[track_caller] - fn box_into_sim_value(self: Box, ty: T) -> SimValue { - Self::into_sim_value(*self, ty) - } -} - -impl ToSimValue for bitvec::boxed::BitBox { - #[track_caller] - fn to_sim_value(&self, ty: T) -> SimValue { - self.clone().into_sim_value(ty) - } - - #[track_caller] - fn into_sim_value(self, ty: T) -> SimValue { - assert_eq!(ty.canonical().bit_width(), self.len()); - SimValue { - ty, - bits: self.into_bitvec(), - } - } - - #[track_caller] - fn box_into_sim_value(self: Box, ty: T) -> SimValue { - Self::into_sim_value(*self, ty) - } -} - -impl ToSimValue for BitSlice { - #[track_caller] - fn to_sim_value(&self, ty: T) -> SimValue { - assert_eq!(ty.canonical().bit_width(), self.len()); - SimValue { - ty, - bits: self.to_bitvec(), - } - } -} - -impl SimValue { - pub fn ty(&self) -> T { - self.ty - } - pub fn bits(&self) -> &BitSlice { - &self.bits - } - pub fn into_bits(self) -> BitVec { - self.bits - } - #[track_caller] - pub fn from_canonical(v: SimValue) -> Self { - Self { - ty: T::from_canonical(v.ty), - bits: v.bits, - } - } - pub fn into_canonical(self) -> SimValue { - SimValue { - ty: self.ty.canonical(), - bits: self.bits, - } - } - #[track_caller] - pub fn from_dyn_int(v: SimValue) -> Self +impl MaybeNeedsSettle { + fn apply_no_settle(self, arg: T) -> MaybeNeedsSettle where - T: IntType, + N: MaybeNeedsSettleFn, { - Self { - ty: T::from_dyn_int(v.ty), - bits: v.bits, - } - } - pub fn into_dyn_int(self) -> SimValue - where - T: IntType, - { - SimValue { - ty: self.ty.as_dyn_int(), - bits: self.bits, - } - } - #[track_caller] - pub fn from_bundle(v: SimValue) -> Self - where - T: BundleType, - { - Self { - ty: T::from_canonical(CanonicalType::Bundle(v.ty)), - bits: v.bits, - } - } - pub fn into_bundle(self) -> SimValue - where - T: BundleType, - { - SimValue { - ty: Bundle::from_canonical(self.ty.canonical()), - bits: self.bits, - } - } - #[track_caller] - pub fn from_enum(v: SimValue) -> Self - where - T: EnumType, - { - Self { - ty: T::from_canonical(CanonicalType::Enum(v.ty)), - bits: v.bits, - } - } - pub fn into_enum(self) -> SimValue - where - T: EnumType, - { - SimValue { - ty: Enum::from_canonical(self.ty.canonical()), - bits: self.bits, + match self { + MaybeNeedsSettle::NeedsSettle(v) => MaybeNeedsSettle::NeedsSettle(v), + MaybeNeedsSettle::NoSettleNeeded(v) => MaybeNeedsSettle::NoSettleNeeded(v.call(arg)), } } } -pub trait ToSimValue { - #[track_caller] - fn to_sim_value(&self, ty: T) -> SimValue; - #[track_caller] - fn into_sim_value(self, ty: T) -> SimValue - where - Self: Sized, - { - self.to_sim_value(ty) - } - #[track_caller] - fn box_into_sim_value(self: Box, ty: T) -> SimValue { - self.to_sim_value(ty) - } -} - -impl, T: Type> ToSimValue for &'_ This { - #[track_caller] - fn to_sim_value(&self, ty: T) -> SimValue { - This::to_sim_value(self, ty) - } -} - -impl, T: Type> ToSimValue for &'_ mut This { - #[track_caller] - fn to_sim_value(&self, ty: T) -> SimValue { - This::to_sim_value(self, ty) - } -} - -impl, T: Type> ToSimValue for Box { - #[track_caller] - fn to_sim_value(&self, ty: T) -> SimValue { - This::to_sim_value(self, ty) - } - #[track_caller] - fn into_sim_value(self, ty: T) -> SimValue { - This::box_into_sim_value(self, ty) - } - #[track_caller] - fn box_into_sim_value(self: Box, ty: T) -> SimValue { - This::box_into_sim_value(*self, ty) - } -} - -impl + Send + Sync + 'static, T: Type> ToSimValue - for Interned -{ - #[track_caller] - fn to_sim_value(&self, ty: T) -> SimValue { - This::to_sim_value(self, ty) - } -} - -impl SimValue> { - #[track_caller] - pub fn from_array_elements< - I: IntoIterator, IntoIter: ExactSizeIterator>, - >( - elements: I, - ty: ArrayType, - ) -> Self { - let mut iter = elements.into_iter(); - assert_eq!(iter.len(), ty.len()); - let Some(first) = iter.next() else { - return SimValue { - ty, - bits: BitVec::new(), - }; - }; - let SimValue { - ty: element_ty, - mut bits, - } = first.into_sim_value(ty.element()); - assert_eq!(element_ty, ty.element()); - bits.reserve(ty.type_properties().bit_width - bits.len()); - for element in iter { - let SimValue { - ty: element_ty, - bits: element_bits, - } = element.into_sim_value(ty.element()); - assert_eq!(element_ty, ty.element()); - bits.extend_from_bitslice(&element_bits); - } - SimValue { ty, bits } - } -} - -impl, T: Type> ToSimValue> for [Element] { - #[track_caller] - fn to_sim_value(&self, ty: Array) -> SimValue> { - SimValue::from_array_elements(self, ty) - } - #[track_caller] - fn box_into_sim_value(self: Box, ty: Array) -> SimValue> { - SimValue::from_array_elements(self, ty) - } -} - -impl> ToSimValue for [Element] { - #[track_caller] - fn to_sim_value(&self, ty: CanonicalType) -> SimValue { - SimValue::from_array_elements(self, ::from_canonical(ty)).into_canonical() - } - #[track_caller] - fn box_into_sim_value(self: Box, ty: CanonicalType) -> SimValue { - SimValue::from_array_elements(self, ::from_canonical(ty)).into_canonical() - } -} - -impl, T: Type, const N: usize> ToSimValue> for [Element; N] -where - ConstUsize: KnownSize, -{ - #[track_caller] - fn to_sim_value(&self, ty: Array) -> SimValue> { - SimValue::from_array_elements(self, ty) - } - #[track_caller] - fn into_sim_value(self, ty: Array) -> SimValue> { - SimValue::from_array_elements(self, ty) - } - #[track_caller] - fn box_into_sim_value(self: Box, ty: Array) -> SimValue> { - SimValue::from_array_elements( as From>>::from(self), ty) - } -} - -impl, T: Type, const N: usize> ToSimValue> for [Element; N] -where - ConstUsize: KnownSize, -{ - #[track_caller] - fn to_sim_value(&self, ty: Array) -> SimValue> { - SimValue::from_array_elements(self, ty) - } - #[track_caller] - fn into_sim_value(self, ty: Array) -> SimValue> { - SimValue::from_array_elements(self, ty) - } - #[track_caller] - fn box_into_sim_value(self: Box, ty: Array) -> SimValue> { - SimValue::from_array_elements( as From>>::from(self), ty) - } -} - -impl, const N: usize> ToSimValue - for [Element; N] -{ - #[track_caller] - fn to_sim_value(&self, ty: CanonicalType) -> SimValue { - SimValue::from_array_elements(self, ::from_canonical(ty)).into_canonical() - } - #[track_caller] - fn into_sim_value(self, ty: CanonicalType) -> SimValue { - SimValue::from_array_elements(self, ::from_canonical(ty)).into_canonical() - } - #[track_caller] - fn box_into_sim_value(self: Box, ty: CanonicalType) -> SimValue { - SimValue::from_array_elements( - as From>>::from(self), - ::from_canonical(ty), - ) - .into_canonical() - } -} - -impl, T: Type> ToSimValue> for Vec { - #[track_caller] - fn to_sim_value(&self, ty: Array) -> SimValue> { - SimValue::from_array_elements(self, ty) - } - #[track_caller] - fn into_sim_value(self, ty: Array) -> SimValue> { - SimValue::from_array_elements(self, ty) - } - #[track_caller] - fn box_into_sim_value(self: Box, ty: Array) -> SimValue> { - SimValue::from_array_elements(*self, ty) - } -} - -impl> ToSimValue for Vec { - #[track_caller] - fn to_sim_value(&self, ty: CanonicalType) -> SimValue { - SimValue::from_array_elements(self, ::from_canonical(ty)).into_canonical() - } - #[track_caller] - fn into_sim_value(self, ty: CanonicalType) -> SimValue { - SimValue::from_array_elements(self, ::from_canonical(ty)).into_canonical() - } - #[track_caller] - fn box_into_sim_value(self: Box, ty: CanonicalType) -> SimValue { - SimValue::from_array_elements(*self, ::from_canonical(ty)).into_canonical() - } -} - -impl ToSimValue for Expr { - #[track_caller] - fn to_sim_value(&self, ty: T) -> SimValue { - assert_eq!(Expr::ty(*self), ty); - SimValue { - ty, - bits: self - .to_literal_bits() - .expect("must be a literal expression") - .to_bitvec(), - } - } -} - -macro_rules! impl_to_sim_value_for_bool_like { - ($ty:ident) => { - impl ToSimValue<$ty> for bool { - fn to_sim_value(&self, ty: $ty) -> SimValue<$ty> { - SimValue { - ty, - bits: BitVec::repeat(*self, 1), - } - } - } - }; -} - -impl_to_sim_value_for_bool_like!(Bool); -impl_to_sim_value_for_bool_like!(AsyncReset); -impl_to_sim_value_for_bool_like!(SyncReset); -impl_to_sim_value_for_bool_like!(Reset); -impl_to_sim_value_for_bool_like!(Clock); - -impl ToSimValue for bool { - #[track_caller] - fn to_sim_value(&self, ty: CanonicalType) -> SimValue { - match ty { - CanonicalType::UInt(_) - | CanonicalType::SInt(_) - | CanonicalType::Array(_) - | CanonicalType::Enum(_) - | CanonicalType::Bundle(_) => { - panic!("can't create SimValue from bool: expected value of type: {ty:?}"); - } - CanonicalType::Bool(_) - | CanonicalType::AsyncReset(_) - | CanonicalType::SyncReset(_) - | CanonicalType::Reset(_) - | CanonicalType::Clock(_) => SimValue { - ty, - bits: BitVec::repeat(*self, 1), - }, - } - } -} - -macro_rules! impl_to_sim_value_for_primitive_int { - ($prim:ident) => { - impl ToSimValue<<$prim as ToExpr>::Type> for $prim { - #[track_caller] - fn to_sim_value( - &self, - ty: <$prim as ToExpr>::Type, - ) -> SimValue<<$prim as ToExpr>::Type> { - SimValue { - ty, - bits: <<$prim as ToExpr>::Type as BoolOrIntType>::le_bytes_to_bits_wrapping( - &self.to_le_bytes(), - ty.width(), - ), - } - } - } - - impl ToSimValue<<<$prim as ToExpr>::Type as IntType>::Dyn> for $prim { - #[track_caller] - fn to_sim_value( - &self, - ty: <<$prim as ToExpr>::Type as IntType>::Dyn, - ) -> SimValue<<<$prim as ToExpr>::Type as IntType>::Dyn> { - SimValue { - ty, - bits: <<$prim as ToExpr>::Type as BoolOrIntType>::le_bytes_to_bits_wrapping( - &self.to_le_bytes(), - ty.width(), - ), - } - } - } - - impl ToSimValue for $prim { - #[track_caller] - fn to_sim_value(&self, ty: CanonicalType) -> SimValue { - let ty: <<$prim as ToExpr>::Type as IntType>::Dyn = Type::from_canonical(ty); - self.to_sim_value(ty).into_canonical() - } - } - }; -} - -impl_to_sim_value_for_primitive_int!(u8); -impl_to_sim_value_for_primitive_int!(u16); -impl_to_sim_value_for_primitive_int!(u32); -impl_to_sim_value_for_primitive_int!(u64); -impl_to_sim_value_for_primitive_int!(u128); -impl_to_sim_value_for_primitive_int!(usize); -impl_to_sim_value_for_primitive_int!(i8); -impl_to_sim_value_for_primitive_int!(i16); -impl_to_sim_value_for_primitive_int!(i32); -impl_to_sim_value_for_primitive_int!(i64); -impl_to_sim_value_for_primitive_int!(i128); -impl_to_sim_value_for_primitive_int!(isize); - -macro_rules! impl_to_sim_value_for_int_value { - ($IntValue:ident, $Int:ident, $IntType:ident) => { - impl ToSimValue<$IntType> for $IntValue { - fn to_sim_value(&self, ty: $IntType) -> SimValue<$IntType> { - self.bits().to_bitvec().into_sim_value(ty) - } - - fn into_sim_value(self, ty: $IntType) -> SimValue<$IntType> { - Arc::try_unwrap(self.into_bits()) - .unwrap_or_else(|v: Arc| v.to_bitvec()) - .into_sim_value(ty) - } - - fn box_into_sim_value( - self: Box, - ty: $IntType, - ) -> SimValue<$IntType> { - Self::into_sim_value(*self, ty) - } - } - - impl ToSimValue<$Int> for $IntValue { - fn to_sim_value(&self, ty: $Int) -> SimValue<$Int> { - self.bits().to_bitvec().into_sim_value(ty) - } - - fn into_sim_value(self, ty: $Int) -> SimValue<$Int> { - Arc::try_unwrap(self.into_bits()) - .unwrap_or_else(|v: Arc| v.to_bitvec()) - .into_sim_value(ty) - } - - fn box_into_sim_value(self: Box, ty: $Int) -> SimValue<$Int> { - Self::into_sim_value(*self, ty) - } - } - - impl ToSimValue for $IntValue { - #[track_caller] - fn to_sim_value(&self, ty: CanonicalType) -> SimValue { - ToSimValue::<$Int>::to_sim_value(self, $Int::from_canonical(ty)).into_canonical() - } - - #[track_caller] - fn into_sim_value(self, ty: CanonicalType) -> SimValue { - ToSimValue::<$Int>::into_sim_value(self, $Int::from_canonical(ty)).into_canonical() - } - - #[track_caller] - fn box_into_sim_value(self: Box, ty: CanonicalType) -> SimValue { - Self::into_sim_value(*self, ty) - } - } - }; -} - -impl_to_sim_value_for_int_value!(UIntValue, UInt, UIntType); -impl_to_sim_value_for_int_value!(SIntValue, SInt, SIntType); - -struct SimulationImpl { - state: interpreter::State, - io: Expr, - uninitialized_inputs: HashMap>, +struct SimulationModuleState { + base_targets: Vec, + uninitialized_ios: HashMap>, io_targets: HashMap>, - made_initial_step: bool, - needs_settle: bool, - trace_decls: TraceModule, - traces: SimTraces]>>, - trace_memories: HashMap, TraceMem>, - trace_writers: Vec>, - instant: SimInstant, - clocks_triggered: Interned<[StatePartIndex]>, - breakpoints: Option, + did_initial_settle: bool, } -impl fmt::Debug for SimulationImpl { +impl fmt::Debug for SimulationModuleState { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.debug_fmt(None, f) + let Self { + base_targets, + uninitialized_ios, + io_targets, + did_initial_settle, + } = self; + f.debug_struct("SimulationModuleState") + .field("base_targets", base_targets) + .field("uninitialized_ios", &SortedSetDebug(uninitialized_ios)) + .field("io_targets", &SortedSetDebug(io_targets)) + .field("did_initial_settle", did_initial_settle) + .finish() } } -impl SimulationImpl { - fn debug_fmt(&self, io: Option<&dyn fmt::Debug>, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let Self { - state, - io: self_io, - uninitialized_inputs, - io_targets, - made_initial_step, - needs_settle, - trace_decls, - traces, - trace_memories, - trace_writers, - instant, - clocks_triggered, - breakpoints: _, - } = self; - f.debug_struct("Simulation") - .field("state", state) - .field("io", io.unwrap_or(self_io)) - .field( - "uninitialized_inputs", - &SortedSetDebug(uninitialized_inputs), - ) - .field("io_targets", &SortedMapDebug(io_targets)) - .field("made_initial_step", made_initial_step) - .field("needs_settle", needs_settle) - .field("trace_decls", trace_decls) - .field("traces", traces) - .field("trace_memories", trace_memories) - .field("trace_writers", trace_writers) - .field("instant", instant) - .field("clocks_triggered", clocks_triggered) - .finish_non_exhaustive() +impl SimulationModuleState { + fn new(base_targets: impl IntoIterator)>) -> Self { + let mut retval = Self { + base_targets: Vec::new(), + uninitialized_ios: HashMap::default(), + io_targets: HashMap::default(), + did_initial_settle: false, + }; + for (base_target, value) in base_targets { + retval.base_targets.push(base_target); + retval.parse_io(base_target, value); + } + retval } /// returns `true` if `target` or any sub-targets are uninitialized inputs fn parse_io(&mut self, target: Target, value: CompiledValue) -> bool { @@ -6504,7 +1071,7 @@ impl SimulationImpl { CompiledTypeLayoutBody::Scalar => match target.flow() { Flow::Source => false, Flow::Sink => { - self.uninitialized_inputs.insert(target, vec![]); + self.uninitialized_ios.insert(target, vec![]); true } Flow::Duplex => unreachable!(), @@ -6523,7 +1090,7 @@ impl SimulationImpl { if sub_targets.is_empty() { false } else { - self.uninitialized_inputs.insert(target, sub_targets); + self.uninitialized_ios.insert(target, sub_targets); true } } @@ -6541,20 +1108,506 @@ impl SimulationImpl { if sub_targets.is_empty() { false } else { - self.uninitialized_inputs.insert(target, sub_targets); + self.uninitialized_ios.insert(target, sub_targets); true } } } } + fn mark_target_as_initialized(&mut self, mut target: Target) { + fn remove_target_and_children( + uninitialized_ios: &mut HashMap>, + target: Target, + ) { + let Some(children) = uninitialized_ios.remove(&target) else { + return; + }; + for child in children { + remove_target_and_children(uninitialized_ios, child); + } + } + remove_target_and_children(&mut self.uninitialized_ios, target); + while let Some(target_child) = target.child() { + let parent = target_child.parent(); + for child in self + .uninitialized_ios + .get(&*parent) + .map(|v| &**v) + .unwrap_or(&[]) + { + if self.uninitialized_ios.contains_key(child) { + return; + } + } + target = *parent; + self.uninitialized_ios.remove(&target); + } + } + #[track_caller] + fn get_io( + &self, + mut target: Target, + which_module: WhichModule, + ) -> CompiledValue { + assert!( + target.canonical_ty().is_passive(), + "simulator read/write expression must have a passive type \ + (recursively contains no fields with `#[hdl(flip)]`)" + ); + if let Some(&retval) = self.io_targets.get(&target) { + return retval; + } + loop { + target = match target { + Target::Base(_) => break, + Target::Child(child) => { + match *child.path_element() { + TargetPathElement::BundleField(_) | TargetPathElement::ArrayElement(_) => {} + TargetPathElement::DynArrayElement(_) => panic!( + "simulator read/write expression must not have dynamic array indexes" + ), + } + *child.parent() + } + }; + } + match which_module { + WhichModule::Main => panic!( + "simulator read/write expression must be \ + an array element/field of `Simulation::io()`" + ), + WhichModule::Extern { .. } => panic!( + "simulator read/write expression must be \ + one of this module's inputs/outputs or an \ + array element/field of one of this module's inputs/outputs" + ), + } + } + #[track_caller] + fn read_helper( + &self, + io: Expr, + which_module: WhichModule, + ) -> MaybeNeedsSettle> { + let Some(target) = io.target() else { + match which_module { + WhichModule::Main => panic!( + "can't read from an expression that's not a field/element of `Simulation::io()`" + ), + WhichModule::Extern { .. } => panic!( + "can't read from an expression that's not based on one of this module's inputs/outputs" + ), + } + }; + let compiled_value = self.get_io(*target, which_module); + match target.flow() { + Flow::Source => { + if !self.uninitialized_ios.is_empty() { + match which_module { + WhichModule::Main => { + panic!("can't read from an output before initializing all inputs"); + } + WhichModule::Extern { .. } => { + panic!("can't read from an input before initializing all outputs"); + } + } + } + MaybeNeedsSettle::NeedsSettle(compiled_value) + } + Flow::Sink => { + if self.uninitialized_ios.contains_key(&*target) { + match which_module { + WhichModule::Main => panic!("can't read from an uninitialized input"), + WhichModule::Extern { .. } => { + panic!("can't read from an uninitialized output"); + } + } + } + MaybeNeedsSettle::NoSettleNeeded(compiled_value) + } + Flow::Duplex => unreachable!(), + } + } + #[track_caller] + fn write_helper( + &mut self, + io: Expr, + which_module: WhichModule, + ) -> CompiledValue { + let Some(target) = io.target() else { + match which_module { + WhichModule::Main => panic!( + "can't write to an expression that's not a field/element of `Simulation::io()`" + ), + WhichModule::Extern { .. } => panic!( + "can't write to an expression that's not based on one of this module's outputs" + ), + } + }; + let compiled_value = self.get_io(*target, which_module); + match target.flow() { + Flow::Source => match which_module { + WhichModule::Main => panic!("can't write to an output"), + WhichModule::Extern { .. } => panic!("can't write to an input"), + }, + Flow::Sink => {} + Flow::Duplex => unreachable!(), + } + if !self.did_initial_settle { + self.mark_target_as_initialized(*target); + } + compiled_value + } +} + +#[derive(Copy, Clone, Debug)] +enum WaitTarget { + Settle, + Instant(SimInstant), + Change { key: ChangeKey, value: ChangeValue }, +} + +#[derive(Clone)] +struct EarliestWaitTargets { + settle: bool, + instant: Option, + changes: HashMap, SimValue>, +} + +impl fmt::Debug for EarliestWaitTargets { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_set().entries(self.iter()).finish() + } +} + +impl Default for EarliestWaitTargets { + fn default() -> Self { + Self { + settle: false, + instant: None, + changes: HashMap::default(), + } + } +} + +impl EarliestWaitTargets { + fn settle() -> Self { + Self { + settle: true, + instant: None, + changes: HashMap::default(), + } + } + fn instant(instant: SimInstant) -> Self { + Self { + settle: false, + instant: Some(instant), + changes: HashMap::default(), + } + } + fn len(&self) -> usize { + self.settle as usize + self.instant.is_some() as usize + self.changes.len() + } + fn is_empty(&self) -> bool { + self.len() == 0 + } + fn clear(&mut self) { + let Self { + settle, + instant, + changes, + } = self; + *settle = false; + *instant = None; + changes.clear(); + } + fn insert( + &mut self, + value: impl std::borrow::Borrow, ChangeValue>>, + ) where + ChangeValue: std::borrow::Borrow>, + { + let value = value.borrow(); + match value { + WaitTarget::Settle => self.settle = true, + WaitTarget::Instant(instant) => { + if self.instant.is_none_or(|v| v > *instant) { + self.instant = Some(*instant); + } + } + WaitTarget::Change { key, value } => { + self.changes + .entry(*key) + .or_insert_with(|| value.borrow().clone()); + } + } + } + fn convert_earlier_instants_to_settle(&mut self, instant: SimInstant) { + if self.instant.is_some_and(|v| v <= instant) { + self.settle = true; + self.instant = None; + } + } + fn iter<'a>( + &'a self, + ) -> impl Clone + + Iterator, &'a SimValue>> + + 'a { + self.settle + .then_some(WaitTarget::Settle) + .into_iter() + .chain(self.instant.map(|instant| WaitTarget::Instant(instant))) + .chain( + self.changes + .iter() + .map(|(&key, value)| WaitTarget::Change { key, value }), + ) + } +} + +impl>> + Extend, ChangeValue>> for EarliestWaitTargets +{ + fn extend, ChangeValue>>>( + &mut self, + iter: T, + ) { + iter.into_iter().for_each(|v| self.insert(v)) + } +} + +impl<'a, ChangeValue: std::borrow::Borrow>> + Extend<&'a WaitTarget, ChangeValue>> for EarliestWaitTargets +{ + fn extend, ChangeValue>>>( + &mut self, + iter: T, + ) { + iter.into_iter().for_each(|v| self.insert(v)) + } +} + +impl FromIterator for EarliestWaitTargets +where + Self: Extend, +{ + fn from_iter>(iter: T) -> Self { + let mut retval = Self::default(); + retval.extend(iter); + retval + } +} + +struct SimulationExternModuleState { + module_state: SimulationModuleState, + sim: ExternModuleSimulation, + running_generator: Option + 'static>>>, + wait_targets: EarliestWaitTargets, +} + +impl fmt::Debug for SimulationExternModuleState { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let Self { + module_state, + sim, + running_generator, + wait_targets, + } = self; + f.debug_struct("SimulationExternModuleState") + .field("module_state", module_state) + .field("sim", sim) + .field( + "running_generator", + &running_generator.as_ref().map(|_| DebugAsDisplay("...")), + ) + .field("wait_targets", wait_targets) + .finish() + } +} + +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +enum WhichModule { + Main, + Extern { module_index: usize }, +} + +struct ReadBitFn { + compiled_value: CompiledValue, +} + +impl MaybeNeedsSettleFn<&'_ mut interpreter::State> for ReadBitFn { + type Output = bool; + + fn call(self, state: &mut interpreter::State) -> Self::Output { + match self.compiled_value.range.len().as_single() { + Some(TypeLenSingle::SmallSlot) => { + state.small_slots[self.compiled_value.range.small_slots.start] != 0 + } + Some(TypeLenSingle::BigSlot) => !state.big_slots + [self.compiled_value.range.big_slots.start] + .clone() + .is_zero(), + Some(TypeLenSingle::SimOnlySlot) | None => unreachable!(), + } + } +} + +struct ReadBoolOrIntFn { + compiled_value: CompiledValue, + io: Expr, +} + +impl MaybeNeedsSettleFn<&'_ mut interpreter::State> for ReadBoolOrIntFn { + type Output = I::Value; + + fn call(self, state: &mut interpreter::State) -> Self::Output { + let Self { compiled_value, io } = self; + match compiled_value.range.len().as_single() { + Some(TypeLenSingle::SmallSlot) => Expr::ty(io) + .value_from_int_wrapping(state.small_slots[compiled_value.range.small_slots.start]), + Some(TypeLenSingle::BigSlot) => Expr::ty(io).value_from_int_wrapping( + state.big_slots[compiled_value.range.big_slots.start].clone(), + ), + Some(TypeLenSingle::SimOnlySlot) | None => unreachable!(), + } + } +} + +struct ReadFn { + compiled_value: CompiledValue, + io: Expr, +} + +impl MaybeNeedsSettleFn<&'_ mut interpreter::State> for ReadFn { + type Output = SimValue; + + fn call(self, state: &mut interpreter::State) -> Self::Output { + let Self { compiled_value, io } = self; + SimulationImpl::read_no_settle_helper(state, io, compiled_value) + } +} + +struct GeneratorWaker; + +impl std::task::Wake for GeneratorWaker { + fn wake(self: Arc) { + panic!("can't await other kinds of futures in function passed to ExternalModuleSimulation"); + } +} + +#[derive(Default)] +struct ReadyToRunSet { + state_ready_to_run: bool, + extern_modules_ready_to_run: Vec, +} + +impl ReadyToRunSet { + fn clear(&mut self) { + let Self { + state_ready_to_run, + extern_modules_ready_to_run, + } = self; + *state_ready_to_run = false; + extern_modules_ready_to_run.clear(); + } +} + +struct SimulationImpl { + state: interpreter::State, + io: Expr, + main_module: SimulationModuleState, + extern_modules: Box<[SimulationExternModuleState]>, + state_ready_to_run: bool, + trace_decls: TraceModule, + traces: SimTraces]>>, + trace_memories: HashMap, TraceMem>, + trace_writers: Vec>, + instant: SimInstant, + clocks_triggered: Interned<[StatePartIndex]>, + breakpoints: Option, + generator_waker: std::task::Waker, +} + +impl fmt::Debug for SimulationImpl { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.debug_fmt(None, f) + } +} + +impl SimulationImpl { + fn debug_fmt(&self, io: Option<&dyn fmt::Debug>, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let Self { + state, + io: self_io, + main_module, + extern_modules, + state_ready_to_run, + trace_decls, + traces, + trace_memories, + trace_writers, + instant, + clocks_triggered, + breakpoints: _, + generator_waker: _, + } = self; + f.debug_struct("Simulation") + .field("state", state) + .field("io", io.unwrap_or(self_io)) + .field("main_module", main_module) + .field("extern_modules", extern_modules) + .field("state_ready_to_run", state_ready_to_run) + .field("trace_decls", trace_decls) + .field("traces", traces) + .field("trace_memories", trace_memories) + .field("trace_writers", trace_writers) + .field("instant", instant) + .field("clocks_triggered", clocks_triggered) + .finish_non_exhaustive() + } fn new(compiled: Compiled) -> Self { - let mut retval = Self { + let io_target = Target::from(compiled.io); + let extern_modules = Box::from_iter(compiled.extern_modules.iter().map( + |&CompiledExternModule { + module_io_targets, + module_io, + simulation, + }| { + SimulationExternModuleState { + module_state: SimulationModuleState::new( + module_io_targets + .iter() + .copied() + .zip(module_io.iter().copied()), + ), + sim: simulation, + running_generator: None, + wait_targets: EarliestWaitTargets::settle(), + } + }, + )); + Self { state: State::new(compiled.insns), io: compiled.io.to_expr(), - uninitialized_inputs: HashMap::new(), - io_targets: HashMap::new(), - made_initial_step: false, - needs_settle: true, + main_module: SimulationModuleState::new( + compiled + .io + .ty() + .fields() + .into_iter() + .zip(compiled.base_module.module_io) + .map(|(BundleField { name, .. }, value)| { + ( + io_target.join( + TargetPathElement::from(TargetPathBundleField { name }) + .intern_sized(), + ), + value, + ) + }), + ), + extern_modules, + state_ready_to_run: true, trace_decls: compiled.base_module.trace_decls, traces: SimTraces(Box::from_iter(compiled.traces.0.iter().map( |&SimTrace { @@ -6572,22 +1625,8 @@ impl SimulationImpl { instant: SimInstant::START, clocks_triggered: compiled.clocks_triggered, breakpoints: None, - }; - let io_target = Target::from(compiled.io); - for (BundleField { name, .. }, value) in compiled - .io - .ty() - .fields() - .into_iter() - .zip(compiled.base_module.module_io) - { - retval.parse_io( - io_target - .join(TargetPathElement::from(TargetPathBundleField { name }).intern_sized()), - value, - ); + generator_waker: Arc::new(GeneratorWaker).into(), } - retval } fn write_traces( &mut self, @@ -6630,24 +1669,25 @@ impl SimulationImpl { let id = TraceScalarId(id); match kind { SimTraceKind::BigUInt { .. } | SimTraceKind::SmallUInt { .. } => { - trace_writer.set_signal_uint(id, state)?; + trace_writer.set_signal_uint(id, state.unwrap_bits_ref())?; } SimTraceKind::BigSInt { .. } | SimTraceKind::SmallSInt { .. } => { - trace_writer.set_signal_sint(id, state)?; + trace_writer.set_signal_sint(id, state.unwrap_bits_ref())?; } SimTraceKind::BigBool { .. } | SimTraceKind::SmallBool { .. } => { - trace_writer.set_signal_bool(id, state[0])?; + trace_writer.set_signal_bool(id, state.unwrap_bits_ref()[0])?; } SimTraceKind::BigAsyncReset { .. } | SimTraceKind::SmallAsyncReset { .. } => { - trace_writer.set_signal_async_reset(id, state[0])?; + trace_writer.set_signal_async_reset(id, state.unwrap_bits_ref()[0])?; } SimTraceKind::BigSyncReset { .. } | SimTraceKind::SmallSyncReset { .. } => { - trace_writer.set_signal_sync_reset(id, state[0])?; + trace_writer.set_signal_sync_reset(id, state.unwrap_bits_ref()[0])?; } SimTraceKind::BigClock { .. } | SimTraceKind::SmallClock { .. } => { - trace_writer.set_signal_clock(id, state[0])?; + trace_writer.set_signal_clock(id, state.unwrap_bits_ref()[0])?; } SimTraceKind::EnumDiscriminant { ty, .. } => { + let state = state.unwrap_bits_ref(); let mut variant_index = [0; mem::size_of::()]; variant_index.view_bits_mut::()[0..state.len()] .clone_from_bitslice(state); @@ -6657,6 +1697,9 @@ impl SimulationImpl { ty, )?; } + SimTraceKind::SimOnly { .. } => { + trace_writer.set_signal_sim_only_value(id, state.unwrap_sim_only_ref())? + } } } Ok(trace_writer) @@ -6688,6 +1731,7 @@ impl SimulationImpl { } match kind { SimTraceKind::BigUInt { index, ty: _ } | SimTraceKind::BigSInt { index, ty: _ } => { + let state = state.unwrap_bits_mut(); let bigint = &self.state.big_slots[index]; let mut bytes = bigint.to_signed_bytes_le(); bytes.resize( @@ -6702,11 +1746,14 @@ impl SimulationImpl { | SimTraceKind::BigAsyncReset { index } | SimTraceKind::BigSyncReset { index } | SimTraceKind::BigClock { index } => { - state.set(0, !self.state.big_slots[index].is_zero()); + state + .unwrap_bits_mut() + .set(0, !self.state.big_slots[index].is_zero()); } SimTraceKind::SmallUInt { index, ty: _ } | SimTraceKind::SmallSInt { index, ty: _ } | SimTraceKind::EnumDiscriminant { index, ty: _ } => { + let state = state.unwrap_bits_mut(); let bytes = self.state.small_slots[index].to_le_bytes(); let bitslice = BitSlice::::from_slice(&bytes); let bitslice = &bitslice[..state.len()]; @@ -6716,18 +1763,139 @@ impl SimulationImpl { | SimTraceKind::SmallAsyncReset { index } | SimTraceKind::SmallSyncReset { index } | SimTraceKind::SmallClock { index } => { - state.set(0, self.state.small_slots[index] != 0); + state + .unwrap_bits_mut() + .set(0, self.state.small_slots[index] != 0); + } + SimTraceKind::SimOnly { index, ty: _ } => { + state + .unwrap_sim_only_mut() + .clone_from(&self.state.sim_only_slots[index]); } } if IS_INITIAL_STEP { - last_state.copy_from_bitslice(state); + last_state.clone_from(state); } } } #[track_caller] - fn advance_time(&mut self, duration: SimDuration) { - self.settle(); - self.instant += duration; + fn advance_time(this_ref: &Rc>, duration: SimDuration) { + let run_target = this_ref.borrow().instant + duration; + Self::run_until(this_ref, run_target); + } + /// clears `targets` + #[must_use] + fn yield_wait<'a>( + this: Rc>, + module_index: usize, + targets: &'a mut EarliestWaitTargets, + ) -> impl Future + 'a { + struct MyGenerator<'a> { + sim: Rc>, + yielded_at_all: bool, + module_index: usize, + targets: &'a mut EarliestWaitTargets, + } + impl Future for MyGenerator<'_> { + type Output = (); + + fn poll( + mut self: Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + ) -> Poll { + let this = &mut *self; + let mut sim = this.sim.borrow_mut(); + let sim = &mut *sim; + assert!( + cx.waker().will_wake(&sim.generator_waker), + "can't use ExternModuleSimulationState's methods outside of ExternModuleSimulation" + ); + this.targets.convert_earlier_instants_to_settle(sim.instant); + if this.targets.is_empty() { + this.targets.settle = true; + } + if this.targets.settle { + if this.yielded_at_all { + this.targets.clear(); + return Poll::Ready(()); + } + } + sim.extern_modules[this.module_index] + .wait_targets + .extend(this.targets.iter()); + this.targets.clear(); + this.yielded_at_all = true; + Poll::Pending + } + } + MyGenerator { + sim: this, + yielded_at_all: false, + module_index, + targets, + } + } + async fn yield_advance_time_or_settle( + this: Rc>, + module_index: usize, + duration: Option, + ) { + let mut targets = duration.map_or(EarliestWaitTargets::settle(), |duration| { + EarliestWaitTargets::instant(this.borrow().instant + duration) + }); + Self::yield_wait(this, module_index, &mut targets).await; + } + fn is_extern_module_ready_to_run(&mut self, module_index: usize) -> Option { + let module = &self.extern_modules[module_index]; + let mut retval = None; + for wait_target in module.wait_targets.iter() { + retval = match (wait_target, retval) { + (WaitTarget::Settle, _) => Some(self.instant), + (WaitTarget::Instant(instant), _) if instant <= self.instant => Some(self.instant), + (WaitTarget::Instant(instant), None) => Some(instant), + (WaitTarget::Instant(instant), Some(retval)) => Some(instant.min(retval)), + (WaitTarget::Change { key, value }, retval) => { + if Self::value_changed(&mut self.state, key, SimValue::opaque(value)) { + Some(self.instant) + } else { + retval + } + } + }; + if retval == Some(self.instant) { + break; + } + } + retval + } + fn get_ready_to_run_set(&mut self, ready_to_run_set: &mut ReadyToRunSet) -> Option { + ready_to_run_set.clear(); + let mut retval = None; + if self.state_ready_to_run { + ready_to_run_set.state_ready_to_run = true; + retval = Some(self.instant); + } + for module_index in 0..self.extern_modules.len() { + let Some(instant) = self.is_extern_module_ready_to_run(module_index) else { + continue; + }; + if let Some(retval) = &mut retval { + match instant.cmp(retval) { + std::cmp::Ordering::Less => ready_to_run_set.clear(), + std::cmp::Ordering::Equal => {} + std::cmp::Ordering::Greater => continue, + } + } else { + retval = Some(instant); + } + ready_to_run_set + .extern_modules_ready_to_run + .push(module_index); + } + retval + } + fn set_instant_no_sim(&mut self, instant: SimInstant) { + self.instant = instant; self.for_each_trace_writer_storing_error(|this, mut trace_writer_state| { match &mut trace_writer_state { TraceWriterState::Decls(_) | TraceWriterState::Init(_) => unreachable!(), @@ -6739,54 +1907,145 @@ impl SimulationImpl { Ok(trace_writer_state) }); } + #[must_use] #[track_caller] - fn settle(&mut self) { + fn run_state_settle_cycle(&mut self) -> bool { + self.state_ready_to_run = false; + self.state.setup_call(0); + if self.breakpoints.is_some() { + loop { + match self + .state + .run(self.breakpoints.as_mut().expect("just checked")) + { + RunResult::Break(break_action) => { + println!( + "hit breakpoint at:\n{:?}", + self.state.debug_insn_at(self.state.pc), + ); + match break_action { + BreakAction::DumpStateAndContinue => { + println!("{self:#?}"); + } + BreakAction::Continue => {} + } + } + RunResult::Return(()) => break, + } + } + } else { + let RunResult::Return(()) = self.state.run(()); + } + if self + .clocks_triggered + .iter() + .any(|i| self.state.small_slots[*i] != 0) + { + self.state_ready_to_run = true; + true + } else { + false + } + } + #[track_caller] + fn run_extern_modules_cycle( + this_ref: &Rc>, + generator_waker: &std::task::Waker, + extern_modules_ready_to_run: &[usize], + ) { + let mut this = this_ref.borrow_mut(); + for module_index in extern_modules_ready_to_run.iter().copied() { + let extern_module = &mut this.extern_modules[module_index]; + extern_module.wait_targets.clear(); + let mut generator = if !extern_module.module_state.did_initial_settle { + let sim = extern_module.sim; + drop(this); + Box::into_pin(sim.run(ExternModuleSimulationState { + sim_impl: this_ref.clone(), + module_index, + wait_for_changes_wait_targets: EarliestWaitTargets::default(), + })) + } else if let Some(generator) = extern_module.running_generator.take() { + drop(this); + generator + } else { + continue; + }; + let generator = match generator + .as_mut() + .poll(&mut std::task::Context::from_waker(generator_waker)) + { + Poll::Ready(()) => None, + Poll::Pending => Some(generator), + }; + this = this_ref.borrow_mut(); + this.extern_modules[module_index] + .module_state + .did_initial_settle = true; + if !this.extern_modules[module_index] + .module_state + .uninitialized_ios + .is_empty() + { + panic!( + "extern module didn't initialize all outputs before \ + waiting, settling, or reading any inputs: {}", + this.extern_modules[module_index].sim.source_location + ); + } + this.extern_modules[module_index].running_generator = generator; + } + } + /// clears `targets` + #[track_caller] + fn run_until(this_ref: &Rc>, run_target: SimInstant) { + let mut this = this_ref.borrow_mut(); + let mut ready_to_run_set = ReadyToRunSet::default(); + let generator_waker = this.generator_waker.clone(); assert!( - self.uninitialized_inputs.is_empty(), + this.main_module.uninitialized_ios.is_empty(), "didn't initialize all inputs", ); - for _ in 0..100000 { - if !self.needs_settle { - return; + let run_target = run_target.max(this.instant); + let mut settle_cycle = 0; + let mut run_extern_modules = true; + loop { + assert!(settle_cycle < 100000, "settle(): took too many steps"); + settle_cycle += 1; + let next_wait_target = match this.get_ready_to_run_set(&mut ready_to_run_set) { + Some(next_wait_target) if next_wait_target <= run_target => next_wait_target, + _ => break, + }; + if next_wait_target > this.instant { + settle_cycle = 0; + this.set_instant_no_sim(next_wait_target); } - self.state.setup_call(0); - if self.breakpoints.is_some() { - loop { - match self - .state - .run(self.breakpoints.as_mut().expect("just checked")) - { - RunResult::Break(break_action) => { - println!( - "hit breakpoint at:\n{:?}", - self.state.debug_insn_at(self.state.pc), - ); - match break_action { - BreakAction::DumpStateAndContinue => { - println!("{self:#?}"); - } - BreakAction::Continue => {} - } - } - RunResult::Return(()) => break, - } + if run_extern_modules { + drop(this); + Self::run_extern_modules_cycle( + this_ref, + &generator_waker, + &ready_to_run_set.extern_modules_ready_to_run, + ); + this = this_ref.borrow_mut(); + } + if ready_to_run_set.state_ready_to_run { + if this.run_state_settle_cycle() { + // wait for clocks to settle before running extern modules again + run_extern_modules = false; + } else { + run_extern_modules = true; } - } else { - let RunResult::Return(()) = self.state.run(()); } - if self.made_initial_step { - self.read_traces::(); + if this.main_module.did_initial_settle { + this.read_traces::(); } else { - self.read_traces::(); + this.read_traces::(); } - self.state.memory_write_log.sort_unstable(); - self.state.memory_write_log.dedup(); - self.made_initial_step = true; - self.needs_settle = self - .clocks_triggered - .iter() - .any(|i| self.state.small_slots[*i] != 0); - self.for_each_trace_writer_storing_error(|this, trace_writer_state| { + this.state.memory_write_log.sort_unstable(); + this.state.memory_write_log.dedup(); + this.main_module.did_initial_settle = true; + this.for_each_trace_writer_storing_error(|this, trace_writer_state| { Ok(match trace_writer_state { TraceWriterState::Decls(trace_writer_decls) => TraceWriterState::Running( this.init_trace_writer(trace_writer_decls.write_decls( @@ -6804,162 +2063,105 @@ impl SimulationImpl { TraceWriterState::Errored(e) => TraceWriterState::Errored(e), }) }); - self.state.memory_write_log.clear(); + this.state.memory_write_log.clear(); } - panic!("settle(): took too many steps"); - } - #[track_caller] - fn get_io(&self, target: Target) -> CompiledValue { - if let Some(&retval) = self.io_targets.get(&target) { - return retval; - } - if Some(&target) == self.io.target().as_deref() - || Some(target.base()) != self.io.target().map(|v| v.base()) - { - panic!("simulator read/write expression must be an array element/field of `Simulation::io()`"); - }; - panic!("simulator read/write expression must not have dynamic array indexes"); - } - fn mark_target_as_initialized(&mut self, mut target: Target) { - fn remove_target_and_children( - uninitialized_inputs: &mut HashMap>, - target: Target, - ) { - let Some(children) = uninitialized_inputs.remove(&target) else { - return; - }; - for child in children { - remove_target_and_children(uninitialized_inputs, child); - } - } - remove_target_and_children(&mut self.uninitialized_inputs, target); - while let Some(target_child) = target.child() { - let parent = target_child.parent(); - for child in self - .uninitialized_inputs - .get(&*parent) - .map(|v| &**v) - .unwrap_or(&[]) - { - if self.uninitialized_inputs.contains_key(child) { - return; - } - } - target = *parent; - self.uninitialized_inputs.remove(&target); + if run_target > this.instant { + this.set_instant_no_sim(run_target); } } #[track_caller] - fn read_helper(&mut self, io: Expr) -> CompiledValue { - let Some(target) = io.target() else { - panic!("can't read from expression that's not a field/element of `Simulation::io()`"); - }; - let compiled_value = self.get_io(*target); - if self.made_initial_step { - self.settle(); - } else { - match target.flow() { - Flow::Source => { - if !self.uninitialized_inputs.is_empty() { - panic!( - "can't read from an output before the simulation has made any steps" - ); - } - self.settle(); - } - Flow::Sink => { - if self.uninitialized_inputs.contains_key(&*target) { - panic!("can't read from an uninitialized input"); - } - } - Flow::Duplex => unreachable!(), - } - } - compiled_value + fn settle(this_ref: &Rc>) { + let run_target = this_ref.borrow().instant; + Self::run_until(this_ref, run_target); } - #[track_caller] - fn write_helper(&mut self, io: Expr) -> CompiledValue { - let Some(target) = io.target() else { - panic!("can't write to an expression that's not a field/element of `Simulation::io()`"); - }; - let compiled_value = self.get_io(*target); - match target.flow() { - Flow::Source => { - panic!("can't write to an output"); - } - Flow::Sink => {} - Flow::Duplex => unreachable!(), + fn get_module(&self, which_module: WhichModule) -> &SimulationModuleState { + match which_module { + WhichModule::Main => &self.main_module, + WhichModule::Extern { module_index } => &self.extern_modules[module_index].module_state, } - if !self.made_initial_step { - self.mark_target_as_initialized(*target); - } - self.needs_settle = true; - compiled_value } - #[track_caller] - fn read_bit(&mut self, io: Expr) -> bool { - let compiled_value = self.read_helper(Expr::canonical(io)); - match compiled_value.range.len() { - TypeLen::A_SMALL_SLOT => { - self.state.small_slots[compiled_value.range.small_slots.start] != 0 + fn get_module_mut(&mut self, which_module: WhichModule) -> &mut SimulationModuleState { + match which_module { + WhichModule::Main => &mut self.main_module, + WhichModule::Extern { module_index } => { + &mut self.extern_modules[module_index].module_state } - TypeLen::A_BIG_SLOT => !self.state.big_slots[compiled_value.range.big_slots.start] - .clone() - .is_zero(), - _ => unreachable!(), } } #[track_caller] - fn write_bit(&mut self, io: Expr, value: bool) { - let compiled_value = self.write_helper(io); - match compiled_value.range.len() { - TypeLen::A_SMALL_SLOT => { + fn read_bit( + &mut self, + io: Expr, + which_module: WhichModule, + ) -> MaybeNeedsSettle { + self.get_module(which_module) + .read_helper(Expr::canonical(io), which_module) + .map(|compiled_value| ReadBitFn { compiled_value }) + .apply_no_settle(&mut self.state) + } + #[track_caller] + fn write_bit(&mut self, io: Expr, value: bool, which_module: WhichModule) { + let compiled_value = self + .get_module_mut(which_module) + .write_helper(io, which_module); + self.state_ready_to_run = true; + match compiled_value.range.len().as_single() { + Some(TypeLenSingle::SmallSlot) => { self.state.small_slots[compiled_value.range.small_slots.start] = value as _; } - TypeLen::A_BIG_SLOT => { + Some(TypeLenSingle::BigSlot) => { self.state.big_slots[compiled_value.range.big_slots.start] = value.into() } - _ => unreachable!(), + Some(TypeLenSingle::SimOnlySlot) | None => unreachable!(), } } #[track_caller] - fn read_bool_or_int(&mut self, io: Expr) -> I::Value { - let compiled_value = self.read_helper(Expr::canonical(io)); - match compiled_value.range.len() { - TypeLen::A_SMALL_SLOT => Expr::ty(io).value_from_int_wrapping( - self.state.small_slots[compiled_value.range.small_slots.start], - ), - TypeLen::A_BIG_SLOT => Expr::ty(io).value_from_int_wrapping( - self.state.big_slots[compiled_value.range.big_slots.start].clone(), - ), - _ => unreachable!(), - } + fn read_bool_or_int( + &mut self, + io: Expr, + which_module: WhichModule, + ) -> MaybeNeedsSettle, I::Value> { + self.get_module(which_module) + .read_helper(Expr::canonical(io), which_module) + .map(|compiled_value| ReadBoolOrIntFn { compiled_value, io }) + .apply_no_settle(&mut self.state) } #[track_caller] - fn write_bool_or_int(&mut self, io: Expr, value: I::Value) { - let compiled_value = self.write_helper(Expr::canonical(io)); + fn write_bool_or_int( + &mut self, + io: Expr, + value: I::Value, + which_module: WhichModule, + ) { + let compiled_value = self + .get_module_mut(which_module) + .write_helper(Expr::canonical(io), which_module); + self.state_ready_to_run = true; let value: BigInt = value.into(); - match compiled_value.range.len() { - TypeLen::A_SMALL_SLOT => { + match compiled_value.range.len().as_single() { + Some(TypeLenSingle::SmallSlot) => { let mut small_value = value.iter_u64_digits().next().unwrap_or(0); if value.is_negative() { small_value = small_value.wrapping_neg(); } self.state.small_slots[compiled_value.range.small_slots.start] = small_value; } - TypeLen::A_BIG_SLOT => { + Some(TypeLenSingle::BigSlot) => { self.state.big_slots[compiled_value.range.big_slots.start] = value } - _ => unreachable!(), + Some(TypeLenSingle::SimOnlySlot) | None => unreachable!(), } } #[track_caller] - fn read_write_sim_value_helper( - &mut self, + fn read_write_sim_value_helper( + state: &mut interpreter::State, compiled_value: CompiledValue, - bits: &mut BitSlice, - read_write_big_scalar: impl Fn(bool, &mut BitSlice, &mut BigInt) + Copy, - read_write_small_scalar: impl Fn(bool, &mut BitSlice, &mut SmallUInt) + Copy, + start_index: OpaqueSimValueSize, + opaque: &mut Opaque, + read_write_big_scalar: impl Fn(bool, std::ops::Range, &mut Opaque, &mut BigInt) + Copy, + read_write_small_scalar: impl Fn(bool, std::ops::Range, &mut Opaque, &mut SmallUInt) + + Copy, + read_write_sim_only_scalar: impl Fn(usize, &mut Opaque, &mut DynSimOnlyValue) + Copy, ) { match compiled_value.layout.body { CompiledTypeLayoutBody::Scalar => { @@ -6974,26 +2176,39 @@ impl SimulationImpl { CanonicalType::SyncReset(_) => false, CanonicalType::Reset(_) => false, CanonicalType::Clock(_) => false, + CanonicalType::PhantomConst(_) => unreachable!(), + CanonicalType::DynSimOnly(_) => false, }; - match compiled_value.range.len() { - TypeLen::A_SMALL_SLOT => read_write_small_scalar( + let indexes = OpaqueSimValueSizeRange::from( + start_index..start_index + compiled_value.layout.ty.size(), + ); + match compiled_value.range.len().as_single() { + Some(TypeLenSingle::SmallSlot) => read_write_small_scalar( signed, - bits, - &mut self.state.small_slots[compiled_value.range.small_slots.start], + indexes.bit_width, + opaque, + &mut state.small_slots[compiled_value.range.small_slots.start], ), - TypeLen::A_BIG_SLOT => read_write_big_scalar( + Some(TypeLenSingle::BigSlot) => read_write_big_scalar( signed, - bits, - &mut self.state.big_slots[compiled_value.range.big_slots.start], + indexes.bit_width, + opaque, + &mut state.big_slots[compiled_value.range.big_slots.start], ), - _ => unreachable!(), + Some(TypeLenSingle::SimOnlySlot) => read_write_sim_only_scalar( + indexes.sim_only_values_len.start, + opaque, + &mut state.sim_only_slots[compiled_value.range.sim_only_slots.start], + ), + None => unreachable!(), } } CompiledTypeLayoutBody::Array { element } => { let ty = ::from_canonical(compiled_value.layout.ty); - let element_bit_width = ty.element().bit_width(); + let element_size = ty.element().size(); for element_index in 0..ty.len() { - self.read_write_sim_value_helper( + Self::read_write_sim_value_helper( + state, CompiledValue { layout: *element, range: compiled_value @@ -7001,23 +2216,26 @@ impl SimulationImpl { .index_array(element.layout.len(), element_index), write: None, }, - &mut bits[element_index * element_bit_width..][..element_bit_width], + start_index + element_index * element_size, + opaque, read_write_big_scalar, read_write_small_scalar, + read_write_sim_only_scalar, ); } } CompiledTypeLayoutBody::Bundle { fields } => { let ty = Bundle::from_canonical(compiled_value.layout.ty); for ( - (field, offset), + (_field, offset), CompiledBundleField { offset: layout_offset, ty: field_layout, }, ) in ty.fields().iter().zip(ty.field_offsets()).zip(fields) { - self.read_write_sim_value_helper( + Self::read_write_sim_value_helper( + state, CompiledValue { layout: field_layout, range: compiled_value.range.slice(TypeIndexRange::new( @@ -7026,57 +2244,201 @@ impl SimulationImpl { )), write: None, }, - &mut bits[offset..][..field.ty.bit_width()], + start_index + offset, + opaque, read_write_big_scalar, read_write_small_scalar, + read_write_sim_only_scalar, ); } } } } #[track_caller] - fn read(&mut self, io: Expr) -> SimValue { - let compiled_value = self.read_helper(io); - let mut bits = BitVec::repeat(false, compiled_value.layout.ty.bit_width()); - self.read_write_sim_value_helper( + fn read_no_settle_helper( + state: &mut interpreter::State, + io: Expr, + compiled_value: CompiledValue, + ) -> SimValue { + #[track_caller] + fn read_write_sim_only_scalar( + index: usize, + writer: &mut OpaqueSimValueWriter<'_>, + value: &mut DynSimOnlyValue, + ) { + assert_eq!(writer.sim_only_values_range().start, index); + writer.fill_prefix_with( + OpaqueSimValueSize { + bit_width: 0, + sim_only_values_len: 1, + }, + |writer| { + writer.fill_cloned_from_slice(OpaqueSimValueSlice::from_parts( + BitSlice::empty(), + std::array::from_ref::(value), + )) + }, + ); + } + let size = Expr::ty(io).size(); + let mut opaque = OpaqueSimValue::with_capacity(size); + opaque.rewrite_with(size, |mut writer| { + SimulationImpl::read_write_sim_value_helper( + state, + compiled_value, + OpaqueSimValueSize::empty(), + &mut writer, + |_signed, bit_range, writer, value| { + writer.fill_prefix_with( + OpaqueSimValueSize::from_bit_width(bit_range.len()), + |writer| { + writer.fill_with_bits_with(|bits| { + ::copy_bits_from_bigint_wrapping(value, bits); + }) + }, + ); + }, + |_signed, bit_range, writer, value| { + let bytes = value.to_le_bytes(); + let bitslice = BitSlice::::from_slice(&bytes); + let bitslice = &bitslice[..bit_range.len()]; + writer.fill_prefix_with( + OpaqueSimValueSize::from_bit_width(bit_range.len()), + |writer| { + writer.fill_with_bits_with(|bits| bits.clone_from_bitslice(bitslice)) + }, + ); + }, + read_write_sim_only_scalar, + ); + writer.fill_cloned_from_slice(OpaqueSimValueSlice::empty()) + }); + SimValue::from_opaque(Expr::ty(io), opaque) + } + /// doesn't modify `opaque` + fn value_changed( + state: &mut interpreter::State, + compiled_value: CompiledValue, + mut opaque: &OpaqueSimValue, + ) -> bool { + assert_eq!(opaque.size(), compiled_value.layout.ty.size()); + let any_change = std::cell::Cell::new(false); + SimulationImpl::read_write_sim_value_helper( + state, compiled_value, - &mut bits, - |_signed, bits, value| ::copy_bits_from_bigint_wrapping(value, bits), - |_signed, bits, value| { + OpaqueSimValueSize::empty(), + &mut opaque, + |_signed, bit_range, opaque, value| { + if !::bits_equal_bigint_wrapping(value, &opaque.bits().bits()[bit_range]) { + any_change.set(true); + } + }, + |_signed, bit_range, opaque, value| { let bytes = value.to_le_bytes(); let bitslice = BitSlice::::from_slice(&bytes); - bits.clone_from_bitslice(&bitslice[..bits.len()]); + let bitslice = &bitslice[..bit_range.len()]; + if opaque.bits().bits()[bit_range] != *bitslice { + any_change.set(true); + } + }, + |index, opaque, value| { + if opaque.sim_only_values()[index] != *value { + any_change.set(true); + } }, ); - SimValue { - ty: Expr::ty(io), - bits, - } + any_change.get() } #[track_caller] - fn write(&mut self, io: Expr, value: SimValue) { - let compiled_value = self.write_helper(io); - assert_eq!(Expr::ty(io), value.ty()); - self.read_write_sim_value_helper( + fn read( + &mut self, + io: Expr, + which_module: WhichModule, + ) -> ( + CompiledValue, + MaybeNeedsSettle>, + ) { + let compiled_value = self.get_module(which_module).read_helper(io, which_module); + let value = compiled_value + .map(|compiled_value| ReadFn { compiled_value, io }) + .apply_no_settle(&mut self.state); + let (MaybeNeedsSettle::NeedsSettle(compiled_value) + | MaybeNeedsSettle::NoSettleNeeded(compiled_value)) = compiled_value; + (compiled_value, value) + } + #[track_caller] + fn write( + &mut self, + io: Expr, + value: &SimValue, + which_module: WhichModule, + ) { + let compiled_value = self + .get_module_mut(which_module) + .write_helper(io, which_module); + self.state_ready_to_run = true; + assert_eq!(Expr::ty(io), SimValue::ty(value)); + Self::read_write_sim_value_helper( + &mut self.state, compiled_value, - &mut value.into_bits(), - |signed, bits, value| { + OpaqueSimValueSize::empty(), + &mut SimValue::opaque(value), + |signed, bit_range, opaque, value| { if signed { - *value = SInt::bits_to_bigint(bits); + *value = SInt::bits_to_bigint(&opaque.bits().bits()[bit_range]); } else { - *value = UInt::bits_to_bigint(bits); + *value = UInt::bits_to_bigint(&opaque.bits().bits()[bit_range]); } }, - |signed, bits, value| { + |signed, bit_range, opaque, value| { let mut small_value = [0; mem::size_of::()]; - if signed && bits.last().as_deref().copied() == Some(true) { + if signed + && opaque.bits().bits()[bit_range.clone()] + .last() + .as_deref() + .copied() + == Some(true) + { small_value.fill(u8::MAX); } - small_value.view_bits_mut::()[0..bits.len()].clone_from_bitslice(bits); + small_value.view_bits_mut::()[0..bit_range.len()] + .clone_from_bitslice(&opaque.bits().bits()[bit_range]); *value = SmallUInt::from_le_bytes(small_value); }, + |index, opaque, value: &mut DynSimOnlyValue| { + value.clone_from(&opaque.sim_only_values()[index]); + }, ); } + #[track_caller] + fn settle_if_needed(this_ref: &Rc>, v: MaybeNeedsSettle) -> O + where + for<'a> F: MaybeNeedsSettleFn<&'a mut interpreter::State, Output = O>, + { + match v { + MaybeNeedsSettle::NeedsSettle(v) => { + Self::settle(this_ref); + v.call(&mut this_ref.borrow_mut().state) + } + MaybeNeedsSettle::NoSettleNeeded(v) => v, + } + } + async fn yield_settle_if_needed( + this_ref: &Rc>, + module_index: usize, + v: MaybeNeedsSettle, + ) -> O + where + for<'a> F: MaybeNeedsSettleFn<&'a mut interpreter::State, Output = O>, + { + match v { + MaybeNeedsSettle::NeedsSettle(v) => { + Self::yield_advance_time_or_settle(this_ref.clone(), module_index, None).await; + v.call(&mut this_ref.borrow_mut().state) + } + MaybeNeedsSettle::NoSettleNeeded(v) => v, + } + } fn close_all_trace_writers(&mut self) -> std::io::Result<()> { let trace_writers = mem::take(&mut self.trace_writers); let mut retval = Ok(()); @@ -7146,17 +2508,17 @@ impl SimulationImpl { self.trace_writers = trace_writers; retval } - fn close(mut self) -> std::io::Result<()> { - if self.made_initial_step { - self.settle(); + fn close(this: Rc>) -> std::io::Result<()> { + if this.borrow().main_module.did_initial_settle { + Self::settle(&this); } - self.close_all_trace_writers() + this.borrow_mut().close_all_trace_writers() } - fn flush_traces(&mut self) -> std::io::Result<()> { - if self.made_initial_step { - self.settle(); + fn flush_traces(this_ref: &Rc>) -> std::io::Result<()> { + if this_ref.borrow().main_module.did_initial_settle { + Self::settle(this_ref); } - self.for_each_trace_writer_getting_error( + this_ref.borrow_mut().for_each_trace_writer_getting_error( |this, trace_writer: TraceWriterState| match trace_writer { TraceWriterState::Decls(v) => { let mut v = v.write_decls( @@ -7190,7 +2552,7 @@ impl Drop for SimulationImpl { } pub struct Simulation { - sim_impl: SimulationImpl, + sim_impl: Rc>, io: Expr, } @@ -7212,9 +2574,9 @@ impl fmt::Debug for SortedSetDebug<'_, T, V> { } } -struct SortedMapDebug<'a, K, V>(&'a HashMap); +struct SortedMapDebug<'a, K: 'static + Send + Sync, V>(&'a BTreeMap, V>); -impl fmt::Debug for SortedMapDebug<'_, K, V> { +impl fmt::Debug for SortedMapDebug<'_, K, V> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let mut entries = Vec::from_iter(self.0.iter().map(|(k, v)| { if f.alternate() { @@ -7234,27 +2596,135 @@ impl fmt::Debug for SortedMapDebug<'_, K, V> { } } +struct SliceAsMapDebug<'a, T>(&'a [Option]); + +impl fmt::Debug for SliceAsMapDebug<'_, T> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_map() + .entries( + self.0 + .iter() + .enumerate() + .filter_map(|(k, v)| Some((k, v.as_ref()?))), + ) + .finish() + } +} + impl fmt::Debug for Simulation { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let Self { sim_impl, io } = self; - sim_impl.debug_fmt(Some(io), f) + sim_impl.borrow().debug_fmt(Some(io), f) } } +macro_rules! impl_simulation_methods { + ( + async_await = ($($async:tt, $await:tt)?), + track_caller = ($(#[$track_caller:tt])?), + which_module = |$self:ident| $which_module:expr, + ) => { + $(#[$track_caller])? + pub $($async)? fn read_bool_or_int(&mut $self, io: Expr) -> I::Value { + let retval = $self + .sim_impl + .borrow_mut() + .read_bool_or_int(io, $which_module); + $self.settle_if_needed(retval)$(.$await)? + } + $(#[$track_caller])? + pub $($async)? fn write_bool_or_int( + &mut $self, + io: Expr, + value: impl ToExpr, + ) { + let value = value.to_expr(); + assert_eq!(Expr::ty(io), Expr::ty(value), "type mismatch"); + let value = value + .to_literal_bits() + .expect("the value that is being written to an input must be a literal"); + $self.sim_impl.borrow_mut().write_bool_or_int( + io, + I::bits_to_value(Cow::Borrowed(&value)), + $which_module, + ); + } + $(#[$track_caller])? + pub $($async)? fn write_clock(&mut $self, io: Expr, value: bool) { + $self.sim_impl + .borrow_mut() + .write_bit(Expr::canonical(io), value, $which_module); + } + $(#[$track_caller])? + pub $($async)? fn read_clock(&mut $self, io: Expr) -> bool { + let retval = $self + .sim_impl + .borrow_mut() + .read_bit(Expr::canonical(io), $which_module); + $self.settle_if_needed(retval)$(.$await)? + } + $(#[$track_caller])? + pub $($async)? fn write_bool(&mut $self, io: Expr, value: bool) { + $self.sim_impl + .borrow_mut() + .write_bit(Expr::canonical(io), value, $which_module); + } + $(#[$track_caller])? + pub $($async)? fn read_bool(&mut $self, io: Expr) -> bool { + let retval = $self + .sim_impl + .borrow_mut() + .read_bit(Expr::canonical(io), $which_module); + $self.settle_if_needed(retval)$(.$await)? + } + $(#[$track_caller])? + pub $($async)? fn write_reset(&mut $self, io: Expr, value: bool) { + $self.sim_impl + .borrow_mut() + .write_bit(Expr::canonical(io), value, $which_module); + } + $(#[$track_caller])? + pub $($async)? fn read_reset(&mut $self, io: Expr) -> bool { + let retval = $self + .sim_impl + .borrow_mut() + .read_bit(Expr::canonical(io), $which_module); + $self.settle_if_needed(retval)$(.$await)? + } + $(#[$track_caller])? + pub $($async)? fn read(&mut $self, io: Expr) -> SimValue { + let retval = $self + .sim_impl + .borrow_mut() + .read(Expr::canonical(io), $which_module).1; + SimValue::from_canonical($self.settle_if_needed(retval)$(.$await)?) + } + $(#[$track_caller])? + pub $($async)? fn write>(&mut $self, io: Expr, value: V) { + $self.sim_impl.borrow_mut().write( + Expr::canonical(io), + &SimValue::into_canonical(value.into_sim_value_with_type(Expr::ty(io))), + $which_module, + ); + } + }; +} + impl Simulation { pub fn new(module: Interned>) -> Self { Self::from_compiled(Compiled::new(module)) } pub fn add_trace_writer(&mut self, writer: W) { self.sim_impl + .borrow_mut() .trace_writers .push(TraceWriterState::Decls(DynTraceWriterDecls::new(writer))); } pub fn flush_traces(&mut self) -> std::io::Result<()> { - self.sim_impl.flush_traces() + SimulationImpl::flush_traces(&self.sim_impl) } pub fn close(self) -> std::io::Result<()> { - self.sim_impl.close() + SimulationImpl::close(self.sim_impl) } pub fn canonical(self) -> Simulation { let Self { sim_impl, io } = self; @@ -7277,77 +2747,290 @@ impl Simulation { let sim_impl = SimulationImpl::new(compiled.canonical()); Self { io: Expr::from_bundle(sim_impl.io), - sim_impl, + sim_impl: Rc::new(RefCell::new(sim_impl)), } } #[track_caller] pub fn settle(&mut self) { - self.sim_impl.settle(); + SimulationImpl::settle(&self.sim_impl); } #[track_caller] pub fn advance_time(&mut self, duration: SimDuration) { - self.sim_impl.advance_time(duration); + SimulationImpl::advance_time(&self.sim_impl, duration); } #[track_caller] - pub fn read_bool_or_int(&mut self, io: Expr) -> I::Value { - self.sim_impl.read_bool_or_int(io) - } - #[track_caller] - pub fn write_bool_or_int( - &mut self, - io: Expr, - value: impl ToExpr, - ) { - let value = value.to_expr(); - assert_eq!(Expr::ty(io), Expr::ty(value), "type mismatch"); - let value = value - .to_literal_bits() - .expect("the value that is being written to an input must be a literal"); - self.sim_impl - .write_bool_or_int(io, I::bits_to_value(Cow::Borrowed(&value))); - } - #[track_caller] - pub fn write_clock(&mut self, io: Expr, value: bool) { - self.sim_impl.write_bit(Expr::canonical(io), value); - } - #[track_caller] - pub fn read_clock(&mut self, io: Expr) -> bool { - self.sim_impl.read_bit(Expr::canonical(io)) - } - #[track_caller] - pub fn write_bool(&mut self, io: Expr, value: bool) { - self.sim_impl.write_bit(Expr::canonical(io), value); - } - #[track_caller] - pub fn read_bool(&mut self, io: Expr) -> bool { - self.sim_impl.read_bit(Expr::canonical(io)) - } - #[track_caller] - pub fn write_reset(&mut self, io: Expr, value: bool) { - self.sim_impl.write_bit(Expr::canonical(io), value); - } - #[track_caller] - pub fn read_reset(&mut self, io: Expr) -> bool { - self.sim_impl.read_bit(Expr::canonical(io)) - } - #[track_caller] - pub fn read(&mut self, io: Expr) -> SimValue { - SimValue::from_canonical(self.sim_impl.read(Expr::canonical(io))) - } - #[track_caller] - pub fn write>(&mut self, io: Expr, value: V) { - self.sim_impl.write( - Expr::canonical(io), - value.into_sim_value(Expr::ty(io)).into_canonical(), - ); + fn settle_if_needed(&mut self, v: MaybeNeedsSettle) -> O + where + for<'a> F: MaybeNeedsSettleFn<&'a mut interpreter::State, Output = O>, + { + SimulationImpl::settle_if_needed(&self.sim_impl, v) } + impl_simulation_methods!( + async_await = (), + track_caller = (#[track_caller]), + which_module = |self| WhichModule::Main, + ); #[doc(hidden)] /// This is explicitly unstable and may be changed/removed at any time pub fn set_breakpoints_unstable(&mut self, pcs: HashSet, trace: bool) { - self.sim_impl.breakpoints = Some(BreakpointsSet { + self.sim_impl.borrow_mut().breakpoints = Some(BreakpointsSet { last_was_break: false, set: pcs, trace, }); } } + +pub struct ExternModuleSimulationState { + sim_impl: Rc>, + module_index: usize, + wait_for_changes_wait_targets: EarliestWaitTargets, +} + +impl fmt::Debug for ExternModuleSimulationState { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let Self { + sim_impl: _, + module_index, + wait_for_changes_wait_targets: _, + } = self; + f.debug_struct("ExternModuleSimulationState") + .field("sim_impl", &DebugAsDisplay("...")) + .field("module_index", module_index) + .finish_non_exhaustive() + } +} + +impl ExternModuleSimulationState { + pub async fn settle(&mut self) { + SimulationImpl::yield_advance_time_or_settle(self.sim_impl.clone(), self.module_index, None) + .await + } + pub async fn advance_time(&mut self, duration: SimDuration) { + SimulationImpl::yield_advance_time_or_settle( + self.sim_impl.clone(), + self.module_index, + Some(duration), + ) + .await + } + pub async fn wait_for_changes>( + &mut self, + iter: I, + timeout: Option, + ) { + self.wait_for_changes_wait_targets.clear(); + let which_module = WhichModule::Extern { + module_index: self.module_index, + }; + for io in iter { + let io = Expr::canonical(io.to_expr()); + let (key, value) = self.sim_impl.borrow_mut().read(io, which_module); + let value = self.settle_if_needed(value).await; + self.wait_for_changes_wait_targets + .insert(WaitTarget::Change { key, value }); + } + if let Some(timeout) = timeout { + self.wait_for_changes_wait_targets.instant = + Some(self.sim_impl.borrow().instant + timeout); + } + SimulationImpl::yield_wait( + self.sim_impl.clone(), + self.module_index, + &mut self.wait_for_changes_wait_targets, + ) + .await; + } + pub async fn wait_for_clock_edge(&mut self, clk: impl ToExpr) { + let clk = clk.to_expr(); + while self.read_clock(clk).await { + self.wait_for_changes([clk], None).await; + } + while !self.read_clock(clk).await { + self.wait_for_changes([clk], None).await; + } + } + async fn settle_if_needed(&mut self, v: MaybeNeedsSettle) -> O + where + for<'a> F: MaybeNeedsSettleFn<&'a mut interpreter::State, Output = O>, + { + SimulationImpl::yield_settle_if_needed(&self.sim_impl, self.module_index, v).await + } + impl_simulation_methods!( + async_await = (async, await), + track_caller = (), + which_module = |self| WhichModule::Extern { module_index: self.module_index }, + ); +} + +pub trait ExternModuleSimGenerator: Clone + Eq + Hash + Any + Send + Sync + fmt::Debug { + fn run<'a>(&'a self, sim: ExternModuleSimulationState) -> impl IntoFuture + 'a; +} + +pub struct SimGeneratorFn { + pub args: Args, + pub f: fn(Args, ExternModuleSimulationState) -> Fut, +} + +impl fmt::Debug for SimGeneratorFn { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let Self { args, f: _ } = self; + f.debug_struct("SimGeneratorFn") + .field("args", args) + .field("f", &DebugAsDisplay("...")) + .finish() + } +} + +impl Hash for SimGeneratorFn { + fn hash(&self, state: &mut H) { + let Self { args, f } = self; + args.hash(state); + f.hash(state); + } +} + +impl Eq for SimGeneratorFn {} + +impl PartialEq for SimGeneratorFn { + fn eq(&self, other: &Self) -> bool { + let Self { args, f } = self; + *args == other.args && ptr::fn_addr_eq(*f, other.f) + } +} + +impl Clone for SimGeneratorFn { + fn clone(&self) -> Self { + Self { + args: self.args.clone(), + f: self.f, + } + } +} + +impl Copy for SimGeneratorFn {} + +impl< + T: fmt::Debug + Clone + Eq + Hash + Send + Sync + 'static, + Fut: IntoFuture + 'static, +> ExternModuleSimGenerator for SimGeneratorFn +{ + fn run<'a>(&'a self, sim: ExternModuleSimulationState) -> impl IntoFuture + 'a { + (self.f)(self.args.clone(), sim) + } +} + +pub(crate) trait DynExternModuleSimGenerator: + Any + Send + Sync + SupportsPtrEqWithTypeId + fmt::Debug +{ + fn dyn_run<'a>(&'a self, sim: ExternModuleSimulationState) + -> Box + 'a>; +} + +impl DynExternModuleSimGenerator for T { + fn dyn_run<'a>( + &'a self, + sim: ExternModuleSimulationState, + ) -> Box + 'a> { + Box::new(self.run(sim).into_future()) + } +} + +impl InternedCompare for dyn DynExternModuleSimGenerator { + type InternedCompareKey = PtrEqWithTypeId; + + fn interned_compare_key_ref(this: &Self) -> Self::InternedCompareKey { + this.get_ptr_eq_with_type_id() + } +} + +#[derive(Clone, Copy, PartialEq, Eq, Hash)] +pub struct ExternModuleSimulation { + pub(crate) generator: Interned, + /// Map of [`ModuleIO`]s for the containing module to the [`ModuleIO`]s that the generator will use. + /// Used when transforming the containing module's [`ModuleIO`]s to different types, e.g. with + /// [`deduce_resets`][crate::module::transform::deduce_resets::deduce_resets]. + /// + /// only the keys (sim I/O) are [visited][Visit]/[folded][Fold]. + pub sim_io_to_generator_map: + Interned>, Interned>>>, + pub source_location: SourceLocation, +} + +impl fmt::Debug for ExternModuleSimulation { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("ExternModuleSimulation") + .field("generator", &self.generator) + .field( + "sim_io_to_generator_map", + &SortedMapDebug(&self.sim_io_to_generator_map), + ) + .field("source_location", &self.source_location) + .finish() + } +} + +impl ExternModuleSimulation { + pub fn new_with_loc( + source_location: SourceLocation, + generator: G, + ) -> Self { + Self { + generator: Interned::cast_unchecked( + generator.intern(), + |v| -> &dyn DynExternModuleSimGenerator { v }, + ), + sim_io_to_generator_map: Interned::default(), + source_location, + } + } + #[track_caller] + pub fn new(generator: G) -> Self { + Self::new_with_loc(SourceLocation::caller(), generator) + } + fn run(&self, sim: ExternModuleSimulationState) -> Box + 'static> { + Interned::into_inner(self.generator).dyn_run(sim) + } +} + +impl Visit for ExternModuleSimulation { + fn visit(&self, state: &mut State) -> Result<(), State::Error> { + state.visit_extern_module_simulation(self) + } + + fn default_visit(&self, state: &mut State) -> Result<(), State::Error> { + let Self { + generator: _, + sim_io_to_generator_map, + source_location, + } = self; + sim_io_to_generator_map + .keys() + .try_for_each(|k| k.visit(state))?; + source_location.visit(state) + } +} + +impl Fold for ExternModuleSimulation { + fn fold(self, state: &mut State) -> Result::Error> { + state.fold_extern_module_simulation(self) + } + + fn default_fold(self, state: &mut State) -> Result::Error> { + let Self { + generator, + sim_io_to_generator_map, + source_location, + } = self; + Ok(Self { + generator, + sim_io_to_generator_map: Result::, _>::from_iter( + sim_io_to_generator_map + .iter() + .map(|(&sim_io, generator_io)| Ok((sim_io.fold(state)?, *generator_io))), + )? + .intern_sized(), + source_location: source_location.fold(state)?, + }) + } +} diff --git a/crates/fayalite/src/sim/compiler.rs b/crates/fayalite/src/sim/compiler.rs new file mode 100644 index 0000000..2b291be --- /dev/null +++ b/crates/fayalite/src/sim/compiler.rs @@ -0,0 +1,5161 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// See Notices.txt for copyright information + +//! Compiler to Interpreter IR for Fayalite Simulation + +use crate::{ + bundle::{BundleField, BundleType}, + enum_::{EnumType, EnumVariant}, + expr::{ + ExprEnum, Flow, ops, + target::{ + GetTarget, Target, TargetBase, TargetPathArrayElement, TargetPathBundleField, + TargetPathElement, + }, + }, + int::BoolOrIntType, + intern::{Intern, Interned, Memoize}, + memory::PortKind, + module::{ + AnnotatedModuleIO, Block, ExternModuleBody, Id, InstantiatedModule, ModuleBody, NameId, + NormalModuleBody, ScopedNameId, Stmt, StmtConnect, StmtDeclaration, StmtFormal, StmtIf, + StmtInstance, StmtMatch, StmtReg, StmtWire, TargetInInstantiatedModule, + transform::deduce_resets::deduce_resets, + }, + prelude::*, + reset::{ResetType, ResetTypeDispatch}, + sim::{ + ExternModuleSimulation, SimTrace, SimTraceKind, SimTraces, TraceArray, TraceAsyncReset, + TraceBool, TraceBundle, TraceClock, TraceDecl, TraceEnumDiscriminant, TraceEnumWithFields, + TraceFieldlessEnum, TraceInstance, TraceLocation, TraceMem, TraceMemPort, TraceMemoryId, + TraceMemoryLocation, TraceModule, TraceModuleIO, TraceReg, TraceSInt, TraceScalarId, + TraceScope, TraceSimOnly, TraceSyncReset, TraceUInt, TraceWire, + interpreter::{ + Insn, InsnField, InsnFieldKind, InsnFieldType, InsnOrLabel, Insns, InsnsBuilding, + InsnsBuildingDone, InsnsBuildingKind, Label, SmallUInt, StatePartArrayIndex, + StatePartArrayIndexed, + parts::{ + MemoryData, SlotDebugData, StatePartIndex, StatePartIndexRange, StatePartKind, + StatePartKindBigSlots, StatePartKindMemories, StatePartKindSimOnlySlots, + StatePartKindSmallSlots, StatePartLayout, StatePartLen, TypeIndex, TypeIndexRange, + TypeLayout, TypeLen, TypeLenSingle, get_state_part_kinds, + }, + }, + }, + ty::{OpaqueSimValueSize, StaticType}, + util::{HashMap, chain}, +}; +use bitvec::vec::BitVec; +use num_bigint::BigInt; +use petgraph::{ + data::FromElements, + visit::{ + EdgeRef, GraphBase, IntoEdgeReferences, IntoNeighbors, IntoNeighborsDirected, + IntoNodeIdentifiers, IntoNodeReferences, NodeRef, VisitMap, Visitable, + }, +}; +use std::{collections::BTreeSet, fmt, hash::Hash, mem}; + +#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)] +enum CondBody { + IfTrue { + cond: CompiledValue, + }, + IfFalse { + cond: CompiledValue, + }, + MatchArm { + discriminant: StatePartIndex, + variant_index: usize, + }, +} + +#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)] +struct Cond { + body: CondBody, + source_location: SourceLocation, +} + +#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)] +pub(crate) struct CompiledBundleField { + pub(crate) offset: TypeIndex, + pub(crate) ty: CompiledTypeLayout, +} + +#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)] +pub(crate) enum CompiledTypeLayoutBody { + Scalar, + Array { + /// debug names are ignored, use parent's layout instead + element: Interned>, + }, + Bundle { + /// debug names are ignored, use parent's layout instead + fields: Interned<[CompiledBundleField]>, + }, +} + +#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)] +pub(crate) struct CompiledTypeLayout { + pub(crate) ty: T, + pub(crate) layout: TypeLayout, + pub(crate) body: CompiledTypeLayoutBody, +} + +impl CompiledTypeLayout { + fn with_prefixed_debug_names(self, prefix: &str) -> Self { + let Self { ty, layout, body } = self; + Self { + ty, + layout: layout.with_prefixed_debug_names(prefix), + body, + } + } + fn with_anonymized_debug_info(self) -> Self { + let Self { ty, layout, body } = self; + Self { + ty, + layout: layout.with_anonymized_debug_info(), + body, + } + } + fn get(ty: T) -> Self { + #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)] + struct MyMemoize; + impl Memoize for MyMemoize { + type Input = CanonicalType; + type InputOwned = CanonicalType; + type Output = CompiledTypeLayout; + + fn inner(self, input: &Self::Input) -> Self::Output { + match input { + CanonicalType::UInt(_) + | CanonicalType::SInt(_) + | CanonicalType::Bool(_) + | CanonicalType::Enum(_) + | CanonicalType::AsyncReset(_) + | CanonicalType::SyncReset(_) + | CanonicalType::Reset(_) + | CanonicalType::Clock(_) => { + let mut layout = TypeLayout::empty(); + let debug_data = SlotDebugData { + name: Interned::default(), + ty: *input, + }; + layout.big_slots = StatePartLayout::scalar(debug_data, ()); + CompiledTypeLayout { + ty: *input, + layout: layout.into(), + body: CompiledTypeLayoutBody::Scalar, + } + } + CanonicalType::Array(array) => { + let mut layout = TypeLayout::empty(); + let element = CompiledTypeLayout::get(array.element()).intern_sized(); + for index in 0..array.len() { + layout.allocate( + &element + .layout + .with_prefixed_debug_names(&format!("[{index}]")), + ); + } + CompiledTypeLayout { + ty: *input, + layout: layout.into(), + body: CompiledTypeLayoutBody::Array { element }, + } + } + CanonicalType::PhantomConst(_) => { + let unit_layout = CompiledTypeLayout::get(()); + CompiledTypeLayout { + ty: *input, + layout: unit_layout.layout, + body: unit_layout.body, + } + } + CanonicalType::Bundle(bundle) => { + let mut layout = TypeLayout::empty(); + let fields = bundle + .fields() + .iter() + .map( + |BundleField { + name, + flipped: _, + ty, + }| { + let ty = CompiledTypeLayout::get(*ty); + let offset = layout + .allocate( + &ty.layout + .with_prefixed_debug_names(&format!(".{name}")), + ) + .start(); + CompiledBundleField { offset, ty } + }, + ) + .collect(); + CompiledTypeLayout { + ty: *input, + layout: layout.into(), + body: CompiledTypeLayoutBody::Bundle { fields }, + } + } + CanonicalType::DynSimOnly(ty) => { + let mut layout = TypeLayout::empty(); + let debug_data = SlotDebugData { + name: Interned::default(), + ty: *input, + }; + layout.sim_only_slots = StatePartLayout::scalar(debug_data, *ty); + CompiledTypeLayout { + ty: *input, + layout: layout.into(), + body: CompiledTypeLayoutBody::Scalar, + } + } + } + } + } + let CompiledTypeLayout { + ty: _, + layout, + body, + } = MyMemoize.get_owned(ty.canonical()); + Self { ty, layout, body } + } +} + +#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)] +pub(crate) struct CompiledValue { + pub(crate) layout: CompiledTypeLayout, + pub(crate) range: TypeIndexRange, + pub(crate) write: Option<(CompiledTypeLayout, TypeIndexRange)>, +} + +impl CompiledValue { + fn write(self) -> (CompiledTypeLayout, TypeIndexRange) { + self.write.unwrap_or((self.layout, self.range)) + } + fn write_value(self) -> Self { + let (layout, range) = self.write(); + Self { + layout, + range, + write: None, + } + } + fn map( + self, + mut f: impl FnMut( + CompiledTypeLayout, + TypeIndexRange, + ) -> (CompiledTypeLayout, TypeIndexRange), + ) -> CompiledValue { + let (layout, range) = f(self.layout, self.range); + CompiledValue { + layout, + range, + write: self.write.map(|(layout, range)| f(layout, range)), + } + } + pub(crate) fn map_ty(self, mut f: impl FnMut(T) -> U) -> CompiledValue { + self.map(|CompiledTypeLayout { ty, layout, body }, range| { + ( + CompiledTypeLayout { + ty: f(ty), + layout, + body, + }, + range, + ) + }) + } +} + +impl CompiledValue { + fn field_by_index(self, field_index: usize) -> CompiledValue { + self.map(|layout, range| { + let CompiledTypeLayout { + ty: _, + layout: _, + body: CompiledTypeLayoutBody::Bundle { fields }, + } = layout + else { + unreachable!(); + }; + ( + fields[field_index].ty, + range.slice(TypeIndexRange::new( + fields[field_index].offset, + fields[field_index].ty.layout.len(), + )), + ) + }) + } + pub(crate) fn field_by_name(self, name: Interned) -> CompiledValue { + self.field_by_index(self.layout.ty.name_indexes()[&name]) + } +} + +impl CompiledValue { + pub(crate) fn element(self, index: usize) -> CompiledValue { + self.map(|layout, range| { + let CompiledTypeLayoutBody::Array { element } = layout.body else { + unreachable!(); + }; + (*element, range.index_array(element.layout.len(), index)) + }) + } + fn element_dyn( + self, + index_slot: StatePartIndex, + ) -> CompiledExpr { + CompiledExpr::from(self).element_dyn(index_slot) + } +} + +macro_rules! make_type_array_indexes { + ( + type_plural_fields = [$($type_plural_field:ident,)*]; + type_kinds = [$($type_kind:ident,)*]; + ) => { + #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, Default)] + pub(crate) struct TypeArrayIndexes { + $(pub(crate) $type_plural_field: Interned<[StatePartArrayIndex<$type_kind>]>,)* + } + + impl TypeArrayIndexes { + pub(crate) fn as_ref(&self) -> TypeArrayIndexesRef<'_> { + TypeArrayIndexesRef { + $($type_plural_field: &self.$type_plural_field,)* + } + } + #[must_use] + pub(crate) fn join(self, next: TypeArrayIndex) -> TypeArrayIndexes { + TypeArrayIndexes { + $($type_plural_field: Interned::from_iter(self.$type_plural_field.iter().copied().chain([next.$type_plural_field])),)* + } + } + } + + #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] + pub(crate) struct TypeArrayIndex { + $(pub(crate) $type_plural_field: StatePartArrayIndex<$type_kind>,)* + } + + impl TypeArrayIndex { + pub(crate) fn from_parts(index: StatePartIndex, len: usize, stride: TypeLen) -> Self { + Self { + $($type_plural_field: StatePartArrayIndex { + index, + len, + stride: stride.$type_plural_field, + },)* + } + } + pub(crate) fn len(self) -> usize { + let len = self.small_slots.len; + $(assert_eq!(self.$type_plural_field.len, len, "array length mismatch");)* + len + } + pub(crate) fn index(self) -> StatePartIndex { + let index = self.small_slots.index; + $(assert_eq!(self.$type_plural_field.index, index, "array index mismatch");)* + index + } + pub(crate) fn is_empty(self) -> bool { + self.len() == 0 + } + pub(crate) fn stride(self) -> TypeLen { + TypeLen { + $($type_plural_field: self.$type_plural_field.stride,)* + } + } + } + + #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, Default)] + pub(crate) struct TypeArrayIndexesRef<'a> { + $(pub(crate) $type_plural_field: &'a [StatePartArrayIndex<$type_kind>],)* + } + + impl<'a> TypeArrayIndexesRef<'a> { + pub(crate) fn len(self) -> usize { + let len = self.small_slots.len(); + $(assert_eq!(self.$type_plural_field.len(), len, "indexes count mismatch");)* + len + } + pub(crate) fn is_empty(self) -> bool { + self.len() == 0 + } + pub(crate) fn iter(self) -> impl Iterator + 'a { + (0..self.len()).map(move |i| TypeArrayIndex { + $($type_plural_field: self.$type_plural_field[i],)* + }) + } + pub(crate) fn for_each_offset( + self, + mut f: impl FnMut(TypeIndex), + ) { + self.for_each_offset2(TypeIndex { + $($type_plural_field: StatePartIndex::new(0),)* + }, &mut f); + } + pub(crate) fn split_first(self) -> Option<(TypeArrayIndex, Self)> { + $(let $type_plural_field = self.$type_plural_field.split_first()?;)* + let next = TypeArrayIndex { + $($type_plural_field: *$type_plural_field.0,)* + }; + let rest = TypeArrayIndexesRef { + $($type_plural_field: $type_plural_field.1,)* + }; + Some((next, rest)) + } + pub(crate) fn for_each_offset2( + self, + base_offset: TypeIndex, + f: &mut (impl FnMut(TypeIndex) + ?Sized), + ) { + if let Some((next, rest)) = self.split_first() { + let stride = next.stride(); + for index in 0..next.len().try_into().expect("array too big") { + let mut offset = TypeIndex { + $($type_plural_field: StatePartIndex::new( + stride + .$type_plural_field + .value + .checked_mul(index) + .expect("array too big"), + ),)* + }; + $(offset.$type_plural_field.value = + base_offset + .$type_plural_field + .value + .checked_add(offset.$type_plural_field.value) + .expect("array too big");)* + rest.for_each_offset2(offset, f); + } + } else { + $(assert!(self.$type_plural_field.is_empty(), "indexes count mismatch");)* + f(base_offset); + } + } + } + + #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] + pub(crate) struct TypeArrayIndexed { + $(pub(crate) $type_plural_field: StatePartArrayIndexed<$type_kind>,)* + } + + impl TypeArrayIndexed { + pub(crate) fn from_parts(base: TypeIndex, indexes: TypeArrayIndexes) -> Self { + Self { + $($type_plural_field: StatePartArrayIndexed { + base: base.$type_plural_field, + indexes: indexes.$type_plural_field, + },)* + } + } + pub(crate) fn base(self) -> TypeIndex { + TypeIndex { + $($type_plural_field: self.$type_plural_field.base,)* + } + } + pub(crate) fn indexes(self) -> TypeArrayIndexes { + TypeArrayIndexes { + $($type_plural_field: self.$type_plural_field.indexes,)* + } + } + } + + impl From for TypeArrayIndexed { + fn from(value: TypeIndex) -> Self { + TypeArrayIndexed { + $($type_plural_field: value.$type_plural_field.into(),)* + } + } + } + }; +} + +get_state_part_kinds! { + make_type_array_indexes! { + type_plural_fields; + type_kinds; + } +} + +#[derive(Debug, PartialEq, Eq, Hash, Copy, Clone)] +struct CompiledExpr { + static_part: CompiledValue, + indexes: TypeArrayIndexes, +} + +impl From> for CompiledExpr { + fn from(static_part: CompiledValue) -> Self { + Self { + static_part, + indexes: TypeArrayIndexes::default(), + } + } +} + +impl CompiledExpr { + fn map_ty(self, f: impl FnMut(T) -> U) -> CompiledExpr { + let Self { + static_part, + indexes, + } = self; + CompiledExpr { + static_part: static_part.map_ty(f), + indexes, + } + } + fn add_target_without_indexes_to_set(self, inputs: &mut SlotSet) { + let Self { + static_part, + indexes, + } = self; + indexes.as_ref().for_each_offset(|offset| { + inputs.extend([static_part.range.offset(offset)]); + }); + } + fn add_target_and_indexes_to_set(self, inputs: &mut SlotSet) { + let Self { + static_part: _, + indexes, + } = self; + self.add_target_without_indexes_to_set(inputs); + inputs.extend(indexes.as_ref().iter()); + } +} + +impl CompiledExpr { + fn field_by_index(self, field_index: usize) -> CompiledExpr { + CompiledExpr { + static_part: self.static_part.field_by_index(field_index), + indexes: self.indexes, + } + } + fn field_by_name(self, name: Interned) -> CompiledExpr { + CompiledExpr { + static_part: self.static_part.field_by_name(name), + indexes: self.indexes, + } + } +} + +impl CompiledExpr { + fn element(self, index: usize) -> CompiledExpr { + CompiledExpr { + static_part: self.static_part.element(index), + indexes: self.indexes, + } + } + fn element_dyn( + self, + index_slot: StatePartIndex, + ) -> CompiledExpr { + let CompiledTypeLayoutBody::Array { element } = self.static_part.layout.body else { + unreachable!(); + }; + let stride = element.layout.len(); + let indexes = self.indexes.join(TypeArrayIndex::from_parts( + index_slot, + self.static_part.layout.ty.len(), + stride, + )); + CompiledExpr { + static_part: self.static_part.map(|layout, range| { + let CompiledTypeLayoutBody::Array { element } = layout.body else { + unreachable!(); + }; + (*element, range.index_array(stride, 0)) + }), + indexes, + } + } +} + +macro_rules! make_assignment_graph { + ( + type_plural_fields = [$($type_plural_field:ident,)*]; + type_singular_variants = [$($type_singular_variant:ident,)*]; + type_kinds = [$($type_kind:ident,)*]; + array_indexed_variants = [$($array_indexed_variant:ident,)*]; + input_variants = [$($input_variant:ident,)*]; + output_variants = [$($output_variant:ident,)*]; + ) => { + #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] + enum AssignmentOrSlotIndex { + AssignmentIndex(usize), + $($type_singular_variant(StatePartIndex<$type_kind>),)* + } + + #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] + enum AssignmentIO { + $($input_variant { + assignment_index: usize, + slot: StatePartIndex<$type_kind>, + },)* + $($output_variant { + assignment_index: usize, + slot: StatePartIndex<$type_kind>, + },)* + } + + #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] + enum AssignmentsEdge { + IO(AssignmentIO), + AssignmentImmediatePredecessor { + predecessor_assignment_index: usize, + assignment_index: usize, + }, + } + + #[derive(Debug)] + enum Assignments { + Accumulating { + assignments: Vec, + }, + Finalized { + assignments: Box<[Assignment]>, + slots_layout: TypeLayout, + slot_readers: SlotToAssignmentIndexFullMap, + slot_writers: SlotToAssignmentIndexFullMap, + assignment_immediate_predecessors: Box<[Box<[usize]>]>, + assignment_immediate_successors: Box<[Box<[usize]>]>, + }, + } + + impl Default for Assignments { + fn default() -> Self { + Self::Accumulating { + assignments: Vec::new(), + } + } + } + + impl Assignments { + fn finalize(&mut self, slots_layout: TypeLayout) { + let Self::Accumulating { assignments } = self else { + unreachable!("already finalized"); + }; + let assignments = mem::take(assignments).into_boxed_slice(); + let mut slot_readers = SlotToAssignmentIndexFullMap::new(slots_layout.len()); + let mut slot_writers = SlotToAssignmentIndexFullMap::new(slots_layout.len()); + let mut assignment_immediate_predecessors = vec![BTreeSet::new(); assignments.len()]; + let mut assignment_immediate_successors = vec![BTreeSet::new(); assignments.len()]; + for (assignment_index, assignment) in assignments.iter().enumerate() { + slot_readers + .keys_for_assignment(assignment_index) + .extend([&assignment.inputs]); + slot_readers + .keys_for_assignment(assignment_index) + .extend(&assignment.conditions); + $(for &slot in &assignment.outputs.$type_plural_field { + if let Some(&pred) = slot_writers[slot].last() { + assignment_immediate_predecessors[assignment_index].insert(pred); + assignment_immediate_successors[pred].insert(assignment_index); + } + slot_writers[slot].push(assignment_index); + })* + } + *self = Self::Finalized { + assignments, + slots_layout, + slot_readers, + slot_writers, + assignment_immediate_predecessors: assignment_immediate_predecessors + .into_iter() + .map(Box::from_iter) + .collect(), + assignment_immediate_successors: assignment_immediate_successors + .into_iter() + .map(Box::from_iter) + .collect(), + }; + } + fn push(&mut self, v: Assignment) { + let Self::Accumulating { assignments } = self else { + unreachable!("already finalized"); + }; + assignments.push(v); + } + fn assignments(&self) -> &[Assignment] { + let Self::Finalized { assignments, .. } = self else { + unreachable!("Assignments::finalize should have been called"); + }; + assignments + } + fn slots_layout(&self) -> TypeLayout { + let Self::Finalized { slots_layout, .. } = self else { + unreachable!("Assignments::finalize should have been called"); + }; + *slots_layout + } + fn slot_readers(&self) -> &SlotToAssignmentIndexFullMap { + let Self::Finalized { slot_readers, .. } = self else { + unreachable!("Assignments::finalize should have been called"); + }; + slot_readers + } + fn slot_writers(&self) -> &SlotToAssignmentIndexFullMap { + let Self::Finalized { slot_writers, .. } = self else { + unreachable!("Assignments::finalize should have been called"); + }; + slot_writers + } + fn assignment_immediate_predecessors(&self) -> &[Box<[usize]>] { + let Self::Finalized { + assignment_immediate_predecessors, + .. + } = self + else { + unreachable!("Assignments::finalize should have been called"); + }; + assignment_immediate_predecessors + } + fn assignment_immediate_successors(&self) -> &[Box<[usize]>] { + let Self::Finalized { + assignment_immediate_successors, + .. + } = self + else { + unreachable!("Assignments::finalize should have been called"); + }; + assignment_immediate_successors + } + fn elements(&self) -> AssignmentsElements<'_> { + let SlotToAssignmentIndexFullMap { + $($type_plural_field,)* + } = self.slot_readers(); + AssignmentsElements { + node_indexes: HashMap::with_capacity_and_hasher( + self.assignments().len() $(+ $type_plural_field.len())*, + Default::default(), + ), + nodes: self.node_references(), + edges: self.edge_references(), + } + } + } + + impl GraphBase for Assignments { + type EdgeId = AssignmentsEdge; + type NodeId = AssignmentOrSlotIndex; + } + + #[derive(Debug, Clone, Copy)] + enum AssignmentsNodeRef<'a> { + Assignment { + index: usize, + #[allow(dead_code, reason = "used in Debug impl")] + assignment: &'a Assignment, + }, + $($type_singular_variant( + StatePartIndex<$type_kind>, + #[allow(dead_code, reason = "used in Debug impl")] SlotDebugData, + ),)* + } + + impl<'a> NodeRef for AssignmentsNodeRef<'a> { + type NodeId = AssignmentOrSlotIndex; + type Weight = AssignmentsNodeRef<'a>; + + fn id(&self) -> Self::NodeId { + match *self { + AssignmentsNodeRef::Assignment { + index, + assignment: _, + } => AssignmentOrSlotIndex::AssignmentIndex(index), + $(AssignmentsNodeRef::$type_singular_variant(slot, _) => AssignmentOrSlotIndex::$type_singular_variant(slot),)* + } + } + + fn weight(&self) -> &Self::Weight { + self + } + } + + impl<'a> petgraph::visit::Data for &'a Assignments { + type NodeWeight = AssignmentsNodeRef<'a>; + type EdgeWeight = AssignmentsEdge; + } + + struct AssignmentsElements<'a> { + node_indexes: HashMap, + nodes: AssignmentsNodes<'a>, + edges: AssignmentsEdges<'a>, + } + + impl<'a> Iterator for AssignmentsElements<'a> { + type Item = petgraph::data::Element< + <&'a Assignments as petgraph::visit::Data>::NodeWeight, + <&'a Assignments as petgraph::visit::Data>::EdgeWeight, + >; + + fn next(&mut self) -> Option { + let Self { + node_indexes, + nodes, + edges, + } = self; + if let Some(node) = nodes.next() { + node_indexes.insert(node.id(), node_indexes.len()); + return Some(petgraph::data::Element::Node { weight: node }); + } + let edge = edges.next()?; + Some(petgraph::data::Element::Edge { + source: node_indexes[&edge.source()], + target: node_indexes[&edge.target()], + weight: *edge.weight(), + }) + } + } + + #[derive(Clone)] + struct AssignmentsNodeIdentifiers { + assignment_indexes: std::ops::Range, + $($type_plural_field: std::ops::Range,)* + } + + impl AssignmentsNodeIdentifiers { + fn internal_iter<'a>(&'a mut self) -> impl Iterator + 'a { + let Self { + assignment_indexes, + $($type_plural_field,)* + } = self; + assignment_indexes + .map(AssignmentOrSlotIndex::AssignmentIndex) + $(.chain($type_plural_field.map(|value| { + AssignmentOrSlotIndex::$type_singular_variant(StatePartIndex::new(value)) + })))* + } + } + + impl Iterator for AssignmentsNodeIdentifiers { + type Item = AssignmentOrSlotIndex; + fn next(&mut self) -> Option { + self.internal_iter().next() + } + + fn nth(&mut self, n: usize) -> Option { + self.internal_iter().nth(n) + } + } + + impl<'a> IntoNodeIdentifiers for &'a Assignments { + type NodeIdentifiers = AssignmentsNodeIdentifiers; + + fn node_identifiers(self) -> Self::NodeIdentifiers { + let TypeLen { + $($type_plural_field,)* + } = self.slot_readers().len(); + AssignmentsNodeIdentifiers { + assignment_indexes: 0..self.assignments().len(), + $($type_plural_field: 0..$type_plural_field.value,)* + } + } + } + + struct AssignmentsNodes<'a> { + assignments: &'a Assignments, + nodes: AssignmentsNodeIdentifiers, + } + + impl<'a> Iterator for AssignmentsNodes<'a> { + type Item = AssignmentsNodeRef<'a>; + + fn next(&mut self) -> Option { + self.nodes.next().map(|node| match node { + AssignmentOrSlotIndex::AssignmentIndex(index) => AssignmentsNodeRef::Assignment { + index, + assignment: &self.assignments.assignments()[index], + }, + $(AssignmentOrSlotIndex::$type_singular_variant(slot) => AssignmentsNodeRef::$type_singular_variant( + slot, + *self.assignments.slots_layout().$type_plural_field.debug_data(slot), + ),)* + }) + } + } + + impl<'a> IntoNodeReferences for &'a Assignments { + type NodeRef = AssignmentsNodeRef<'a>; + type NodeReferences = AssignmentsNodes<'a>; + + fn node_references(self) -> Self::NodeReferences { + AssignmentsNodes { + assignments: self, + nodes: self.node_identifiers(), + } + } + } + + #[derive(Default)] + struct AssignmentsNeighborsDirected<'a> { + assignment_indexes: std::slice::Iter<'a, usize>, + $($type_plural_field: std::collections::btree_set::Iter<'a, StatePartIndex<$type_kind>>,)* + } + + impl Iterator for AssignmentsNeighborsDirected<'_> { + type Item = AssignmentOrSlotIndex; + fn next(&mut self) -> Option { + let Self { + assignment_indexes, + $($type_plural_field,)* + } = self; + if let retval @ Some(_) = assignment_indexes + .next() + .copied() + .map(AssignmentOrSlotIndex::AssignmentIndex) + { + retval + } $(else if let retval @ Some(_) = $type_plural_field + .next() + .copied() + .map(AssignmentOrSlotIndex::$type_singular_variant) + { + retval + })* else { + None + } + } + } + + impl<'a> IntoNeighbors for &'a Assignments { + type Neighbors = AssignmentsNeighborsDirected<'a>; + + fn neighbors(self, n: Self::NodeId) -> Self::Neighbors { + self.neighbors_directed(n, petgraph::Direction::Outgoing) + } + } + + impl<'a> IntoNeighborsDirected for &'a Assignments { + type NeighborsDirected = AssignmentsNeighborsDirected<'a>; + + fn neighbors_directed( + self, + n: Self::NodeId, + d: petgraph::Direction, + ) -> Self::NeighborsDirected { + use petgraph::Direction::*; + let slot_map = match d { + Outgoing => self.slot_readers(), + Incoming => self.slot_writers(), + }; + match n { + AssignmentOrSlotIndex::AssignmentIndex(assignment_index) => { + let assignment = &self.assignments()[assignment_index]; + let ( + assignment_indexes, + SlotSet { + $($type_plural_field,)* + }, + ) = match d { + Outgoing => ( + &self.assignment_immediate_successors()[assignment_index], + &assignment.outputs, + ), + Incoming => ( + &self.assignment_immediate_predecessors()[assignment_index], + &assignment.inputs, + ), + }; + AssignmentsNeighborsDirected { + assignment_indexes: assignment_indexes.iter(), + $($type_plural_field: $type_plural_field.iter(),)* + } + } + $(AssignmentOrSlotIndex::$type_singular_variant(slot) => AssignmentsNeighborsDirected { + assignment_indexes: slot_map[slot].iter(), + ..Default::default() + },)* + } + } + } + + impl EdgeRef for AssignmentsEdge { + type NodeId = AssignmentOrSlotIndex; + type EdgeId = AssignmentsEdge; + type Weight = AssignmentsEdge; + + fn source(&self) -> Self::NodeId { + match *self { + $(AssignmentsEdge::IO(AssignmentIO::$input_variant { + assignment_index: _, + slot, + }) => AssignmentOrSlotIndex::$type_singular_variant(slot),)* + $(AssignmentsEdge::IO(AssignmentIO::$output_variant { + assignment_index, + slot: _, + }) => AssignmentOrSlotIndex::AssignmentIndex(assignment_index),)* + AssignmentsEdge::AssignmentImmediatePredecessor { + predecessor_assignment_index, + assignment_index: _, + } => AssignmentOrSlotIndex::AssignmentIndex(predecessor_assignment_index), + } + } + + fn target(&self) -> Self::NodeId { + match *self { + $(AssignmentsEdge::IO(AssignmentIO::$input_variant { + assignment_index, + slot: _, + }) => AssignmentOrSlotIndex::AssignmentIndex(assignment_index),)* + $(AssignmentsEdge::IO(AssignmentIO::$output_variant { + assignment_index: _, + slot, + }) => AssignmentOrSlotIndex::$type_singular_variant(slot),)* + AssignmentsEdge::AssignmentImmediatePredecessor { + predecessor_assignment_index: _, + assignment_index, + } => AssignmentOrSlotIndex::AssignmentIndex(assignment_index), + } + } + + fn weight(&self) -> &Self::Weight { + self + } + + fn id(&self) -> Self::EdgeId { + *self + } + } + + struct AssignmentsEdges<'a> { + assignments: &'a Assignments, + nodes: AssignmentsNodeIdentifiers, + outgoing_neighbors: Option<(AssignmentOrSlotIndex, AssignmentsNeighborsDirected<'a>)>, + } + + impl Iterator for AssignmentsEdges<'_> { + type Item = AssignmentsEdge; + + fn next(&mut self) -> Option { + loop { + if let Some((node, outgoing_neighbors)) = &mut self.outgoing_neighbors { + if let Some(outgoing_neighbor) = outgoing_neighbors.next() { + return Some(match (*node, outgoing_neighbor) { + ( + $(AssignmentOrSlotIndex::$type_singular_variant(_))|*, + $(AssignmentOrSlotIndex::$type_singular_variant(_))|*, + ) => unreachable!(), + ( + AssignmentOrSlotIndex::AssignmentIndex(predecessor_assignment_index), + AssignmentOrSlotIndex::AssignmentIndex(assignment_index), + ) => AssignmentsEdge::AssignmentImmediatePredecessor { + predecessor_assignment_index, + assignment_index, + }, + $(( + AssignmentOrSlotIndex::AssignmentIndex(assignment_index), + AssignmentOrSlotIndex::$type_singular_variant(slot), + ) => AssignmentsEdge::IO(AssignmentIO::$output_variant { + assignment_index, + slot, + }),)* + $(( + AssignmentOrSlotIndex::$type_singular_variant(slot), + AssignmentOrSlotIndex::AssignmentIndex(assignment_index), + ) => AssignmentsEdge::IO(AssignmentIO::$input_variant { + assignment_index, + slot, + }),)* + }); + } + } + let node = self.nodes.next()?; + self.outgoing_neighbors = Some(( + node, + self.assignments + .neighbors_directed(node, petgraph::Direction::Outgoing), + )); + } + } + } + + impl<'a> IntoEdgeReferences for &'a Assignments { + type EdgeRef = AssignmentsEdge; + type EdgeReferences = AssignmentsEdges<'a>; + + fn edge_references(self) -> Self::EdgeReferences { + AssignmentsEdges { + assignments: self, + nodes: self.node_identifiers(), + outgoing_neighbors: None, + } + } + } + + struct AssignmentsVisitMap { + assignments: Vec, + slots: DenseSlotSet, + } + + impl VisitMap for AssignmentsVisitMap { + fn visit(&mut self, n: AssignmentOrSlotIndex) -> bool { + match n { + AssignmentOrSlotIndex::AssignmentIndex(assignment_index) => { + !mem::replace(&mut self.assignments[assignment_index], true) + } + $(AssignmentOrSlotIndex::$type_singular_variant(slot) => self.slots.insert(slot),)* + } + } + + fn is_visited(&self, n: &AssignmentOrSlotIndex) -> bool { + match *n { + AssignmentOrSlotIndex::AssignmentIndex(assignment_index) => { + self.assignments[assignment_index] + } + $(AssignmentOrSlotIndex::$type_singular_variant(slot) => self.slots.contains(slot),)* + } + } + + fn unvisit(&mut self, n: AssignmentOrSlotIndex) -> bool { + match n { + AssignmentOrSlotIndex::AssignmentIndex(assignment_index) => { + mem::replace(&mut self.assignments[assignment_index], false) + } + $(AssignmentOrSlotIndex::$type_singular_variant(slot) => self.slots.remove(slot),)* + } + } + } + + impl Visitable for Assignments { + type Map = AssignmentsVisitMap; + + fn visit_map(self: &Self) -> Self::Map { + AssignmentsVisitMap { + assignments: vec![false; self.assignments().len()], + slots: DenseSlotSet::new(self.slot_readers().len()), + } + } + + fn reset_map(self: &Self, map: &mut Self::Map) { + let AssignmentsVisitMap { assignments, slots } = map; + assignments.clear(); + assignments.resize(self.assignments().len(), false); + if slots.len() != self.slot_readers().len() { + *slots = DenseSlotSet::new(self.slot_readers().len()); + } else { + slots.clear(); + } + } + } + + #[derive(Debug)] + struct Assignment { + inputs: SlotSet, + outputs: SlotSet, + conditions: Interned<[Cond]>, + insns: Vec, + source_location: SourceLocation, + } + + #[derive(Debug)] + struct SlotToAssignmentIndexFullMap { + $($type_plural_field: Box<[Vec]>,)* + } + + impl SlotToAssignmentIndexFullMap { + fn new(len: TypeLen) -> Self { + Self { + $($type_plural_field: vec![Vec::new(); len.$type_plural_field.value.try_into().expect("length too big")] + .into_boxed_slice(),)* + } + } + fn len(&self) -> TypeLen { + TypeLen { + $($type_plural_field: StatePartLen::new(self.$type_plural_field.len() as _),)* + } + } + fn keys_for_assignment( + &mut self, + assignment_index: usize, + ) -> SlotToAssignmentIndexFullMapKeysForAssignment<'_> { + SlotToAssignmentIndexFullMapKeysForAssignment { + map: self, + assignment_index, + } + } + fn for_each( + &self, + $(mut $type_plural_field: impl FnMut(StatePartIndex<$type_kind>, &[usize]),)* + ) { + $(self.$type_plural_field.iter().enumerate().for_each(|(k, v)| { + $type_plural_field(StatePartIndex::new(k as _), v) + });)* + } + } + + $(impl std::ops::Index> for SlotToAssignmentIndexFullMap { + type Output = Vec; + + fn index(&self, index: StatePartIndex<$type_kind>) -> &Self::Output { + &self.$type_plural_field[index.as_usize()] + } + } + + impl std::ops::IndexMut> for SlotToAssignmentIndexFullMap { + fn index_mut(&mut self, index: StatePartIndex<$type_kind>) -> &mut Self::Output { + &mut self.$type_plural_field[index.as_usize()] + } + })* + + struct SlotToAssignmentIndexFullMapKeysForAssignment<'a> { + map: &'a mut SlotToAssignmentIndexFullMap, + assignment_index: usize, + } + + $(impl<'a> Extend<&'a StatePartIndex<$type_kind>> + for SlotToAssignmentIndexFullMapKeysForAssignment<'_> { + fn extend>>(&mut self, iter: T) { + self.extend(iter.into_iter().copied()); + } + })* + + $(impl Extend> + for SlotToAssignmentIndexFullMapKeysForAssignment<'_> { + fn extend>>(&mut self, iter: T) { + iter.into_iter() + .for_each(|slot| self.map[slot].push(self.assignment_index)); + } + })* + + impl<'a> Extend<&'a SlotSet> for SlotToAssignmentIndexFullMapKeysForAssignment<'_> { + fn extend>(&mut self, iter: T) { + iter.into_iter().for_each( + |set| { + $(self.extend(&set.$type_plural_field);)* + }, + ); + } + } + + impl<'a> Extend<&'a Cond> for SlotToAssignmentIndexFullMapKeysForAssignment<'_> { + fn extend>(&mut self, iter: T) { + iter.into_iter().for_each(|cond| match cond.body { + CondBody::IfTrue { cond } | CondBody::IfFalse { cond } => { + let CompiledValue { + range, + layout: _, + write: _, + } = cond; + $(self.extend(range.$type_plural_field.iter());)* + } + CondBody::MatchArm { + discriminant, + variant_index: _, + } => self.extend([discriminant]), + }); + } + } + + impl Assignment { + fn new( + conditions: Interned<[Cond]>, + insns: Vec, + source_location: SourceLocation, + ) -> Self { + let mut inputs = SlotSet::default(); + let mut outputs = SlotSet::default(); + for insn in &insns { + let insn = match insn { + InsnOrLabel::Insn(insn) => insn, + InsnOrLabel::Label(_) => continue, + }; + for InsnField { ty, kind } in insn.fields() { + match (kind, ty) { + $((InsnFieldKind::Input, InsnFieldType::$type_singular_variant(&slot)) => { + inputs.extend([slot]); + })* + $(( + InsnFieldKind::Input, + InsnFieldType::$array_indexed_variant(&array_indexed), + ) => { + array_indexed.for_each_target(|slot| inputs.extend([slot])); + inputs.extend(array_indexed.indexes); + })* + $((InsnFieldKind::Output, InsnFieldType::$type_singular_variant(&slot)) => { + outputs.extend([slot]); + })* + $(( + InsnFieldKind::Output, + InsnFieldType::$array_indexed_variant(&array_indexed), + ) => { + array_indexed.for_each_target(|slot| { + outputs.extend([slot]); + }); + inputs.extend(array_indexed.indexes); + })* + ( + _, + InsnFieldType::Memory(_) + | InsnFieldType::SmallUInt(_) + | InsnFieldType::SmallSInt(_) + | InsnFieldType::InternedBigInt(_) + | InsnFieldType::U8(_) + | InsnFieldType::USize(_) + | InsnFieldType::Empty(_), + ) + | ( + InsnFieldKind::Immediate + | InsnFieldKind::Memory + | InsnFieldKind::BranchTarget, + _, + ) => {} + } + } + } + Self { + inputs, + outputs, + conditions, + insns, + source_location, + } + } + } + }; +} + +get_state_part_kinds! { + make_assignment_graph! { + type_plural_fields; + type_singular_variants; + type_kinds; + array_indexed_variants; + #[custom] input_variants = [small_slot = SmallInput, big_slot = BigInput, sim_only_slot = SimOnlyInput,]; + #[custom] output_variants = [small_slot = SmallOutput, big_slot = BigOutput, sim_only_slot = SimOnlyOutput,]; + } +} + +macro_rules! make_dense_slot_set { + ( + type_plural_fields = [$($type_plural_field:ident,)*]; + type_kinds = [$($type_kind:ident,)*]; + ) => { + #[derive(Clone, Debug, PartialEq, Eq, Hash)] + struct DenseSlotSet { + $($type_plural_field: Box<[bool]>,)* + } + + impl DenseSlotSet { + fn new(len: TypeLen) -> Self { + Self { + $($type_plural_field: vec![false; len.$type_plural_field.value.try_into().expect("length too big")] + .into_boxed_slice(),)* + } + } + fn len(&self) -> TypeLen { + TypeLen { + $($type_plural_field: StatePartLen::new(self.$type_plural_field.len() as _),)* + } + } + fn clear(&mut self) { + $(self.$type_plural_field.fill(false);)* + } + } + + trait DenseSlotSetMethods: Extend> { + fn contains(&self, k: StatePartIndex) -> bool; + fn remove(&mut self, k: StatePartIndex) -> bool { + self.take(k).is_some() + } + fn take(&mut self, k: StatePartIndex) -> Option>; + fn replace(&mut self, k: StatePartIndex) -> Option>; + fn insert(&mut self, k: StatePartIndex) -> bool { + self.replace(k).is_none() + } + } + + impl Extend> for DenseSlotSet + where + Self: DenseSlotSetMethods, + { + fn extend>>(&mut self, iter: T) { + iter.into_iter().for_each(|v| { + self.insert(v); + }); + } + } + + $(impl DenseSlotSetMethods<$type_kind> for DenseSlotSet { + fn contains(&self, k: StatePartIndex<$type_kind>) -> bool { + self.$type_plural_field[k.as_usize()] + } + + fn take( + &mut self, + k: StatePartIndex<$type_kind>, + ) -> Option> { + mem::replace(self.$type_plural_field.get_mut(k.as_usize())?, false).then_some(k) + } + + fn replace( + &mut self, + k: StatePartIndex<$type_kind>, + ) -> Option> { + mem::replace(&mut self.$type_plural_field[k.as_usize()], true).then_some(k) + } + })* + }; +} + +get_state_part_kinds! { + make_dense_slot_set! { + type_plural_fields; + type_kinds; + } +} + +macro_rules! make_slot_vec { + ( + type_plural_fields = [$($type_plural_field:ident,)*]; + type_kinds = [$($type_kind:ident,)*]; + ) => { + #[derive(Clone, Debug, Default, PartialEq, Eq, Hash)] + struct SlotVec { + $($type_plural_field: Vec>,)* + } + + impl SlotVec { + fn is_empty(&self) -> bool { + true $(&& self.$type_plural_field.is_empty())* + } + } + }; +} + +get_state_part_kinds! { + make_slot_vec! { + type_plural_fields; + type_kinds; + } +} + +macro_rules! make_slot_set { + ( + type_plural_fields = [$($type_plural_field:ident,)*]; + type_kinds = [$($type_kind:ident,)*]; + ) => { + #[derive(Clone, Debug, Default, PartialEq, Eq, Hash)] + struct SlotSet { + $($type_plural_field: BTreeSet>,)* + } + + impl SlotSet { + fn is_empty(&self) -> bool { + true $(&& self.$type_plural_field.is_empty())* + } + fn for_each( + &self, + $($type_plural_field: impl FnMut(StatePartIndex<$type_kind>),)* + ) { + $(self.$type_plural_field.iter().copied().for_each($type_plural_field);)* + } + fn all( + &self, + $($type_plural_field: impl FnMut(StatePartIndex<$type_kind>) -> bool,)* + ) -> bool { + true $(&& self.$type_plural_field.iter().copied().all($type_plural_field))* + } + } + + $(impl Extend> for SlotSet { + fn extend>>( + &mut self, + iter: T, + ) { + self.$type_plural_field.extend(iter); + } + })* + + $(impl Extend> for SlotSet { + fn extend>>(&mut self, iter: T) { + self.extend(iter.into_iter().flat_map(|v| v.iter())); + } + })* + + impl Extend for SlotSet { + fn extend>(&mut self, iter: T) { + iter.into_iter().for_each( + |range| { + $(self.extend(range.$type_plural_field.iter());)* + }, + ) + } + } + + impl Extend for SlotSet { + fn extend>(&mut self, iter: T) { + iter.into_iter().for_each( + |v| { + $(self.extend([v.$type_plural_field]);)* + }, + ) + } + } + + $(impl Extend> for SlotSet { + fn extend>>(&mut self, iter: T) { + self.extend(iter.into_iter().map(|v| v.index)); + } + })* + + impl Extend for SlotSet { + fn extend>(&mut self, iter: T) { + iter.into_iter().for_each(|cond_body| match cond_body { + CondBody::IfTrue { cond } | CondBody::IfFalse { cond } => { + self.extend([cond.range]); + } + CondBody::MatchArm { + discriminant, + variant_index: _, + } => self.extend([discriminant]), + }) + } + } + + impl Extend for SlotSet { + fn extend>(&mut self, iter: T) { + self.extend(iter.into_iter().map(|v| v.body)) + } + } + }; +} + +get_state_part_kinds! { + make_slot_set! { + type_plural_fields; + type_kinds; + } +} + +#[derive(Debug)] +struct RegisterReset { + is_async: bool, + init: CompiledValue, + rst: StatePartIndex, +} + +#[derive(Debug, Clone, Copy)] +struct ClockTrigger { + last_clk_was_low: StatePartIndex, + clk: StatePartIndex, + clk_triggered: StatePartIndex, + source_location: SourceLocation, +} + +#[derive(Debug)] +struct Register { + value: CompiledValue, + clk_triggered: StatePartIndex, + reset: Option, + source_location: SourceLocation, +} + +#[derive(Debug)] + +struct MemoryPort { + clk_triggered: StatePartIndex, + addr_delayed: Vec>, + en_delayed: Vec>, + #[allow(dead_code, reason = "used in Debug impl")] + data_layout: CompiledTypeLayout, + read_data_delayed: Vec, + write_data_delayed: Vec, + write_mask_delayed: Vec, + write_mode_delayed: Vec>, + write_insns: Vec, +} + +struct MemoryPortReadInsns<'a> { + addr: StatePartIndex, + en: StatePartIndex, + write_mode: Option>, + data: TypeIndexRange, + insns: &'a mut Vec, +} + +struct MemoryPortWriteInsns<'a> { + addr: StatePartIndex, + en: StatePartIndex, + write_mode: Option>, + data: TypeIndexRange, + mask: TypeIndexRange, + insns: &'a mut Vec, +} + +#[derive(Debug)] +struct Memory { + mem: Mem, + memory: StatePartIndex, + trace: TraceMem, + ports: Vec, +} + +#[derive(Copy, Clone)] +enum MakeTraceDeclTarget { + Expr(Expr), + Memory { + id: TraceMemoryId, + depth: usize, + stride: usize, + start: usize, + ty: CanonicalType, + }, +} + +impl MakeTraceDeclTarget { + fn flow(self) -> Flow { + match self { + MakeTraceDeclTarget::Expr(expr) => Expr::flow(expr), + MakeTraceDeclTarget::Memory { .. } => Flow::Duplex, + } + } + fn ty(self) -> CanonicalType { + match self { + MakeTraceDeclTarget::Expr(expr) => Expr::ty(expr), + MakeTraceDeclTarget::Memory { ty, .. } => ty, + } + } +} + +struct DebugOpaque(T); + +impl fmt::Debug for DebugOpaque { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("<...>") + } +} + +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +pub(crate) struct CompiledExternModule { + pub(crate) module_io_targets: Interned<[Target]>, + pub(crate) module_io: Interned<[CompiledValue]>, + pub(crate) simulation: ExternModuleSimulation, +} + +#[derive(Debug)] +pub struct Compiler { + insns: Insns, + original_base_module: Interned>, + base_module: Interned>, + modules: HashMap, + extern_modules: Vec, + compiled_values: HashMap>, + compiled_exprs: HashMap, CompiledExpr>, + compiled_exprs_to_values: HashMap, CompiledValue>, + decl_conditions: HashMap>, + compiled_values_to_dyn_array_indexes: + HashMap, StatePartIndex>, + compiled_value_bool_dest_is_small_map: + HashMap, StatePartIndex>, + assignments: Assignments, + clock_triggers: Vec, + compiled_value_to_clock_trigger_map: HashMap, ClockTrigger>, + enum_discriminants: HashMap, StatePartIndex>, + registers: Vec, + traces: SimTraces>>, + memories: Vec, + dump_assignments_dot: Option>>, +} + +macro_rules! impl_compiler { + ( + type_plural_fields = [$($type_plural_field:ident,)*]; + type_singular_fields = [$($type_singular_field:ident,)*]; + type_singular_variants = [$($type_singular_variant:ident,)*]; + type_kinds = [$($type_kind:ident,)*]; + copy_insns = [$($copy_insn:ident,)*]; + read_indexed_insns = [$($read_indexed_insn:ident,)*]; + write_indexed_insns = [$($write_indexed_insn:ident,)*]; + ) => { + impl Compiler { + fn make_trace_scalar_helper( + &mut self, + instantiated_module: InstantiatedModule, + target: MakeTraceDeclTarget, + source_location: SourceLocation, + $($type_singular_field: impl FnOnce(StatePartIndex<$type_kind>) -> SimTraceKind,)* + ) -> TraceLocation { + match target { + MakeTraceDeclTarget::Expr(target) => { + let compiled_value = self.compile_expr(instantiated_module, target); + let compiled_value = self.compiled_expr_to_value(compiled_value, source_location); + TraceLocation::Scalar(self.new_sim_trace(match compiled_value.range.len().as_single() { + $(Some(TypeLenSingle::$type_singular_variant) => { + $type_singular_field(compiled_value.range.$type_plural_field.start) + })* + None => unreachable!(), + })) + } + MakeTraceDeclTarget::Memory { + id, + depth, + stride, + start, + ty, + } => TraceLocation::Memory(TraceMemoryLocation { + id, + depth, + stride, + start, + len: ty.bit_width(), + }), + } + } + fn make_trace_scalar( + &mut self, + instantiated_module: InstantiatedModule, + target: MakeTraceDeclTarget, + name: Interned, + source_location: SourceLocation, + ) -> TraceDecl { + let flow = target.flow(); + match target.ty() { + CanonicalType::UInt(ty) => TraceUInt { + location: self.make_trace_scalar_helper( + instantiated_module, + target, + source_location, + |index| SimTraceKind::SmallUInt { index, ty }, + |index| SimTraceKind::BigUInt { index, ty }, + |_| unreachable!(""), + ), + name, + ty, + flow, + } + .into(), + CanonicalType::SInt(ty) => TraceSInt { + location: self.make_trace_scalar_helper( + instantiated_module, + target, + source_location, + |index| SimTraceKind::SmallSInt { index, ty }, + |index| SimTraceKind::BigSInt { index, ty }, + |_| unreachable!(""), + ), + name, + ty, + flow, + } + .into(), + CanonicalType::Bool(_) => TraceBool { + location: self.make_trace_scalar_helper( + instantiated_module, + target, + source_location, + |index| SimTraceKind::SmallBool { index }, + |index| SimTraceKind::BigBool { index }, + |_| unreachable!(""), + ), + name, + flow, + } + .into(), + CanonicalType::Array(_) => unreachable!(), + CanonicalType::Enum(ty) => { + assert_eq!(ty.discriminant_bit_width(), ty.type_properties().bit_width); + let location = match target { + MakeTraceDeclTarget::Expr(target) => { + let compiled_value = self.compile_expr(instantiated_module, target); + let compiled_value = + self.compiled_expr_to_value(compiled_value, source_location); + let discriminant = self.compile_enum_discriminant( + compiled_value.map_ty(Enum::from_canonical), + source_location, + ); + TraceLocation::Scalar(self.new_sim_trace(SimTraceKind::EnumDiscriminant { + index: discriminant, + ty, + })) + } + MakeTraceDeclTarget::Memory { + id, + depth, + stride, + start, + ty: _, + } => TraceLocation::Memory(TraceMemoryLocation { + id, + depth, + stride, + start, + len: ty.type_properties().bit_width, + }), + }; + TraceFieldlessEnum { + location, + name, + ty, + flow, + } + .into() + } + CanonicalType::Bundle(_) | CanonicalType::PhantomConst(_) => unreachable!(), + CanonicalType::AsyncReset(_) => TraceAsyncReset { + location: self.make_trace_scalar_helper( + instantiated_module, + target, + source_location, + |index| SimTraceKind::SmallAsyncReset { index }, + |index| SimTraceKind::BigAsyncReset { index }, + |_| unreachable!(""), + ), + name, + flow, + } + .into(), + CanonicalType::SyncReset(_) => TraceSyncReset { + location: self.make_trace_scalar_helper( + instantiated_module, + target, + source_location, + |index| SimTraceKind::SmallSyncReset { index }, + |index| SimTraceKind::BigSyncReset { index }, + |_| unreachable!(""), + ), + name, + flow, + } + .into(), + CanonicalType::Reset(_) => unreachable!(), + CanonicalType::Clock(_) => TraceClock { + location: self.make_trace_scalar_helper( + instantiated_module, + target, + source_location, + |index| SimTraceKind::SmallClock { index }, + |index| SimTraceKind::BigClock { index }, + |_| unreachable!(""), + ), + name, + flow, + } + .into(), + CanonicalType::DynSimOnly(ty) => TraceSimOnly { + location: self.make_trace_scalar_helper( + instantiated_module, + target, + source_location, + |_| unreachable!(""), + |_| unreachable!(""), + |index| SimTraceKind::SimOnly { index, ty }, + ), + name, + ty, + flow, + } + .into(), + } + } + fn compiled_expr_to_value( + &mut self, + expr: CompiledExpr, + source_location: SourceLocation, + ) -> CompiledValue { + if let Some(&retval) = self.compiled_exprs_to_values.get(&expr) { + return retval; + } + assert!( + expr.static_part.layout.ty.is_passive(), + "invalid expression passed to compiled_expr_to_value -- type must be passive", + ); + let CompiledExpr { + static_part, + indexes, + } = expr; + let retval = if indexes.as_ref().is_empty() { + CompiledValue { + layout: static_part.layout, + range: static_part.range, + write: None, + } + } else { + let layout = static_part.layout.with_anonymized_debug_info(); + let retval = CompiledValue { + layout, + range: self.insns.allocate_variable(&layout.layout), + write: None, + }; + let TypeIndexRange { + $($type_plural_field,)* + } = retval.range; + self.add_assignment( + Interned::default(), + chain!( + $($type_plural_field + .iter() + .zip(static_part.range.$type_plural_field.iter()) + .map(|(dest, base)| Insn::$read_indexed_insn { + dest, + src: StatePartArrayIndexed { + base, + indexes: indexes.$type_plural_field, + }, + }),)* + ), + source_location, + ); + retval + }; + self.compiled_exprs_to_values.insert(expr, retval); + retval + } + fn compile_simple_connect( + &mut self, + conditions: Interned<[Cond]>, + lhs: CompiledExpr, + rhs: CompiledValue, + source_location: SourceLocation, + ) { + let CompiledExpr { + static_part: lhs_static_part, + indexes, + } = lhs; + let (lhs_layout, lhs_range) = lhs_static_part.write(); + assert!( + lhs_layout.ty.is_passive(), + "invalid expression passed to compile_simple_connect -- type must be passive", + ); + self.add_assignment( + conditions, + chain!( + $(lhs_range.$type_plural_field + .iter() + .zip(rhs.range.$type_plural_field.iter()) + .map(|(base, src)| { + if indexes.$type_plural_field.is_empty() { + Insn::$copy_insn { dest: base, src } + } else { + Insn::$write_indexed_insn { + dest: StatePartArrayIndexed { + base, + indexes: indexes.$type_plural_field, + }, + src, + } + } + }),)* + ), + source_location, + ); + } + fn process_assignments(&mut self) { + self.assignments + .finalize(self.insns.state_layout().ty.clone().into()); + if let Some(DebugOpaque(dump_assignments_dot)) = &self.dump_assignments_dot { + let graph = + petgraph::graph::DiGraph::<_, _, usize>::from_elements(self.assignments.elements()); + dump_assignments_dot(&petgraph::dot::Dot::new(&graph)); + } + let assignments_order: Vec<_> = match petgraph::algo::toposort(&self.assignments, None) { + Ok(nodes) => nodes + .into_iter() + .filter_map(|n| match n { + AssignmentOrSlotIndex::AssignmentIndex(v) => Some(v), + _ => None, + }) + .collect(), + Err(e) => match e.node_id() { + AssignmentOrSlotIndex::AssignmentIndex(assignment_index) => panic!( + "combinatorial logic cycle detected at: {}", + self.assignments.assignments()[assignment_index].source_location, + ), + $(AssignmentOrSlotIndex::$type_singular_variant(slot) => panic!( + "combinatorial logic cycle detected through: {}", + self.insns.state_layout().ty.$type_plural_field.debug_data[slot.as_usize()].name, + ),)* + }, + }; + struct CondStackEntry<'a> { + cond: &'a Cond, + end_label: Label, + } + let mut cond_stack = Vec::>::new(); + for assignment_index in assignments_order { + let Assignment { + inputs: _, + outputs: _, + conditions, + insns, + source_location, + } = &self.assignments.assignments()[assignment_index]; + let mut same_len = 0; + for (index, (entry, cond)) in cond_stack.iter().zip(conditions).enumerate() { + if entry.cond != cond { + break; + } + same_len = index + 1; + } + while cond_stack.len() > same_len { + let CondStackEntry { cond: _, end_label } = + cond_stack.pop().expect("just checked len"); + self.insns.define_label_at_next_insn(end_label); + } + for cond in &conditions[cond_stack.len()..] { + let end_label = self.insns.new_label(); + match cond.body { + CondBody::IfTrue { cond: cond_value } + | CondBody::IfFalse { cond: cond_value } => { + let (branch_if_zero, branch_if_non_zero) = match cond_value.range.len().as_single() { + Some(TypeLenSingle::SmallSlot) => ( + Insn::BranchIfSmallZero { + target: end_label.0, + value: cond_value.range.small_slots.start, + }, + Insn::BranchIfSmallNonZero { + target: end_label.0, + value: cond_value.range.small_slots.start, + }, + ), + Some(TypeLenSingle::BigSlot) => ( + Insn::BranchIfZero { + target: end_label.0, + value: cond_value.range.big_slots.start, + }, + Insn::BranchIfNonZero { + target: end_label.0, + value: cond_value.range.big_slots.start, + }, + ), + Some(TypeLenSingle::SimOnlySlot) | None => unreachable!(), + }; + self.insns.push( + if let CondBody::IfTrue { .. } = cond.body { + branch_if_zero + } else { + branch_if_non_zero + }, + cond.source_location, + ); + } + CondBody::MatchArm { + discriminant, + variant_index, + } => { + self.insns.push( + Insn::BranchIfSmallNeImmediate { + target: end_label.0, + lhs: discriminant, + rhs: variant_index as _, + }, + cond.source_location, + ); + } + } + cond_stack.push(CondStackEntry { cond, end_label }); + } + self.insns.extend(insns.iter().copied(), *source_location); + } + for CondStackEntry { cond: _, end_label } in cond_stack { + self.insns.define_label_at_next_insn(end_label); + } + } + } + }; +} + +get_state_part_kinds! { + impl_compiler! { + type_plural_fields; + type_singular_fields; + type_singular_variants; + type_kinds; + copy_insns; + read_indexed_insns; + write_indexed_insns; + } +} + +impl Compiler { + pub fn new(base_module: Interned>) -> Self { + let original_base_module = base_module; + let base_module = deduce_resets(base_module, true) + .unwrap_or_else(|e| panic!("failed to deduce reset types: {e}")); + Self { + insns: Insns::new(), + original_base_module, + base_module, + modules: HashMap::default(), + extern_modules: Vec::new(), + compiled_values: HashMap::default(), + compiled_exprs: HashMap::default(), + compiled_exprs_to_values: HashMap::default(), + decl_conditions: HashMap::default(), + compiled_values_to_dyn_array_indexes: HashMap::default(), + compiled_value_bool_dest_is_small_map: HashMap::default(), + assignments: Assignments::default(), + clock_triggers: Vec::new(), + compiled_value_to_clock_trigger_map: HashMap::default(), + enum_discriminants: HashMap::default(), + registers: Vec::new(), + traces: SimTraces(Vec::new()), + memories: Vec::new(), + dump_assignments_dot: None, + } + } + #[doc(hidden)] + /// This is explicitly unstable and may be changed/removed at any time + pub fn dump_assignments_dot(&mut self, callback: Box) { + self.dump_assignments_dot = Some(DebugOpaque(callback)); + } + fn new_sim_trace(&mut self, kind: SimTraceKind) -> TraceScalarId { + let id = TraceScalarId(self.traces.0.len()); + self.traces.0.push(SimTrace { + kind, + state: (), + last_state: (), + }); + id + } + fn make_trace_decl_child( + &mut self, + instantiated_module: InstantiatedModule, + target: MakeTraceDeclTarget, + name: Interned, + source_location: SourceLocation, + ) -> TraceDecl { + match target.ty() { + CanonicalType::Array(ty) => { + let elements = Interned::from_iter((0..ty.len()).map(|index| { + self.make_trace_decl_child( + instantiated_module, + match target { + MakeTraceDeclTarget::Expr(target) => MakeTraceDeclTarget::Expr( + Expr::::from_canonical(target)[index], + ), + MakeTraceDeclTarget::Memory { + id, + depth, + stride, + start, + ty: _, + } => MakeTraceDeclTarget::Memory { + id, + depth, + stride, + start: start + ty.element().bit_width() * index, + ty: ty.element(), + }, + }, + Intern::intern_owned(format!("[{index}]")), + source_location, + ) + })); + TraceArray { + name, + elements, + ty, + flow: target.flow(), + } + .into() + } + CanonicalType::Enum(ty) => { + if ty.variants().iter().all(|v| v.ty.is_none()) { + self.make_trace_scalar(instantiated_module, target, name, source_location) + } else { + let flow = target.flow(); + let location = match target { + MakeTraceDeclTarget::Expr(target) => { + let compiled_value = self.compile_expr(instantiated_module, target); + let compiled_value = + self.compiled_expr_to_value(compiled_value, source_location); + let discriminant = self.compile_enum_discriminant( + compiled_value.map_ty(Enum::from_canonical), + source_location, + ); + TraceLocation::Scalar(self.new_sim_trace( + SimTraceKind::EnumDiscriminant { + index: discriminant, + ty, + }, + )) + } + MakeTraceDeclTarget::Memory { + id, + depth, + stride, + start, + ty: _, + } => TraceLocation::Memory(TraceMemoryLocation { + id, + depth, + stride, + start, + len: ty.discriminant_bit_width(), + }), + }; + let discriminant = TraceEnumDiscriminant { + location, + name: "$tag".intern(), + ty, + flow, + }; + let non_empty_fields = + Interned::from_iter(ty.variants().into_iter().enumerate().flat_map( + |(variant_index, variant)| { + variant.ty.map(|variant_ty| { + self.make_trace_decl_child( + instantiated_module, + match target { + MakeTraceDeclTarget::Expr(target) => { + MakeTraceDeclTarget::Expr( + ops::VariantAccess::new_by_index( + Expr::::from_canonical(target), + variant_index, + ) + .to_expr(), + ) + } + MakeTraceDeclTarget::Memory { + id, + depth, + stride, + start, + ty: _, + } => MakeTraceDeclTarget::Memory { + id, + depth, + stride, + start: start + ty.discriminant_bit_width(), + ty: variant_ty, + }, + }, + variant.name, + source_location, + ) + }) + }, + )); + TraceEnumWithFields { + name, + discriminant, + non_empty_fields, + ty, + flow, + } + .into() + } + } + CanonicalType::Bundle(ty) => { + let fields = Interned::from_iter(ty.fields().iter().zip(ty.field_offsets()).map( + |(field, field_offset)| { + self.make_trace_decl_child( + instantiated_module, + match target { + MakeTraceDeclTarget::Expr(target) => { + MakeTraceDeclTarget::Expr(Expr::field( + Expr::::from_canonical(target), + &field.name, + )) + } + MakeTraceDeclTarget::Memory { + id, + depth, + stride, + start, + ty: _, + } => { + let Some(bit_width) = field_offset.only_bit_width() else { + todo!("memory containing sim-only values"); + }; + MakeTraceDeclTarget::Memory { + id, + depth, + stride, + start: start + bit_width, + ty: field.ty, + } + } + }, + field.name, + source_location, + ) + }, + )); + TraceBundle { + name, + fields, + ty, + flow: target.flow(), + } + .into() + } + CanonicalType::UInt(_) + | CanonicalType::SInt(_) + | CanonicalType::Bool(_) + | CanonicalType::AsyncReset(_) + | CanonicalType::SyncReset(_) + | CanonicalType::Reset(_) + | CanonicalType::Clock(_) + | CanonicalType::DynSimOnly(_) => { + self.make_trace_scalar(instantiated_module, target, name, source_location) + } + CanonicalType::PhantomConst(_) => TraceBundle { + name, + fields: Interned::default(), + ty: Bundle::new(Interned::default()), + flow: target.flow(), + } + .into(), + } + } + fn make_trace_decl( + &mut self, + instantiated_module: InstantiatedModule, + target_base: TargetBase, + ) -> TraceDecl { + let target = MakeTraceDeclTarget::Expr(target_base.to_expr()); + match target_base { + TargetBase::ModuleIO(module_io) => TraceModuleIO { + name: module_io.name(), + child: self + .make_trace_decl_child( + instantiated_module, + target, + module_io.name(), + module_io.source_location(), + ) + .intern(), + ty: module_io.ty(), + flow: module_io.flow(), + } + .into(), + TargetBase::MemPort(mem_port) => { + let name = Intern::intern_owned(mem_port.port_name().to_string()); + let TraceDecl::Scope(TraceScope::Bundle(bundle)) = self.make_trace_decl_child( + instantiated_module, + target, + name, + mem_port.source_location(), + ) else { + unreachable!() + }; + TraceMemPort { + name, + bundle, + ty: mem_port.ty(), + } + .into() + } + TargetBase::Reg(reg) => TraceReg { + name: reg.name(), + child: self + .make_trace_decl_child( + instantiated_module, + target, + reg.name(), + reg.source_location(), + ) + .intern(), + ty: reg.ty(), + } + .into(), + TargetBase::RegSync(reg) => TraceReg { + name: reg.name(), + child: self + .make_trace_decl_child( + instantiated_module, + target, + reg.name(), + reg.source_location(), + ) + .intern(), + ty: reg.ty(), + } + .into(), + TargetBase::RegAsync(reg) => TraceReg { + name: reg.name(), + child: self + .make_trace_decl_child( + instantiated_module, + target, + reg.name(), + reg.source_location(), + ) + .intern(), + ty: reg.ty(), + } + .into(), + TargetBase::Wire(wire) => TraceWire { + name: wire.name(), + child: self + .make_trace_decl_child( + instantiated_module, + target, + wire.name(), + wire.source_location(), + ) + .intern(), + ty: wire.ty(), + } + .into(), + TargetBase::Instance(instance) => { + let TraceDecl::Scope(TraceScope::Bundle(instance_io)) = self.make_trace_decl_child( + instantiated_module, + target, + instance.name(), + instance.source_location(), + ) else { + unreachable!() + }; + let compiled_module = &self.modules[&InstantiatedModule::Child { + parent: instantiated_module.intern(), + instance: instance.intern(), + }]; + TraceInstance { + name: instance.name(), + instance_io, + module: compiled_module.trace_decls, + ty: instance.ty(), + } + .into() + } + } + } + fn compile_value( + &mut self, + target: TargetInInstantiatedModule, + ) -> CompiledValue { + if let Some(&retval) = self.compiled_values.get(&target) { + return retval; + } + let retval = match target.target { + Target::Base(base) => { + let unprefixed_layout = CompiledTypeLayout::get(base.canonical_ty()); + let layout = unprefixed_layout.with_prefixed_debug_names(&format!( + "{:?}.{:?}", + target.instantiated_module, + base.target_name() + )); + let range = self.insns.allocate_variable(&layout.layout); + let write = match *base { + TargetBase::ModuleIO(_) + | TargetBase::MemPort(_) + | TargetBase::Wire(_) + | TargetBase::Instance(_) => None, + TargetBase::Reg(_) | TargetBase::RegSync(_) | TargetBase::RegAsync(_) => { + let write_layout = unprefixed_layout.with_prefixed_debug_names(&format!( + "{:?}.{:?}$next", + target.instantiated_module, + base.target_name() + )); + Some(( + write_layout, + self.insns.allocate_variable(&write_layout.layout), + )) + } + }; + CompiledValue { + range, + layout, + write, + } + } + Target::Child(target_child) => { + let parent = self.compile_value(TargetInInstantiatedModule { + instantiated_module: target.instantiated_module, + target: *target_child.parent(), + }); + match *target_child.path_element() { + TargetPathElement::BundleField(TargetPathBundleField { name }) => { + parent.map_ty(Bundle::from_canonical).field_by_name(name) + } + TargetPathElement::ArrayElement(TargetPathArrayElement { index }) => { + parent.map_ty(Array::from_canonical).element(index) + } + TargetPathElement::DynArrayElement(_) => unreachable!(), + } + } + }; + self.compiled_values.insert(target, retval); + retval + } + fn add_assignment>( + &mut self, + conditions: Interned<[Cond]>, + insns: impl IntoIterator, + source_location: SourceLocation, + ) { + let insns = Vec::from_iter(insns.into_iter().map(Into::into)); + self.assignments + .push(Assignment::new(conditions, insns, source_location)); + } + fn simple_big_expr_input( + &mut self, + instantiated_module: InstantiatedModule, + input: Expr, + ) -> StatePartIndex { + let input = self.compile_expr(instantiated_module, input); + let input = + self.compiled_expr_to_value(input, instantiated_module.leaf_module().source_location()); + assert_eq!(input.range.len(), TypeLen::big_slot()); + input.range.big_slots.start + } + fn compile_expr_helper( + &mut self, + instantiated_module: InstantiatedModule, + dest_ty: CanonicalType, + make_insns: impl FnOnce(&mut Self, TypeIndexRange) -> Vec, + ) -> CompiledValue { + let layout = CompiledTypeLayout::get(dest_ty); + let range = self.insns.allocate_variable(&layout.layout); + let retval = CompiledValue { + layout, + range, + write: None, + }; + let insns = make_insns(self, range); + self.add_assignment( + Interned::default(), + insns, + instantiated_module.leaf_module().source_location(), + ); + retval + } + fn simple_nary_big_expr_helper( + &mut self, + instantiated_module: InstantiatedModule, + dest_ty: CanonicalType, + make_insns: impl FnOnce(StatePartIndex) -> Vec, + ) -> CompiledValue { + self.compile_expr_helper(instantiated_module, dest_ty, |_, dest| { + assert_eq!(dest.len(), TypeLen::big_slot()); + make_insns(dest.big_slots.start) + }) + } + fn simple_nary_big_expr( + &mut self, + instantiated_module: InstantiatedModule, + dest_ty: CanonicalType, + inputs: [Expr; N], + make_insns: impl FnOnce( + StatePartIndex, + [StatePartIndex; N], + ) -> Vec, + ) -> CompiledValue { + let inputs = inputs.map(|input| self.simple_big_expr_input(instantiated_module, input)); + self.simple_nary_big_expr_helper(instantiated_module, dest_ty, |dest| { + make_insns(dest, inputs) + }) + } + fn compiled_value_to_dyn_array_index( + &mut self, + compiled_value: CompiledValue, + source_location: SourceLocation, + ) -> StatePartIndex { + if let Some(&retval) = self + .compiled_values_to_dyn_array_indexes + .get(&compiled_value) + { + return retval; + } + let mut ty = compiled_value.layout.ty; + ty.width = ty.width.min(SmallUInt::BITS as usize); + let retval = match compiled_value.range.len().as_single() { + Some(TypeLenSingle::SmallSlot) => compiled_value.range.small_slots.start, + Some(TypeLenSingle::BigSlot) => { + let debug_data = SlotDebugData { + name: Interned::default(), + ty: ty.canonical(), + }; + let dest = self + .insns + .allocate_variable(&TypeLayout { + small_slots: StatePartLayout::scalar(debug_data, ()), + ..TypeLayout::empty() + }) + .small_slots + .start; + self.add_assignment( + Interned::default(), + vec![Insn::CastBigToArrayIndex { + dest, + src: compiled_value.range.big_slots.start, + }], + source_location, + ); + dest + } + Some(TypeLenSingle::SimOnlySlot) | None => unreachable!(), + }; + self.compiled_values_to_dyn_array_indexes + .insert(compiled_value, retval); + retval + } + fn compiled_value_bool_dest_is_small( + &mut self, + compiled_value: CompiledValue, + source_location: SourceLocation, + ) -> StatePartIndex { + if let Some(&retval) = self + .compiled_value_bool_dest_is_small_map + .get(&compiled_value) + { + return retval; + } + let retval = match compiled_value.range.len().as_single() { + Some(TypeLenSingle::SmallSlot) => compiled_value.range.small_slots.start, + Some(TypeLenSingle::BigSlot) => { + let debug_data = SlotDebugData { + name: Interned::default(), + ty: Bool.canonical(), + }; + let dest = self + .insns + .allocate_variable(&TypeLayout { + small_slots: StatePartLayout::scalar(debug_data, ()), + ..TypeLayout::empty() + }) + .small_slots + .start; + self.add_assignment( + Interned::default(), + vec![Insn::IsNonZeroDestIsSmall { + dest, + src: compiled_value.range.big_slots.start, + }], + source_location, + ); + dest + } + Some(TypeLenSingle::SimOnlySlot) | None => unreachable!(), + }; + self.compiled_value_bool_dest_is_small_map + .insert(compiled_value, retval); + retval + } + fn compile_cast_scalar_to_bits( + &mut self, + instantiated_module: InstantiatedModule, + arg: Expr, + cast_fn: impl FnOnce(Expr) -> Expr, + ) -> CompiledValue { + let arg = Expr::::from_canonical(arg); + let retval = cast_fn(arg); + let retval = self.compile_expr(instantiated_module, Expr::canonical(retval)); + let retval = self + .compiled_expr_to_value(retval, instantiated_module.leaf_module().source_location()); + retval.map_ty(UInt::from_canonical) + } + fn compile_cast_aggregate_to_bits( + &mut self, + instantiated_module: InstantiatedModule, + parts: impl IntoIterator>, + ) -> CompiledValue { + let retval = parts + .into_iter() + .map(|part| part.cast_to_bits()) + .reduce(|accumulator, part| accumulator | (part << Expr::ty(accumulator).width)) + .unwrap_or_else(|| UInt[0].zero().to_expr()); + let retval = self.compile_expr(instantiated_module, Expr::canonical(retval)); + let retval = self + .compiled_expr_to_value(retval, instantiated_module.leaf_module().source_location()); + retval.map_ty(UInt::from_canonical) + } + fn compile_cast_to_bits( + &mut self, + instantiated_module: InstantiatedModule, + expr: ops::CastToBits, + ) -> CompiledValue { + match Expr::ty(expr.arg()) { + CanonicalType::UInt(_) => { + self.compile_cast_scalar_to_bits(instantiated_module, expr.arg(), |arg| arg) + } + CanonicalType::SInt(ty) => self.compile_cast_scalar_to_bits( + instantiated_module, + expr.arg(), + |arg: Expr| arg.cast_to(ty.as_same_width_uint()), + ), + CanonicalType::Bool(_) + | CanonicalType::AsyncReset(_) + | CanonicalType::SyncReset(_) + | CanonicalType::Reset(_) + | CanonicalType::Clock(_) => self.compile_cast_scalar_to_bits( + instantiated_module, + expr.arg(), + |arg: Expr| arg.cast_to(UInt[1]), + ), + CanonicalType::Array(ty) => self.compile_cast_aggregate_to_bits( + instantiated_module, + (0..ty.len()).map(|index| Expr::::from_canonical(expr.arg())[index]), + ), + CanonicalType::Enum(ty) => self + .simple_nary_big_expr( + instantiated_module, + UInt[ty.type_properties().bit_width].canonical(), + [Expr::canonical(expr.arg())], + |dest, [src]| vec![Insn::Copy { dest, src }], + ) + .map_ty(UInt::from_canonical), + CanonicalType::Bundle(ty) => self.compile_cast_aggregate_to_bits( + instantiated_module, + ty.fields().iter().map(|field| { + Expr::field(Expr::::from_canonical(expr.arg()), &field.name) + }), + ), + CanonicalType::PhantomConst(_) | CanonicalType::DynSimOnly(_) => { + self.compile_cast_aggregate_to_bits(instantiated_module, []) + } + } + } + fn compile_cast_bits_to_or_uninit( + &mut self, + instantiated_module: InstantiatedModule, + arg: Option>, + ty: CanonicalType, + ) -> CompiledValue { + let retval = match ty { + CanonicalType::UInt(ty) => Expr::canonical(arg.unwrap_or_else(|| ty.zero().to_expr())), + CanonicalType::SInt(ty) => { + Expr::canonical(arg.map_or_else(|| ty.zero().to_expr(), |arg| arg.cast_to(ty))) + } + CanonicalType::Bool(ty) => { + Expr::canonical(arg.map_or_else(|| false.to_expr(), |arg| arg.cast_to(ty))) + } + CanonicalType::Array(ty) => { + let stride = ty.element().bit_width(); + Expr::::canonical(match arg { + Some(arg) => ops::ArrayLiteral::new( + ty.element(), + Interned::from_iter((0..ty.len()).map(|index| { + let start = stride * index; + let end = start + stride; + arg[start..end].cast_bits_to(ty.element()) + })), + ) + .to_expr(), + None => repeat(ty.element().uninit(), ty.len()), + }) + } + ty @ CanonicalType::Enum(_) => { + return self.simple_nary_big_expr( + instantiated_module, + ty, + [Expr::canonical(arg.unwrap_or_else(|| { + UInt::new_dyn(ty.bit_width()).zero().to_expr() + }))], + |dest, [src]| vec![Insn::Copy { dest, src }], + ); + } + CanonicalType::Bundle(ty) => Expr::canonical( + ops::BundleLiteral::new( + ty, + Interned::from_iter(ty.field_offsets().iter().zip(&ty.fields()).map( + |(&offset, &field)| { + let OpaqueSimValueSize { + bit_width: offset, + sim_only_values_len: 0, + } = offset + else { + unreachable!(); + }; + let end = offset + field.ty.bit_width(); + match arg { + Some(arg) => arg[offset..end].cast_bits_to(field.ty), + None => field.ty.uninit(), + } + }, + )), + ) + .to_expr(), + ), + CanonicalType::AsyncReset(ty) => Expr::canonical( + arg.unwrap_or_else(|| UInt::new_dyn(1).zero().to_expr()) + .cast_to(ty), + ), + CanonicalType::SyncReset(ty) => Expr::canonical( + arg.unwrap_or_else(|| UInt::new_dyn(1).zero().to_expr()) + .cast_to(ty), + ), + CanonicalType::Reset(_) => unreachable!(), + CanonicalType::Clock(ty) => Expr::canonical( + arg.unwrap_or_else(|| UInt::new_dyn(1).zero().to_expr()) + .cast_to(ty), + ), + CanonicalType::PhantomConst(ty) => { + if let Some(arg) = arg { + let _ = self.compile_expr(instantiated_module, Expr::canonical(arg)); + } + Expr::canonical(ty.to_expr()) + } + CanonicalType::DynSimOnly(ty) => { + assert!(arg.is_none(), "can't cast bits to SimOnly"); + return self.compile_expr_helper(instantiated_module, ty.canonical(), |_, dest| { + assert_eq!(dest.len(), TypeLen::sim_only_slot()); + vec![] + }); + } + }; + let retval = self.compile_expr(instantiated_module, Expr::canonical(retval)); + self.compiled_expr_to_value(retval, instantiated_module.leaf_module().source_location()) + } + fn compile_aggregate_literal( + &mut self, + instantiated_module: InstantiatedModule, + dest_ty: CanonicalType, + inputs: Interned<[Expr]>, + ) -> CompiledValue { + self.compile_expr_helper(instantiated_module, dest_ty, |this, dest| { + let mut insns = Vec::new(); + let mut offset = TypeIndex::ZERO; + for input in inputs { + let input = this.compile_expr(instantiated_module, input); + let input = this + .compiled_expr_to_value( + input, + instantiated_module.leaf_module().source_location(), + ) + .range; + insns.extend( + input.insns_for_copy_to(dest.slice(TypeIndexRange::new(offset, input.len()))), + ); + offset = offset.offset(input.len().as_index()); + } + insns + }) + } + fn compile_expr( + &mut self, + instantiated_module: InstantiatedModule, + expr: Expr, + ) -> CompiledExpr { + if let Some(&retval) = self.compiled_exprs.get(&expr) { + return retval; + } + let mut cast_bit = |arg: Expr| { + let src_signed = match Expr::ty(arg) { + CanonicalType::UInt(_) => false, + CanonicalType::SInt(_) => true, + CanonicalType::Bool(_) => false, + CanonicalType::Array(_) => unreachable!(), + CanonicalType::Enum(_) => unreachable!(), + CanonicalType::Bundle(_) => unreachable!(), + CanonicalType::AsyncReset(_) => false, + CanonicalType::SyncReset(_) => false, + CanonicalType::Reset(_) => false, + CanonicalType::Clock(_) => false, + CanonicalType::PhantomConst(_) => unreachable!(), + CanonicalType::DynSimOnly(_) => unreachable!(), + }; + let dest_signed = match Expr::ty(expr) { + CanonicalType::UInt(_) => false, + CanonicalType::SInt(_) => true, + CanonicalType::Bool(_) => false, + CanonicalType::Array(_) => unreachable!(), + CanonicalType::Enum(_) => unreachable!(), + CanonicalType::Bundle(_) => unreachable!(), + CanonicalType::AsyncReset(_) => false, + CanonicalType::SyncReset(_) => false, + CanonicalType::Reset(_) => false, + CanonicalType::Clock(_) => false, + CanonicalType::PhantomConst(_) => unreachable!(), + CanonicalType::DynSimOnly(_) => unreachable!(), + }; + self.simple_nary_big_expr(instantiated_module, Expr::ty(expr), [arg], |dest, [src]| { + match (src_signed, dest_signed) { + (false, false) | (true, true) => { + vec![Insn::Copy { dest, src }] + } + (false, true) => vec![Insn::CastToSInt { + dest, + src, + dest_width: 1, + }], + (true, false) => vec![Insn::CastToUInt { + dest, + src, + dest_width: 1, + }], + } + }) + .into() + }; + let retval: CompiledExpr<_> = match *Expr::expr_enum(expr) { + ExprEnum::UIntLiteral(expr) => self + .simple_nary_big_expr( + instantiated_module, + expr.ty().canonical(), + [], + |dest, []| { + vec![Insn::Const { + dest, + value: expr.to_bigint().intern_sized(), + }] + }, + ) + .into(), + ExprEnum::SIntLiteral(expr) => self + .simple_nary_big_expr( + instantiated_module, + expr.ty().canonical(), + [], + |dest, []| { + vec![Insn::Const { + dest, + value: expr.to_bigint().intern_sized(), + }] + }, + ) + .into(), + ExprEnum::BoolLiteral(expr) => self + .simple_nary_big_expr(instantiated_module, Bool.canonical(), [], |dest, []| { + vec![Insn::Const { + dest, + value: BigInt::from(expr).intern_sized(), + }] + }) + .into(), + ExprEnum::PhantomConst(_) => self + .compile_aggregate_literal(instantiated_module, Expr::ty(expr), Interned::default()) + .into(), + ExprEnum::BundleLiteral(literal) => self + .compile_aggregate_literal( + instantiated_module, + Expr::ty(expr), + literal.field_values(), + ) + .into(), + ExprEnum::ArrayLiteral(literal) => self + .compile_aggregate_literal( + instantiated_module, + Expr::ty(expr), + literal.element_values(), + ) + .into(), + ExprEnum::EnumLiteral(expr) => { + let enum_bits_ty = UInt[expr.ty().type_properties().bit_width]; + let enum_bits = if let Some(variant_value) = expr.variant_value() { + ( + UInt[expr.ty().discriminant_bit_width()] + .from_int_wrapping(expr.variant_index()), + variant_value, + ) + .cast_to_bits() + .cast_to(enum_bits_ty) + } else { + enum_bits_ty + .from_int_wrapping(expr.variant_index()) + .to_expr() + }; + self.compile_expr( + instantiated_module, + enum_bits.cast_bits_to(expr.ty().canonical()), + ) + } + ExprEnum::Uninit(expr) => self + .compile_cast_bits_to_or_uninit(instantiated_module, None, expr.ty()) + .into(), + ExprEnum::NotU(expr) => self + .simple_nary_big_expr( + instantiated_module, + Expr::ty(expr.arg()).canonical(), + [Expr::canonical(expr.arg())], + |dest, [src]| { + vec![Insn::NotU { + dest, + src, + width: Expr::ty(expr.arg()).width(), + }] + }, + ) + .into(), + ExprEnum::NotS(expr) => self + .simple_nary_big_expr( + instantiated_module, + Expr::ty(expr.arg()).canonical(), + [Expr::canonical(expr.arg())], + |dest, [src]| vec![Insn::NotS { dest, src }], + ) + .into(), + ExprEnum::NotB(expr) => self + .simple_nary_big_expr( + instantiated_module, + Expr::ty(expr.arg()).canonical(), + [Expr::canonical(expr.arg())], + |dest, [src]| { + vec![Insn::NotU { + dest, + src, + width: 1, + }] + }, + ) + .into(), + ExprEnum::Neg(expr) => self + .simple_nary_big_expr( + instantiated_module, + expr.ty().canonical(), + [Expr::canonical(expr.arg())], + |dest, [src]| vec![Insn::Neg { dest, src }], + ) + .into(), + ExprEnum::BitAndU(expr) => self + .simple_nary_big_expr( + instantiated_module, + expr.ty().canonical(), + [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], + |dest, [lhs, rhs]| vec![Insn::And { dest, lhs, rhs }], + ) + .into(), + ExprEnum::BitAndS(expr) => self + .simple_nary_big_expr( + instantiated_module, + expr.ty().canonical(), + [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], + |dest, [lhs, rhs]| vec![Insn::And { dest, lhs, rhs }], + ) + .into(), + ExprEnum::BitAndB(expr) => self + .simple_nary_big_expr( + instantiated_module, + Bool.canonical(), + [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], + |dest, [lhs, rhs]| vec![Insn::And { dest, lhs, rhs }], + ) + .into(), + ExprEnum::BitOrU(expr) => self + .simple_nary_big_expr( + instantiated_module, + expr.ty().canonical(), + [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], + |dest, [lhs, rhs]| vec![Insn::Or { dest, lhs, rhs }], + ) + .into(), + ExprEnum::BitOrS(expr) => self + .simple_nary_big_expr( + instantiated_module, + expr.ty().canonical(), + [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], + |dest, [lhs, rhs]| vec![Insn::Or { dest, lhs, rhs }], + ) + .into(), + ExprEnum::BitOrB(expr) => self + .simple_nary_big_expr( + instantiated_module, + Bool.canonical(), + [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], + |dest, [lhs, rhs]| vec![Insn::Or { dest, lhs, rhs }], + ) + .into(), + ExprEnum::BitXorU(expr) => self + .simple_nary_big_expr( + instantiated_module, + expr.ty().canonical(), + [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], + |dest, [lhs, rhs]| vec![Insn::Xor { dest, lhs, rhs }], + ) + .into(), + ExprEnum::BitXorS(expr) => self + .simple_nary_big_expr( + instantiated_module, + expr.ty().canonical(), + [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], + |dest, [lhs, rhs]| vec![Insn::Xor { dest, lhs, rhs }], + ) + .into(), + ExprEnum::BitXorB(expr) => self + .simple_nary_big_expr( + instantiated_module, + Bool.canonical(), + [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], + |dest, [lhs, rhs]| vec![Insn::Xor { dest, lhs, rhs }], + ) + .into(), + ExprEnum::AddU(expr) => self + .simple_nary_big_expr( + instantiated_module, + expr.ty().canonical(), + [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], + |dest, [lhs, rhs]| vec![Insn::Add { dest, lhs, rhs }], + ) + .into(), + ExprEnum::AddS(expr) => self + .simple_nary_big_expr( + instantiated_module, + expr.ty().canonical(), + [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], + |dest, [lhs, rhs]| vec![Insn::Add { dest, lhs, rhs }], + ) + .into(), + ExprEnum::SubU(expr) => self + .simple_nary_big_expr( + instantiated_module, + expr.ty().canonical(), + [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], + |dest, [lhs, rhs]| { + vec![Insn::SubU { + dest, + lhs, + rhs, + dest_width: expr.ty().width(), + }] + }, + ) + .into(), + ExprEnum::SubS(expr) => self + .simple_nary_big_expr( + instantiated_module, + expr.ty().canonical(), + [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], + |dest, [lhs, rhs]| vec![Insn::SubS { dest, lhs, rhs }], + ) + .into(), + ExprEnum::MulU(expr) => self + .simple_nary_big_expr( + instantiated_module, + expr.ty().canonical(), + [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], + |dest, [lhs, rhs]| vec![Insn::Mul { dest, lhs, rhs }], + ) + .into(), + ExprEnum::MulS(expr) => self + .simple_nary_big_expr( + instantiated_module, + expr.ty().canonical(), + [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], + |dest, [lhs, rhs]| vec![Insn::Mul { dest, lhs, rhs }], + ) + .into(), + ExprEnum::DivU(expr) => self + .simple_nary_big_expr( + instantiated_module, + expr.ty().canonical(), + [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], + |dest, [lhs, rhs]| vec![Insn::Div { dest, lhs, rhs }], + ) + .into(), + ExprEnum::DivS(expr) => self + .simple_nary_big_expr( + instantiated_module, + expr.ty().canonical(), + [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], + |dest, [lhs, rhs]| vec![Insn::Div { dest, lhs, rhs }], + ) + .into(), + ExprEnum::RemU(expr) => self + .simple_nary_big_expr( + instantiated_module, + expr.ty().canonical(), + [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], + |dest, [lhs, rhs]| vec![Insn::Rem { dest, lhs, rhs }], + ) + .into(), + ExprEnum::RemS(expr) => self + .simple_nary_big_expr( + instantiated_module, + expr.ty().canonical(), + [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], + |dest, [lhs, rhs]| vec![Insn::Rem { dest, lhs, rhs }], + ) + .into(), + ExprEnum::DynShlU(expr) => self + .simple_nary_big_expr( + instantiated_module, + expr.ty().canonical(), + [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], + |dest, [lhs, rhs]| vec![Insn::DynShl { dest, lhs, rhs }], + ) + .into(), + ExprEnum::DynShlS(expr) => self + .simple_nary_big_expr( + instantiated_module, + expr.ty().canonical(), + [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], + |dest, [lhs, rhs]| vec![Insn::DynShl { dest, lhs, rhs }], + ) + .into(), + ExprEnum::DynShrU(expr) => self + .simple_nary_big_expr( + instantiated_module, + expr.ty().canonical(), + [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], + |dest, [lhs, rhs]| vec![Insn::DynShr { dest, lhs, rhs }], + ) + .into(), + ExprEnum::DynShrS(expr) => self + .simple_nary_big_expr( + instantiated_module, + expr.ty().canonical(), + [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], + |dest, [lhs, rhs]| vec![Insn::DynShr { dest, lhs, rhs }], + ) + .into(), + ExprEnum::FixedShlU(expr) => self + .simple_nary_big_expr( + instantiated_module, + expr.ty().canonical(), + [Expr::canonical(expr.lhs())], + |dest, [lhs]| { + vec![Insn::Shl { + dest, + lhs, + rhs: expr.rhs(), + }] + }, + ) + .into(), + ExprEnum::FixedShlS(expr) => self + .simple_nary_big_expr( + instantiated_module, + expr.ty().canonical(), + [Expr::canonical(expr.lhs())], + |dest, [lhs]| { + vec![Insn::Shl { + dest, + lhs, + rhs: expr.rhs(), + }] + }, + ) + .into(), + ExprEnum::FixedShrU(expr) => self + .simple_nary_big_expr( + instantiated_module, + expr.ty().canonical(), + [Expr::canonical(expr.lhs())], + |dest, [lhs]| { + vec![Insn::Shr { + dest, + lhs, + rhs: expr.rhs(), + }] + }, + ) + .into(), + ExprEnum::FixedShrS(expr) => self + .simple_nary_big_expr( + instantiated_module, + expr.ty().canonical(), + [Expr::canonical(expr.lhs())], + |dest, [lhs]| { + vec![Insn::Shr { + dest, + lhs, + rhs: expr.rhs(), + }] + }, + ) + .into(), + ExprEnum::CmpLtB(expr) => self + .simple_nary_big_expr( + instantiated_module, + Bool.canonical(), + [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], + |dest, [lhs, rhs]| vec![Insn::CmpLt { dest, lhs, rhs }], + ) + .into(), + ExprEnum::CmpLeB(expr) => self + .simple_nary_big_expr( + instantiated_module, + Bool.canonical(), + [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], + |dest, [lhs, rhs]| vec![Insn::CmpLe { dest, lhs, rhs }], + ) + .into(), + ExprEnum::CmpGtB(expr) => self + .simple_nary_big_expr( + instantiated_module, + Bool.canonical(), + // swap both comparison direction and lhs/rhs + [Expr::canonical(expr.rhs()), Expr::canonical(expr.lhs())], + |dest, [lhs, rhs]| vec![Insn::CmpLt { dest, lhs, rhs }], + ) + .into(), + ExprEnum::CmpGeB(expr) => self + .simple_nary_big_expr( + instantiated_module, + Bool.canonical(), + // swap both comparison direction and lhs/rhs + [Expr::canonical(expr.rhs()), Expr::canonical(expr.lhs())], + |dest, [lhs, rhs]| vec![Insn::CmpLe { dest, lhs, rhs }], + ) + .into(), + ExprEnum::CmpEqB(expr) => self + .simple_nary_big_expr( + instantiated_module, + Bool.canonical(), + [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], + |dest, [lhs, rhs]| vec![Insn::CmpEq { dest, lhs, rhs }], + ) + .into(), + ExprEnum::CmpNeB(expr) => self + .simple_nary_big_expr( + instantiated_module, + Bool.canonical(), + [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], + |dest, [lhs, rhs]| vec![Insn::CmpNe { dest, lhs, rhs }], + ) + .into(), + ExprEnum::CmpLtU(expr) => self + .simple_nary_big_expr( + instantiated_module, + Bool.canonical(), + [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], + |dest, [lhs, rhs]| vec![Insn::CmpLt { dest, lhs, rhs }], + ) + .into(), + ExprEnum::CmpLeU(expr) => self + .simple_nary_big_expr( + instantiated_module, + Bool.canonical(), + [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], + |dest, [lhs, rhs]| vec![Insn::CmpLe { dest, lhs, rhs }], + ) + .into(), + ExprEnum::CmpGtU(expr) => self + .simple_nary_big_expr( + instantiated_module, + Bool.canonical(), + // swap both comparison direction and lhs/rhs + [Expr::canonical(expr.rhs()), Expr::canonical(expr.lhs())], + |dest, [lhs, rhs]| vec![Insn::CmpLt { dest, lhs, rhs }], + ) + .into(), + ExprEnum::CmpGeU(expr) => self + .simple_nary_big_expr( + instantiated_module, + Bool.canonical(), + // swap both comparison direction and lhs/rhs + [Expr::canonical(expr.rhs()), Expr::canonical(expr.lhs())], + |dest, [lhs, rhs]| vec![Insn::CmpLe { dest, lhs, rhs }], + ) + .into(), + ExprEnum::CmpEqU(expr) => self + .simple_nary_big_expr( + instantiated_module, + Bool.canonical(), + [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], + |dest, [lhs, rhs]| vec![Insn::CmpEq { dest, lhs, rhs }], + ) + .into(), + ExprEnum::CmpNeU(expr) => self + .simple_nary_big_expr( + instantiated_module, + Bool.canonical(), + [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], + |dest, [lhs, rhs]| vec![Insn::CmpNe { dest, lhs, rhs }], + ) + .into(), + ExprEnum::CmpLtS(expr) => self + .simple_nary_big_expr( + instantiated_module, + Bool.canonical(), + [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], + |dest, [lhs, rhs]| vec![Insn::CmpLt { dest, lhs, rhs }], + ) + .into(), + ExprEnum::CmpLeS(expr) => self + .simple_nary_big_expr( + instantiated_module, + Bool.canonical(), + [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], + |dest, [lhs, rhs]| vec![Insn::CmpLe { dest, lhs, rhs }], + ) + .into(), + ExprEnum::CmpGtS(expr) => self + .simple_nary_big_expr( + instantiated_module, + Bool.canonical(), + // swap both comparison direction and lhs/rhs + [Expr::canonical(expr.rhs()), Expr::canonical(expr.lhs())], + |dest, [lhs, rhs]| vec![Insn::CmpLt { dest, lhs, rhs }], + ) + .into(), + ExprEnum::CmpGeS(expr) => self + .simple_nary_big_expr( + instantiated_module, + Bool.canonical(), + // swap both comparison direction and lhs/rhs + [Expr::canonical(expr.rhs()), Expr::canonical(expr.lhs())], + |dest, [lhs, rhs]| vec![Insn::CmpLe { dest, lhs, rhs }], + ) + .into(), + ExprEnum::CmpEqS(expr) => self + .simple_nary_big_expr( + instantiated_module, + Bool.canonical(), + [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], + |dest, [lhs, rhs]| vec![Insn::CmpEq { dest, lhs, rhs }], + ) + .into(), + ExprEnum::CmpNeS(expr) => self + .simple_nary_big_expr( + instantiated_module, + Bool.canonical(), + [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], + |dest, [lhs, rhs]| vec![Insn::CmpNe { dest, lhs, rhs }], + ) + .into(), + ExprEnum::CastUIntToUInt(expr) => self + .simple_nary_big_expr( + instantiated_module, + expr.ty().canonical(), + [Expr::canonical(expr.arg())], + |dest, [src]| { + vec![Insn::CastToUInt { + dest, + src, + dest_width: expr.ty().width(), + }] + }, + ) + .into(), + ExprEnum::CastUIntToSInt(expr) => self + .simple_nary_big_expr( + instantiated_module, + expr.ty().canonical(), + [Expr::canonical(expr.arg())], + |dest, [src]| { + vec![Insn::CastToSInt { + dest, + src, + dest_width: expr.ty().width(), + }] + }, + ) + .into(), + ExprEnum::CastSIntToUInt(expr) => self + .simple_nary_big_expr( + instantiated_module, + expr.ty().canonical(), + [Expr::canonical(expr.arg())], + |dest, [src]| { + vec![Insn::CastToUInt { + dest, + src, + dest_width: expr.ty().width(), + }] + }, + ) + .into(), + ExprEnum::CastSIntToSInt(expr) => self + .simple_nary_big_expr( + instantiated_module, + expr.ty().canonical(), + [Expr::canonical(expr.arg())], + |dest, [src]| { + vec![Insn::CastToSInt { + dest, + src, + dest_width: expr.ty().width(), + }] + }, + ) + .into(), + ExprEnum::CastBoolToUInt(expr) => cast_bit(Expr::canonical(expr.arg())), + ExprEnum::CastBoolToSInt(expr) => cast_bit(Expr::canonical(expr.arg())), + ExprEnum::CastUIntToBool(expr) => cast_bit(Expr::canonical(expr.arg())), + ExprEnum::CastSIntToBool(expr) => cast_bit(Expr::canonical(expr.arg())), + ExprEnum::CastBoolToSyncReset(expr) => cast_bit(Expr::canonical(expr.arg())), + ExprEnum::CastUIntToSyncReset(expr) => cast_bit(Expr::canonical(expr.arg())), + ExprEnum::CastSIntToSyncReset(expr) => cast_bit(Expr::canonical(expr.arg())), + ExprEnum::CastBoolToAsyncReset(expr) => cast_bit(Expr::canonical(expr.arg())), + ExprEnum::CastUIntToAsyncReset(expr) => cast_bit(Expr::canonical(expr.arg())), + ExprEnum::CastSIntToAsyncReset(expr) => cast_bit(Expr::canonical(expr.arg())), + ExprEnum::CastSyncResetToBool(expr) => cast_bit(Expr::canonical(expr.arg())), + ExprEnum::CastSyncResetToUInt(expr) => cast_bit(Expr::canonical(expr.arg())), + ExprEnum::CastSyncResetToSInt(expr) => cast_bit(Expr::canonical(expr.arg())), + ExprEnum::CastSyncResetToReset(expr) => cast_bit(Expr::canonical(expr.arg())), + ExprEnum::CastAsyncResetToBool(expr) => cast_bit(Expr::canonical(expr.arg())), + ExprEnum::CastAsyncResetToUInt(expr) => cast_bit(Expr::canonical(expr.arg())), + ExprEnum::CastAsyncResetToSInt(expr) => cast_bit(Expr::canonical(expr.arg())), + ExprEnum::CastAsyncResetToReset(expr) => cast_bit(Expr::canonical(expr.arg())), + ExprEnum::CastResetToBool(expr) => cast_bit(Expr::canonical(expr.arg())), + ExprEnum::CastResetToUInt(expr) => cast_bit(Expr::canonical(expr.arg())), + ExprEnum::CastResetToSInt(expr) => cast_bit(Expr::canonical(expr.arg())), + ExprEnum::CastBoolToClock(expr) => cast_bit(Expr::canonical(expr.arg())), + ExprEnum::CastUIntToClock(expr) => cast_bit(Expr::canonical(expr.arg())), + ExprEnum::CastSIntToClock(expr) => cast_bit(Expr::canonical(expr.arg())), + ExprEnum::CastClockToBool(expr) => cast_bit(Expr::canonical(expr.arg())), + ExprEnum::CastClockToUInt(expr) => cast_bit(Expr::canonical(expr.arg())), + ExprEnum::CastClockToSInt(expr) => cast_bit(Expr::canonical(expr.arg())), + ExprEnum::FieldAccess(expr) => self + .compile_expr(instantiated_module, Expr::canonical(expr.base())) + .map_ty(Bundle::from_canonical) + .field_by_index(expr.field_index()), + ExprEnum::VariantAccess(variant_access) => { + let start = Expr::ty(variant_access.base()).discriminant_bit_width(); + let len = Expr::ty(expr).bit_width(); + self.compile_expr( + instantiated_module, + variant_access.base().cast_to_bits()[start..start + len] + .cast_bits_to(Expr::ty(expr)), + ) + } + ExprEnum::ArrayIndex(expr) => self + .compile_expr(instantiated_module, Expr::canonical(expr.base())) + .map_ty(Array::from_canonical) + .element(expr.element_index()), + ExprEnum::DynArrayIndex(expr) => { + let element_index = + self.compile_expr(instantiated_module, Expr::canonical(expr.element_index())); + let element_index = self.compiled_expr_to_value( + element_index, + instantiated_module.leaf_module().source_location(), + ); + let index_slot = self.compiled_value_to_dyn_array_index( + element_index.map_ty(UInt::from_canonical), + instantiated_module.leaf_module().source_location(), + ); + self.compile_expr(instantiated_module, Expr::canonical(expr.base())) + .map_ty(Array::from_canonical) + .element_dyn(index_slot) + } + ExprEnum::ReduceBitAndU(expr) => if Expr::ty(expr.arg()).width() == 0 { + self.compile_expr(instantiated_module, Expr::canonical(true.to_expr())) + } else { + self.compile_expr( + instantiated_module, + Expr::canonical( + expr.arg() + .cmp_eq(Expr::ty(expr.arg()).from_int_wrapping(-1)), + ), + ) + } + .into(), + ExprEnum::ReduceBitAndS(expr) => if Expr::ty(expr.arg()).width() == 0 { + self.compile_expr(instantiated_module, Expr::canonical(true.to_expr())) + } else { + self.compile_expr( + instantiated_module, + Expr::canonical( + expr.arg() + .cmp_eq(Expr::ty(expr.arg()).from_int_wrapping(-1)), + ), + ) + } + .into(), + ExprEnum::ReduceBitOrU(expr) => if Expr::ty(expr.arg()).width() == 0 { + self.compile_expr(instantiated_module, Expr::canonical(false.to_expr())) + } else { + self.compile_expr( + instantiated_module, + Expr::canonical(expr.arg().cmp_ne(Expr::ty(expr.arg()).from_int_wrapping(0))), + ) + } + .into(), + ExprEnum::ReduceBitOrS(expr) => if Expr::ty(expr.arg()).width() == 0 { + self.compile_expr(instantiated_module, Expr::canonical(false.to_expr())) + } else { + self.compile_expr( + instantiated_module, + Expr::canonical(expr.arg().cmp_ne(Expr::ty(expr.arg()).from_int_wrapping(0))), + ) + } + .into(), + ExprEnum::ReduceBitXorU(expr) => self + .simple_nary_big_expr( + instantiated_module, + UInt::<1>::TYPE.canonical(), + [Expr::canonical(expr.arg())], + |dest, [src]| { + vec![Insn::ReduceBitXor { + dest, + src, + input_width: Expr::ty(expr.arg()).width(), + }] + }, + ) + .into(), + ExprEnum::ReduceBitXorS(expr) => self + .simple_nary_big_expr( + instantiated_module, + UInt::<1>::TYPE.canonical(), + [Expr::canonical(expr.arg())], + |dest, [src]| { + vec![Insn::ReduceBitXor { + dest, + src, + input_width: Expr::ty(expr.arg()).width(), + }] + }, + ) + .into(), + ExprEnum::SliceUInt(expr) => self + .simple_nary_big_expr( + instantiated_module, + UInt::new_dyn(expr.range().len()).canonical(), + [Expr::canonical(expr.base())], + |dest, [src]| { + vec![Insn::SliceInt { + dest, + src, + start: expr.range().start, + len: expr.range().len(), + }] + }, + ) + .into(), + ExprEnum::SliceSInt(expr) => self + .simple_nary_big_expr( + instantiated_module, + UInt::new_dyn(expr.range().len()).canonical(), + [Expr::canonical(expr.base())], + |dest, [src]| { + vec![Insn::SliceInt { + dest, + src, + start: expr.range().start, + len: expr.range().len(), + }] + }, + ) + .into(), + ExprEnum::CastToBits(expr) => self + .compile_cast_to_bits(instantiated_module, expr) + .map_ty(CanonicalType::UInt) + .into(), + ExprEnum::CastBitsTo(expr) => self + .compile_cast_bits_to_or_uninit(instantiated_module, Some(expr.arg()), expr.ty()) + .into(), + ExprEnum::ModuleIO(expr) => self + .compile_value(TargetInInstantiatedModule { + instantiated_module, + target: expr.into(), + }) + .into(), + ExprEnum::Instance(expr) => self + .compile_value(TargetInInstantiatedModule { + instantiated_module, + target: expr.into(), + }) + .into(), + ExprEnum::Wire(expr) => self + .compile_value(TargetInInstantiatedModule { + instantiated_module, + target: expr.into(), + }) + .into(), + ExprEnum::Reg(expr) => self + .compile_value(TargetInInstantiatedModule { + instantiated_module, + target: expr.into(), + }) + .into(), + ExprEnum::RegSync(expr) => self + .compile_value(TargetInInstantiatedModule { + instantiated_module, + target: expr.into(), + }) + .into(), + ExprEnum::RegAsync(expr) => self + .compile_value(TargetInInstantiatedModule { + instantiated_module, + target: expr.into(), + }) + .into(), + ExprEnum::MemPort(expr) => self + .compile_value(TargetInInstantiatedModule { + instantiated_module, + target: expr.into(), + }) + .into(), + }; + self.compiled_exprs.insert(expr, retval); + retval + } + fn compile_connect( + &mut self, + lhs_instantiated_module: InstantiatedModule, + lhs_conditions: Interned<[Cond]>, + lhs: Expr, + rhs_instantiated_module: InstantiatedModule, + rhs_conditions: Interned<[Cond]>, + mut rhs: Expr, + source_location: SourceLocation, + ) { + if Expr::ty(lhs) != Expr::ty(rhs) || !Expr::ty(lhs).is_passive() { + match Expr::ty(lhs) { + CanonicalType::UInt(lhs_ty) => { + rhs = Expr::canonical(Expr::::from_canonical(rhs).cast_to(lhs_ty)); + } + CanonicalType::SInt(lhs_ty) => { + rhs = Expr::canonical(Expr::::from_canonical(rhs).cast_to(lhs_ty)); + } + CanonicalType::Bool(_) => unreachable!(), + CanonicalType::Array(lhs_ty) => { + let CanonicalType::Array(rhs_ty) = Expr::ty(rhs) else { + unreachable!(); + }; + assert_eq!(lhs_ty.len(), rhs_ty.len()); + let lhs = Expr::::from_canonical(lhs); + let rhs = Expr::::from_canonical(rhs); + for index in 0..lhs_ty.len() { + self.compile_connect( + lhs_instantiated_module, + lhs_conditions, + lhs[index], + rhs_instantiated_module, + rhs_conditions, + rhs[index], + source_location, + ); + } + return; + } + CanonicalType::Enum(lhs_ty) => { + let CanonicalType::Enum(rhs_ty) = Expr::ty(rhs) else { + unreachable!(); + }; + todo!("handle connect with different enum types"); + } + CanonicalType::Bundle(lhs_ty) => { + let CanonicalType::Bundle(rhs_ty) = Expr::ty(rhs) else { + unreachable!(); + }; + assert_eq!(lhs_ty.fields().len(), rhs_ty.fields().len()); + let lhs = Expr::::from_canonical(lhs); + let rhs = Expr::::from_canonical(rhs); + for ( + field_index, + ( + BundleField { + name, + flipped, + ty: _, + }, + rhs_field, + ), + ) in lhs_ty.fields().into_iter().zip(rhs_ty.fields()).enumerate() + { + assert_eq!(name, rhs_field.name); + assert_eq!(flipped, rhs_field.flipped); + let lhs_expr = ops::FieldAccess::new_by_index(lhs, field_index).to_expr(); + let rhs_expr = ops::FieldAccess::new_by_index(rhs, field_index).to_expr(); + if flipped { + // swap lhs/rhs + self.compile_connect( + rhs_instantiated_module, + rhs_conditions, + rhs_expr, + lhs_instantiated_module, + lhs_conditions, + lhs_expr, + source_location, + ); + } else { + self.compile_connect( + lhs_instantiated_module, + lhs_conditions, + lhs_expr, + rhs_instantiated_module, + rhs_conditions, + rhs_expr, + source_location, + ); + } + } + return; + } + CanonicalType::AsyncReset(_) => unreachable!(), + CanonicalType::SyncReset(_) => unreachable!(), + CanonicalType::Reset(_) => unreachable!(), + CanonicalType::Clock(_) => unreachable!(), + CanonicalType::PhantomConst(_) => unreachable!("PhantomConst mismatch"), + CanonicalType::DynSimOnly(_) => { + unreachable!("DynSimOnly mismatch"); + } + } + } + let Some(target) = lhs.target() else { + unreachable!("connect lhs must have target"); + }; + let lhs_decl_conditions = self.decl_conditions[&TargetInInstantiatedModule { + instantiated_module: lhs_instantiated_module, + target: target.base().into(), + }]; + let lhs = self.compile_expr(lhs_instantiated_module, lhs); + let rhs = self.compile_expr(rhs_instantiated_module, rhs); + let rhs = self.compiled_expr_to_value(rhs, source_location); + self.compile_simple_connect( + lhs_conditions[lhs_decl_conditions.len()..].intern(), + lhs, + rhs, + source_location, + ); + } + fn compile_clock( + &mut self, + clk: CompiledValue, + source_location: SourceLocation, + ) -> ClockTrigger { + if let Some(&retval) = self.compiled_value_to_clock_trigger_map.get(&clk) { + return retval; + } + let mut alloc_small_slot = |part_name: &str| { + self.insns + .state_layout + .ty + .small_slots + .allocate(&StatePartLayout::scalar( + SlotDebugData { + name: Interned::default(), + ty: Bool.canonical(), + }, + (), + )) + .start + }; + let last_clk_was_low = alloc_small_slot("last_clk_was_low"); + let clk_triggered = alloc_small_slot("clk_triggered"); + let retval = ClockTrigger { + last_clk_was_low, + clk: self.compiled_value_bool_dest_is_small( + clk.map_ty(CanonicalType::Clock), + source_location, + ), + clk_triggered, + source_location, + }; + self.add_assignment( + Interned::default(), + [Insn::AndSmall { + dest: clk_triggered, + lhs: retval.clk, + rhs: last_clk_was_low, + }], + source_location, + ); + self.clock_triggers.push(retval); + self.compiled_value_to_clock_trigger_map.insert(clk, retval); + retval + } + fn compile_enum_discriminant( + &mut self, + enum_value: CompiledValue, + source_location: SourceLocation, + ) -> StatePartIndex { + if let Some(&retval) = self.enum_discriminants.get(&enum_value) { + return retval; + } + let retval_ty = Enum::new( + enum_value + .layout + .ty + .variants() + .iter() + .map(|variant| EnumVariant { + name: variant.name, + ty: None, + }) + .collect(), + ); + let retval = if retval_ty == enum_value.layout.ty + && enum_value.range.len() == TypeLen::small_slot() + { + enum_value.range.small_slots.start + } else { + let retval = self + .insns + .state_layout + .ty + .small_slots + .allocate(&StatePartLayout::scalar( + SlotDebugData { + name: Interned::default(), + ty: retval_ty.canonical(), + }, + (), + )) + .start; + let discriminant_bit_width = enum_value.layout.ty.discriminant_bit_width(); + let discriminant_mask = !(!0u64 << discriminant_bit_width); + let insn = match enum_value.range.len().as_single() { + Some(TypeLenSingle::BigSlot) => Insn::AndBigWithSmallImmediate { + dest: retval, + lhs: enum_value.range.big_slots.start, + rhs: discriminant_mask, + }, + Some(TypeLenSingle::SmallSlot) => { + if discriminant_bit_width == enum_value.layout.ty.type_properties().bit_width { + Insn::CopySmall { + dest: retval, + src: enum_value.range.small_slots.start, + } + } else { + Insn::AndSmallImmediate { + dest: retval, + lhs: enum_value.range.small_slots.start, + rhs: discriminant_mask, + } + } + } + Some(TypeLenSingle::SimOnlySlot) | None => unreachable!(), + }; + self.add_assignment(Interned::default(), [insn], source_location); + retval + }; + self.enum_discriminants.insert(enum_value, retval); + retval + } + fn compile_stmt_reg( + &mut self, + stmt_reg: StmtReg, + instantiated_module: InstantiatedModule, + value: CompiledValue, + ) { + let StmtReg { annotations, reg } = stmt_reg; + let clk = self.compile_expr(instantiated_module, Expr::canonical(reg.clock_domain().clk)); + let clk = self + .compiled_expr_to_value(clk, reg.source_location()) + .map_ty(Clock::from_canonical); + let clk = self.compile_clock(clk, reg.source_location()); + struct Dispatch; + impl ResetTypeDispatch for Dispatch { + type Input = (); + + type Output = bool; + + fn reset(self, _input: Self::Input) -> Self::Output { + unreachable!() + } + + fn sync_reset(self, _input: Self::Input) -> Self::Output { + false + } + + fn async_reset(self, _input: Self::Input) -> Self::Output { + true + } + } + let reset = if let Some(init) = reg.init() { + let init = self.compile_expr(instantiated_module, init); + let init = self.compiled_expr_to_value(init, reg.source_location()); + let rst = + self.compile_expr(instantiated_module, Expr::canonical(reg.clock_domain().rst)); + let rst = self.compiled_expr_to_value(rst, reg.source_location()); + let rst = self.compiled_value_bool_dest_is_small(rst, reg.source_location()); + let is_async = R::dispatch((), Dispatch); + if is_async { + let cond = Expr::canonical(reg.clock_domain().rst.cast_to(Bool)); + let cond = self.compile_expr(instantiated_module, cond); + let cond = self.compiled_expr_to_value(cond, reg.source_location()); + let cond = cond.map_ty(Bool::from_canonical); + // write to the register's current value since asynchronous reset is combinational + let lhs = CompiledValue { + layout: value.layout, + range: value.range, + write: None, + } + .into(); + self.compile_simple_connect( + [Cond { + body: CondBody::IfTrue { cond }, + source_location: reg.source_location(), + }][..] + .intern(), + lhs, + init, + reg.source_location(), + ); + } + Some(RegisterReset { + is_async, + init, + rst, + }) + } else { + None + }; + self.registers.push(Register { + value, + clk_triggered: clk.clk_triggered, + reset, + source_location: reg.source_location(), + }); + } + fn compile_declaration( + &mut self, + declaration: StmtDeclaration, + parent_module: Interned, + conditions: Interned<[Cond]>, + ) -> TraceDecl { + let target_base: TargetBase = match &declaration { + StmtDeclaration::Wire(v) => v.wire.into(), + StmtDeclaration::Reg(v) => v.reg.into(), + StmtDeclaration::RegSync(v) => v.reg.into(), + StmtDeclaration::RegAsync(v) => v.reg.into(), + StmtDeclaration::Instance(v) => v.instance.into(), + }; + let target = TargetInInstantiatedModule { + instantiated_module: *parent_module, + target: target_base.into(), + }; + self.decl_conditions.insert(target, conditions); + let compiled_value = self.compile_value(target); + match declaration { + StmtDeclaration::Wire(StmtWire { annotations, wire }) => {} + StmtDeclaration::Reg(_) => { + unreachable!("Reset types were already replaced by SyncReset or AsyncReset"); + } + StmtDeclaration::RegSync(stmt_reg) => { + self.compile_stmt_reg(stmt_reg, *parent_module, compiled_value) + } + StmtDeclaration::RegAsync(stmt_reg) => { + self.compile_stmt_reg(stmt_reg, *parent_module, compiled_value) + } + StmtDeclaration::Instance(StmtInstance { + annotations, + instance, + }) => { + let inner_instantiated_module = InstantiatedModule::Child { + parent: parent_module, + instance: instance.intern_sized(), + } + .intern_sized(); + let instance_expr = instance.to_expr(); + self.compile_module(inner_instantiated_module); + for (field_index, module_io) in + instance.instantiated().module_io().into_iter().enumerate() + { + let instance_field = + ops::FieldAccess::new_by_index(instance_expr, field_index).to_expr(); + match Expr::flow(instance_field) { + Flow::Source => { + // we need to supply the value to the instance since the + // parent module expects to read from the instance + self.compile_connect( + *parent_module, + conditions, + instance_field, + *inner_instantiated_module, + Interned::default(), + module_io.module_io.to_expr(), + instance.source_location(), + ); + } + Flow::Sink => { + // we need to take the value from the instance since the + // parent module expects to write to the instance + self.compile_connect( + *inner_instantiated_module, + Interned::default(), + module_io.module_io.to_expr(), + *parent_module, + conditions, + instance_field, + instance.source_location(), + ); + } + Flow::Duplex => unreachable!(), + } + } + } + } + self.make_trace_decl(*parent_module, target_base) + } + fn allocate_delay_chain( + &mut self, + len: usize, + layout: &TypeLayout, + first: Option, + last: Option, + mut from_allocation: impl FnMut(TypeIndexRange) -> T, + ) -> Vec { + match (len, first, last) { + (0, _, _) => Vec::new(), + (1, Some(v), _) | (1, None, Some(v)) => vec![v], + (2, Some(first), Some(last)) => vec![first, last], + (len, first, last) => { + let inner_len = len - first.is_some() as usize - last.is_some() as usize; + first + .into_iter() + .chain( + (0..inner_len) + .map(|_| from_allocation(self.insns.allocate_variable(layout))), + ) + .chain(last) + .collect() + } + } + } + fn allocate_delay_chain_small( + &mut self, + len: usize, + ty: CanonicalType, + first: Option>, + last: Option>, + ) -> Vec> { + self.allocate_delay_chain( + len, + &TypeLayout { + small_slots: StatePartLayout::scalar( + SlotDebugData { + name: Interned::default(), + ty, + }, + (), + ), + ..TypeLayout::empty() + }, + first, + last, + |range| range.small_slots.start, + ) + } + fn compile_memory_port_rw_helper( + &mut self, + memory: StatePartIndex, + stride: usize, + mut start: usize, + data_layout: CompiledTypeLayout, + mask_layout: CompiledTypeLayout, + mut read: Option>, + mut write: Option>, + ) { + match data_layout.body { + CompiledTypeLayoutBody::Scalar => { + let CompiledTypeLayoutBody::Scalar = mask_layout.body else { + unreachable!(); + }; + let signed = match data_layout.ty { + CanonicalType::UInt(_) => false, + CanonicalType::SInt(_) => true, + CanonicalType::Bool(_) => false, + CanonicalType::Array(_) => unreachable!(), + CanonicalType::Enum(_) => false, + CanonicalType::Bundle(_) => unreachable!(), + CanonicalType::AsyncReset(_) => false, + CanonicalType::SyncReset(_) => false, + CanonicalType::Reset(_) => false, + CanonicalType::Clock(_) => false, + CanonicalType::PhantomConst(_) => unreachable!(), + CanonicalType::DynSimOnly(_) => false, + }; + let width = data_layout.ty.bit_width(); + if let Some(MemoryPortReadInsns { + addr, + en: _, + write_mode: _, + data, + insns, + }) = read + { + insns.push( + match data.len().as_single() { + Some(TypeLenSingle::BigSlot) => { + let dest = data.big_slots.start; + if signed { + Insn::MemoryReadSInt { + dest, + memory, + addr, + stride, + start, + width, + } + } else { + Insn::MemoryReadUInt { + dest, + memory, + addr, + stride, + start, + width, + } + } + } + Some(TypeLenSingle::SmallSlot) => { + let _dest = data.small_slots.start; + todo!("memory ports' data are always big for now"); + } + Some(TypeLenSingle::SimOnlySlot) => { + todo!("memory containing sim-only values"); + } + None => unreachable!(), + } + .into(), + ); + } + if let Some(MemoryPortWriteInsns { + addr, + en: _, + write_mode: _, + data, + mask, + insns, + }) = write + { + let end_label = self.insns.new_label(); + insns.push( + match mask.len().as_single() { + Some(TypeLenSingle::BigSlot) => Insn::BranchIfZero { + target: end_label.0, + value: mask.big_slots.start, + }, + Some(TypeLenSingle::SmallSlot) => Insn::BranchIfSmallZero { + target: end_label.0, + value: mask.small_slots.start, + }, + Some(TypeLenSingle::SimOnlySlot) | None => unreachable!(), + } + .into(), + ); + insns.push( + match data.len().as_single() { + Some(TypeLenSingle::BigSlot) => { + let value = data.big_slots.start; + if signed { + Insn::MemoryWriteSInt { + value, + memory, + addr, + stride, + start, + width, + } + } else { + Insn::MemoryWriteUInt { + value, + memory, + addr, + stride, + start, + width, + } + } + } + Some(TypeLenSingle::SmallSlot) => { + let _value = data.small_slots.start; + todo!("memory ports' data are always big for now"); + } + Some(TypeLenSingle::SimOnlySlot) => { + todo!("memory containing sim-only values"); + } + None => unreachable!(), + } + .into(), + ); + insns.push(end_label.into()); + } + } + CompiledTypeLayoutBody::Array { element } => { + let CompiledTypeLayoutBody::Array { + element: mask_element, + } = mask_layout.body + else { + unreachable!(); + }; + let ty = ::from_canonical(data_layout.ty); + let element_bit_width = ty.element().bit_width(); + let element_size = element.layout.len(); + let mask_element_size = mask_element.layout.len(); + for element_index in 0..ty.len() { + self.compile_memory_port_rw_helper( + memory, + stride, + start, + *element, + *mask_element, + read.as_mut().map( + |MemoryPortReadInsns { + addr, + en, + write_mode, + data, + insns, + }| MemoryPortReadInsns { + addr: *addr, + en: *en, + write_mode: *write_mode, + data: data.index_array(element_size, element_index), + insns, + }, + ), + write.as_mut().map( + |MemoryPortWriteInsns { + addr, + en, + write_mode, + data, + mask, + insns, + }| { + MemoryPortWriteInsns { + addr: *addr, + en: *en, + write_mode: *write_mode, + data: data.index_array(element_size, element_index), + mask: mask.index_array(mask_element_size, element_index), + insns, + } + }, + ), + ); + start += element_bit_width; + } + } + CompiledTypeLayoutBody::Bundle { fields } => { + let CompiledTypeLayoutBody::Bundle { + fields: mask_fields, + } = mask_layout.body + else { + unreachable!(); + }; + assert_eq!(fields.len(), mask_fields.len()); + for (field, mask_field) in fields.into_iter().zip(mask_fields) { + let field_index_range = + TypeIndexRange::new(field.offset, field.ty.layout.len()); + let mask_field_index_range = + TypeIndexRange::new(mask_field.offset, mask_field.ty.layout.len()); + self.compile_memory_port_rw_helper( + memory, + stride, + start, + field.ty, + mask_field.ty, + read.as_mut().map( + |MemoryPortReadInsns { + addr, + en, + write_mode, + data, + insns, + }| MemoryPortReadInsns { + addr: *addr, + en: *en, + write_mode: *write_mode, + data: data.slice(field_index_range), + insns, + }, + ), + write.as_mut().map( + |MemoryPortWriteInsns { + addr, + en, + write_mode, + data, + mask, + insns, + }| { + MemoryPortWriteInsns { + addr: *addr, + en: *en, + write_mode: *write_mode, + data: data.slice(field_index_range), + mask: mask.slice(mask_field_index_range), + insns, + } + }, + ), + ); + start = start + field.ty.ty.bit_width(); + } + } + } + } + fn compile_memory_port_rw( + &mut self, + memory: StatePartIndex, + data_layout: CompiledTypeLayout, + mask_layout: CompiledTypeLayout, + mut read: Option>, + mut write: Option>, + ) { + let read_else_label = read.as_mut().map( + |MemoryPortReadInsns { + addr: _, + en, + write_mode, + data: _, + insns, + }| { + let else_label = self.insns.new_label(); + insns.push( + Insn::BranchIfSmallZero { + target: else_label.0, + value: *en, + } + .into(), + ); + if let Some(write_mode) = *write_mode { + insns.push( + Insn::BranchIfSmallNonZero { + target: else_label.0, + value: write_mode, + } + .into(), + ); + } + else_label + }, + ); + let write_end_label = write.as_mut().map( + |MemoryPortWriteInsns { + addr: _, + en, + write_mode, + data: _, + mask: _, + insns, + }| { + let end_label = self.insns.new_label(); + insns.push( + Insn::BranchIfSmallZero { + target: end_label.0, + value: *en, + } + .into(), + ); + if let Some(write_mode) = *write_mode { + insns.push( + Insn::BranchIfSmallZero { + target: end_label.0, + value: write_mode, + } + .into(), + ); + } + end_label + }, + ); + self.compile_memory_port_rw_helper( + memory, + data_layout.ty.bit_width(), + 0, + data_layout, + mask_layout, + read.as_mut().map( + |MemoryPortReadInsns { + addr, + en, + write_mode, + data, + insns, + }| MemoryPortReadInsns { + addr: *addr, + en: *en, + write_mode: *write_mode, + data: *data, + insns: *insns, + }, + ), + write.as_mut().map( + |MemoryPortWriteInsns { + addr, + en, + write_mode, + data, + mask, + insns, + }| MemoryPortWriteInsns { + addr: *addr, + en: *en, + write_mode: *write_mode, + data: *data, + mask: *mask, + insns: *insns, + }, + ), + ); + if let ( + Some(else_label), + Some(MemoryPortReadInsns { + addr: _, + en: _, + write_mode: _, + data, + insns, + }), + ) = (read_else_label, read) + { + let end_label = self.insns.new_label(); + insns.push( + Insn::Branch { + target: end_label.0, + } + .into(), + ); + insns.push(else_label.into()); + let TypeIndexRange { + small_slots, + big_slots, + sim_only_slots, + } = data; + for dest in small_slots.iter() { + insns.push(Insn::ConstSmall { dest, value: 0 }.into()); + } + for dest in big_slots.iter() { + insns.push( + Insn::Const { + dest, + value: BigInt::ZERO.intern_sized(), + } + .into(), + ); + } + for _dest in sim_only_slots.iter() { + todo!("memory containing sim-only values"); + } + insns.push(end_label.into()); + } + if let (Some(end_label), Some(write)) = (write_end_label, write) { + write.insns.push(end_label.into()); + } + } + fn compile_memory( + &mut self, + mem: Mem, + instantiated_module: InstantiatedModule, + conditions: Interned<[Cond]>, + trace_decls: &mut Vec, + ) { + let data_layout = CompiledTypeLayout::get(mem.array_type().element()); + let mask_layout = CompiledTypeLayout::get(mem.array_type().element().mask_type()); + let read_latency_plus_1 = mem + .read_latency() + .checked_add(1) + .expect("read latency too big"); + let write_latency_plus_1 = mem + .write_latency() + .get() + .checked_add(1) + .expect("write latency too big"); + let read_cycle = match mem.read_under_write() { + ReadUnderWrite::Old => 0, + ReadUnderWrite::New => mem.read_latency(), + ReadUnderWrite::Undefined => mem.read_latency() / 2, // something other than Old or New + }; + let memory = self + .insns + .state_layout + .memories + .allocate(&StatePartLayout::scalar( + (), + MemoryData { + array_type: mem.array_type(), + data: mem.initial_value().unwrap_or_else(|| { + Intern::intern_owned(BitVec::repeat( + false, + mem.array_type().type_properties().bit_width, + )) + }), + }, + )) + .start; + let (ports, trace_ports) = mem + .ports() + .iter() + .map(|&port| { + let target_base = TargetBase::MemPort(port); + let target = TargetInInstantiatedModule { + instantiated_module, + target: target_base.into(), + }; + self.decl_conditions.insert(target, conditions); + let TraceDecl::Scope(TraceScope::MemPort(trace_port)) = + self.make_trace_decl(instantiated_module, target_base) + else { + unreachable!(); + }; + let clk = Expr::field(port.to_expr(), "clk"); + let clk = self.compile_expr(instantiated_module, clk); + let clk = self.compiled_expr_to_value(clk, mem.source_location()); + let clk_triggered = self + .compile_clock(clk.map_ty(Clock::from_canonical), mem.source_location()) + .clk_triggered; + let en = Expr::field(port.to_expr(), "en"); + let en = self.compile_expr(instantiated_module, en); + let en = self.compiled_expr_to_value(en, mem.source_location()); + let en = self.compiled_value_bool_dest_is_small(en, mem.source_location()); + let addr = Expr::field(port.to_expr(), "addr"); + let addr = self.compile_expr(instantiated_module, addr); + let addr = self.compiled_expr_to_value(addr, mem.source_location()); + let addr_ty = addr.layout.ty; + let addr = self.compiled_value_to_dyn_array_index( + addr.map_ty(UInt::from_canonical), + mem.source_location(), + ); + let read_data = port.port_kind().rdata_name().map(|name| { + let read_data = + self.compile_expr(instantiated_module, Expr::field(port.to_expr(), name)); + let read_data = self.compiled_expr_to_value(read_data, mem.source_location()); + read_data.range + }); + let write_data = port.port_kind().wdata_name().map(|name| { + let write_data = + self.compile_expr(instantiated_module, Expr::field(port.to_expr(), name)); + let write_data = self.compiled_expr_to_value(write_data, mem.source_location()); + write_data.range + }); + let write_mask = port.port_kind().wmask_name().map(|name| { + let write_mask = + self.compile_expr(instantiated_module, Expr::field(port.to_expr(), name)); + let write_mask = self.compiled_expr_to_value(write_mask, mem.source_location()); + write_mask.range + }); + let write_mode = port.port_kind().wmode_name().map(|name| { + let write_mode = + self.compile_expr(instantiated_module, Expr::field(port.to_expr(), name)); + let write_mode = self.compiled_expr_to_value(write_mode, mem.source_location()); + self.compiled_value_bool_dest_is_small(write_mode, mem.source_location()) + }); + struct PortParts { + en_delayed_len: usize, + addr_delayed_len: usize, + read_data_delayed_len: usize, + write_data_delayed_len: usize, + write_mask_delayed_len: usize, + write_mode_delayed_len: usize, + read_cycle: Option, + write_cycle: Option, + } + let PortParts { + en_delayed_len, + addr_delayed_len, + read_data_delayed_len, + write_data_delayed_len, + write_mask_delayed_len, + write_mode_delayed_len, + read_cycle, + write_cycle, + } = match port.port_kind() { + PortKind::ReadOnly => PortParts { + en_delayed_len: read_cycle + 1, + addr_delayed_len: read_cycle + 1, + read_data_delayed_len: read_latency_plus_1 - read_cycle, + write_data_delayed_len: 0, + write_mask_delayed_len: 0, + write_mode_delayed_len: 0, + read_cycle: Some(read_cycle), + write_cycle: None, + }, + PortKind::WriteOnly => PortParts { + en_delayed_len: write_latency_plus_1, + addr_delayed_len: write_latency_plus_1, + read_data_delayed_len: 0, + write_data_delayed_len: write_latency_plus_1, + write_mask_delayed_len: write_latency_plus_1, + write_mode_delayed_len: 0, + read_cycle: None, + write_cycle: Some(mem.write_latency().get()), + }, + PortKind::ReadWrite => { + let can_rw_at_end = match mem.read_under_write() { + ReadUnderWrite::Old => false, + ReadUnderWrite::New | ReadUnderWrite::Undefined => true, + }; + let latency_plus_1 = read_latency_plus_1; + if latency_plus_1 != write_latency_plus_1 || !can_rw_at_end { + todo!( + "not sure what to do, issue: \ + https://github.com/chipsalliance/firrtl-spec/issues/263" + ); + } + PortParts { + en_delayed_len: latency_plus_1, + addr_delayed_len: latency_plus_1, + read_data_delayed_len: 1, + write_data_delayed_len: latency_plus_1, + write_mask_delayed_len: latency_plus_1, + write_mode_delayed_len: latency_plus_1, + read_cycle: Some(latency_plus_1 - 1), + write_cycle: Some(latency_plus_1 - 1), + } + } + }; + let addr_delayed = self.allocate_delay_chain_small( + addr_delayed_len, + addr_ty.canonical(), + Some(addr), + None, + ); + let en_delayed = self.allocate_delay_chain_small( + en_delayed_len, + Bool.canonical(), + Some(en), + None, + ); + let read_data_delayed = self.allocate_delay_chain( + read_data_delayed_len, + &data_layout.layout, + None, + read_data, + |v| v, + ); + let write_data_delayed = self.allocate_delay_chain( + write_data_delayed_len, + &data_layout.layout, + write_data, + None, + |v| v, + ); + let write_mask_delayed = self.allocate_delay_chain( + write_mask_delayed_len, + &mask_layout.layout, + write_mask, + None, + |v| v, + ); + let write_mode_delayed = self.allocate_delay_chain_small( + write_mode_delayed_len, + Bool.canonical(), + write_mode, + None, + ); + let mut read_insns = Vec::new(); + let mut write_insns = Vec::new(); + self.compile_memory_port_rw( + memory, + data_layout, + mask_layout, + read_cycle.map(|read_cycle| MemoryPortReadInsns { + addr: addr_delayed[read_cycle], + en: en_delayed[read_cycle], + write_mode: write_mode_delayed.get(read_cycle).copied(), + data: read_data_delayed[0], + insns: &mut read_insns, + }), + write_cycle.map(|write_cycle| MemoryPortWriteInsns { + addr: addr_delayed[write_cycle], + en: en_delayed[write_cycle], + write_mode: write_mode_delayed.get(write_cycle).copied(), + data: write_data_delayed[write_cycle], + mask: write_mask_delayed[write_cycle], + insns: &mut write_insns, + }), + ); + self.add_assignment(Interned::default(), read_insns, mem.source_location()); + ( + MemoryPort { + clk_triggered, + addr_delayed, + en_delayed, + data_layout, + read_data_delayed, + write_data_delayed, + write_mask_delayed, + write_mode_delayed, + write_insns, + }, + trace_port, + ) + }) + .unzip(); + let name = mem.scoped_name().1.0; + let id = TraceMemoryId(self.memories.len()); + let stride = mem.array_type().element().bit_width(); + let trace = TraceMem { + id, + name, + stride, + element_type: self + .make_trace_decl_child( + instantiated_module, + MakeTraceDeclTarget::Memory { + id, + depth: mem.array_type().len(), + stride, + start: 0, + ty: mem.array_type().element(), + }, + name, + mem.source_location(), + ) + .intern_sized(), + ports: Intern::intern_owned(trace_ports), + array_type: mem.array_type(), + }; + trace_decls.push(trace.into()); + self.memories.push(Memory { + mem, + memory, + trace, + ports, + }); + } + fn compile_block( + &mut self, + parent_module: Interned, + block: Block, + conditions: Interned<[Cond]>, + trace_decls: &mut Vec, + ) { + let Block { memories, stmts } = block; + for memory in memories { + self.compile_memory(memory, *parent_module, conditions, trace_decls); + } + for stmt in stmts { + match stmt { + Stmt::Connect(StmtConnect { + lhs, + rhs, + source_location, + }) => self.compile_connect( + *parent_module, + conditions, + lhs, + *parent_module, + conditions, + rhs, + source_location, + ), + Stmt::Formal(StmtFormal { .. }) => todo!("implement simulating formal statements"), + Stmt::If(StmtIf { + cond, + source_location, + blocks: [then_block, else_block], + }) => { + let cond = self.compile_expr(*parent_module, Expr::canonical(cond)); + let cond = self.compiled_expr_to_value(cond, source_location); + let cond = cond.map_ty(Bool::from_canonical); + self.compile_block( + parent_module, + then_block, + Interned::from_iter(conditions.iter().copied().chain([Cond { + body: CondBody::IfTrue { cond }, + source_location, + }])), + trace_decls, + ); + self.compile_block( + parent_module, + else_block, + Interned::from_iter(conditions.iter().copied().chain([Cond { + body: CondBody::IfFalse { cond }, + source_location, + }])), + trace_decls, + ); + } + Stmt::Match(StmtMatch { + expr, + source_location, + blocks, + }) => { + let enum_expr = self.compile_expr(*parent_module, Expr::canonical(expr)); + let enum_expr = self.compiled_expr_to_value(enum_expr, source_location); + let enum_expr = enum_expr.map_ty(Enum::from_canonical); + let discriminant = self.compile_enum_discriminant(enum_expr, source_location); + for (variant_index, block) in blocks.into_iter().enumerate() { + self.compile_block( + parent_module, + block, + Interned::from_iter(conditions.iter().copied().chain([Cond { + body: CondBody::MatchArm { + discriminant, + variant_index, + }, + source_location, + }])), + trace_decls, + ); + } + } + Stmt::Declaration(declaration) => { + trace_decls.push(self.compile_declaration( + declaration, + parent_module, + conditions, + )); + } + } + } + } + fn compile_module(&mut self, module: Interned) -> &CompiledModule { + let mut trace_decls = Vec::new(); + let module_io = module + .leaf_module() + .module_io() + .iter() + .map( + |&AnnotatedModuleIO { + annotations: _, + module_io, + }| { + let target = TargetInInstantiatedModule { + instantiated_module: *module, + target: Target::from(module_io), + }; + self.decl_conditions.insert(target, Interned::default()); + trace_decls.push(self.make_trace_decl(*module, module_io.into())); + self.compile_value(target) + }, + ) + .collect(); + match module.leaf_module().body() { + ModuleBody::Normal(NormalModuleBody { body }) => { + self.compile_block(module, body, Interned::default(), &mut trace_decls); + } + ModuleBody::Extern(ExternModuleBody { + verilog_name: _, + parameters: _, + simulation, + }) => { + let Some(simulation) = simulation else { + panic!( + "can't simulate extern module without extern_module_simulation: {}", + module.leaf_module().source_location() + ); + }; + let module_io_targets = module + .leaf_module() + .module_io() + .iter() + .map(|v| { + Target::from(*simulation.sim_io_to_generator_map[&v.module_io.intern()]) + }) + .collect(); + self.extern_modules.push(CompiledExternModule { + module_io_targets, + module_io, + simulation, + }); + } + } + let hashbrown::hash_map::Entry::Vacant(entry) = self.modules.entry(*module) else { + unreachable!("compiled same instantiated module twice"); + }; + entry.insert(CompiledModule { + module_io, + trace_decls: TraceModule { + name: module.leaf_module().name(), + children: Intern::intern_owned(trace_decls), + }, + }) + } + fn process_clocks(&mut self) -> Interned<[StatePartIndex]> { + mem::take(&mut self.clock_triggers) + .into_iter() + .map( + |ClockTrigger { + last_clk_was_low, + clk, + clk_triggered, + source_location, + }| { + self.insns.push( + Insn::XorSmallImmediate { + dest: last_clk_was_low, + lhs: clk, + rhs: 1, + }, + source_location, + ); + clk_triggered + }, + ) + .collect() + } + fn process_registers(&mut self) { + for Register { + value, + clk_triggered, + reset, + source_location, + } in mem::take(&mut self.registers) + { + match reset { + Some(RegisterReset { + is_async, + init, + rst, + }) => { + let reg_end = self.insns.new_label(); + let reg_reset = self.insns.new_label(); + let branch_if_reset = Insn::BranchIfSmallNonZero { + target: reg_reset.0, + value: rst, + }; + let branch_if_not_triggered = Insn::BranchIfSmallZero { + target: reg_end.0, + value: clk_triggered, + }; + if is_async { + self.insns.push(branch_if_reset, source_location); + self.insns.push(branch_if_not_triggered, source_location); + } else { + self.insns.push(branch_if_not_triggered, source_location); + self.insns.push(branch_if_reset, source_location); + } + self.insns.extend( + value.range.insns_for_copy_from(value.write_value().range), + source_location, + ); + self.insns + .push(Insn::Branch { target: reg_end.0 }, source_location); + self.insns.define_label_at_next_insn(reg_reset); + self.insns + .extend(value.range.insns_for_copy_from(init.range), source_location); + self.insns.define_label_at_next_insn(reg_end); + } + None => { + let reg_end = self.insns.new_label(); + self.insns.push( + Insn::BranchIfSmallZero { + target: reg_end.0, + value: clk_triggered, + }, + source_location, + ); + self.insns.extend( + value.range.insns_for_copy_from(value.write_value().range), + source_location, + ); + self.insns.define_label_at_next_insn(reg_end); + } + } + } + } + fn process_memories(&mut self) { + for memory_index in 0..self.memories.len() { + let Memory { + mem, + memory: _, + trace: _, + ref mut ports, + } = self.memories[memory_index]; + for MemoryPort { + clk_triggered, + addr_delayed, + en_delayed, + data_layout: _, + read_data_delayed, + write_data_delayed, + write_mask_delayed, + write_mode_delayed, + write_insns, + } in mem::take(ports) + { + let port_end = self.insns.new_label(); + let small_shift_reg = + |this: &mut Self, values: &[StatePartIndex]| { + for pair in values.windows(2).rev() { + this.insns.push( + Insn::CopySmall { + dest: pair[1], + src: pair[0], + }, + mem.source_location(), + ); + } + }; + let shift_reg = |this: &mut Self, values: &[TypeIndexRange]| { + for pair in values.windows(2).rev() { + this.insns + .extend(pair[0].insns_for_copy_to(pair[1]), mem.source_location()); + } + }; + self.insns.push( + Insn::BranchIfSmallZero { + target: port_end.0, + value: clk_triggered, + }, + mem.source_location(), + ); + small_shift_reg(self, &addr_delayed); + small_shift_reg(self, &en_delayed); + shift_reg(self, &write_data_delayed); + shift_reg(self, &write_mask_delayed); + small_shift_reg(self, &write_mode_delayed); + shift_reg(self, &read_data_delayed); + self.insns.extend(write_insns, mem.source_location()); + self.insns.define_label_at_next_insn(port_end); + } + } + } + pub fn compile(mut self) -> Compiled { + let base_module = + *self.compile_module(InstantiatedModule::Base(self.base_module).intern_sized()); + self.process_assignments(); + self.process_registers(); + self.process_memories(); + let clocks_triggered = self.process_clocks(); + self.insns + .push(Insn::Return, self.base_module.source_location()); + Compiled { + insns: Insns::from(self.insns).intern_sized(), + base_module, + extern_modules: Intern::intern_owned(self.extern_modules), + io: Instance::new_unchecked( + ScopedNameId( + NameId("".intern(), Id::new()), + self.original_base_module.name_id(), + ), + self.original_base_module, + self.original_base_module.source_location(), + ), + traces: SimTraces(Intern::intern_owned(self.traces.0)), + trace_memories: Interned::from_iter(self.memories.iter().map( + |&Memory { + mem: _, + memory, + trace, + ports: _, + }| (memory, trace), + )), + clocks_triggered, + } + } +} + +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +pub(crate) struct CompiledModule { + pub(crate) module_io: Interned<[CompiledValue]>, + pub(crate) trace_decls: TraceModule, +} + +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +pub struct Compiled { + pub(crate) insns: Interned>, + pub(crate) base_module: CompiledModule, + pub(crate) extern_modules: Interned<[CompiledExternModule]>, + pub(crate) io: Instance, + pub(crate) traces: SimTraces]>>, + pub(crate) trace_memories: Interned<[(StatePartIndex, TraceMem)]>, + pub(crate) clocks_triggered: Interned<[StatePartIndex]>, +} + +impl Compiled { + pub fn new(module: Interned>) -> Self { + Self::from_canonical(Compiler::new(module.canonical().intern()).compile()) + } + pub fn canonical(self) -> Compiled { + let Self { + insns, + base_module, + extern_modules, + io, + traces, + trace_memories, + clocks_triggered, + } = self; + Compiled { + insns, + base_module, + extern_modules, + io: Instance::from_canonical(io.canonical()), + traces, + trace_memories, + clocks_triggered, + } + } + pub fn from_canonical(canonical: Compiled) -> Self { + let Compiled { + insns, + base_module, + extern_modules, + io, + traces, + trace_memories, + clocks_triggered, + } = canonical; + Self { + insns, + base_module, + extern_modules, + io: Instance::from_canonical(io.canonical()), + traces, + trace_memories, + clocks_triggered, + } + } +} diff --git a/crates/fayalite/src/sim/interpreter.rs b/crates/fayalite/src/sim/interpreter.rs index 22f6f5f..1a6c269 100644 --- a/crates/fayalite/src/sim/interpreter.rs +++ b/crates/fayalite/src/sim/interpreter.rs @@ -2,28 +2,31 @@ // See Notices.txt for copyright information use crate::{ - array::Array, int::{BoolOrIntType, SInt, UInt}, intern::{Intern, Interned, Memoize}, + sim::interpreter::parts::{ + StateLayout, StatePartIndex, StatePartKind, StatePartKindBigSlots, StatePartKindMemories, + StatePartKindSimOnlySlots, StatePartKindSmallSlots, StatePartLen, TypeIndexRange, + TypeLayout, get_state_part_kinds, + }, source_location::SourceLocation, - ty::CanonicalType, - util::get_many_mut, + util::{HashMap, HashSet}, }; -use bitvec::{boxed::BitBox, slice::BitSlice}; -use hashbrown::{HashMap, HashSet}; +use bitvec::slice::BitSlice; use num_bigint::BigInt; use num_traits::{One, Signed, ToPrimitive, Zero}; use std::{ - any::TypeId, borrow::BorrowMut, convert::Infallible, fmt::{self, Write}, - hash::{Hash, Hasher}, + hash::Hash, marker::PhantomData, ops::{ControlFlow, Deref, DerefMut, Index, IndexMut}, }; use vec_map::VecMap; +pub(crate) mod parts; + pub(crate) type SmallUInt = u64; pub(crate) type SmallSInt = i64; pub(crate) const MIN_BITS_FOR_NEEDING_BIG: usize = SmallUInt::BITS as usize + 1; @@ -111,19 +114,33 @@ macro_rules! insn_field_enum { }; } -insn_field_enum! { - pub(crate) enum InsnFieldType { - Memory(Transform::Type>), - SmallSlot(Transform::Type>), - BigSlot(Transform::Type>), - SmallSlotArrayIndexed(Transform::Type>), - BigSlotArrayIndexed(Transform::Type>), - SmallUInt(Transform::Type), - SmallSInt(Transform::Type), - InternedBigInt(Transform::Type>), - U8(Transform::Type), - USize(Transform::Type), - Empty(Transform::Type<[(); 0]>), +macro_rules! insn_field_enum2 { + ( + type_singular_variants = [$($type_singular_variant:ident,)*]; + array_indexed_variants = [$($array_indexed_variant:ident,)*]; + type_kinds = [$($type_kind:ident,)*]; + ) => { + insn_field_enum! { + pub(crate) enum InsnFieldType { + Memory(Transform::Type>), + $($type_singular_variant(Transform::Type>),)* + $($array_indexed_variant(Transform::Type>),)* + SmallUInt(Transform::Type), + SmallSInt(Transform::Type), + InternedBigInt(Transform::Type>), + U8(Transform::Type), + USize(Transform::Type), + Empty(Transform::Type<[(); 0]>), + } + } + }; +} + +get_state_part_kinds! { + insn_field_enum2! { + type_singular_variants; + array_indexed_variants; + type_kinds; } } @@ -252,8 +269,10 @@ impl Insn { InsnFieldType::Memory(_) | InsnFieldType::SmallSlot(_) | InsnFieldType::BigSlot(_) + | InsnFieldType::SimOnlySlot(_) | InsnFieldType::SmallSlotArrayIndexed(_) | InsnFieldType::BigSlotArrayIndexed(_) + | InsnFieldType::SimOnlySlotArrayIndexed(_) | InsnFieldType::SmallUInt(_) | InsnFieldType::SmallSInt(_) | InsnFieldType::InternedBigInt(_) @@ -280,12 +299,18 @@ impl Insn { InsnFieldType::BigSlot(v) => { debug_fmt_state_part!(v)?; } + InsnFieldType::SimOnlySlot(v) => { + debug_fmt_state_part!(v)?; + } InsnFieldType::SmallSlotArrayIndexed(v) => { debug_fmt_state_part!(v)?; } InsnFieldType::BigSlotArrayIndexed(v) => { debug_fmt_state_part!(v)?; } + InsnFieldType::SimOnlySlotArrayIndexed(v) => { + debug_fmt_state_part!(v)?; + } InsnFieldType::SmallUInt(v) => write!(f, "{v:#x}")?, InsnFieldType::SmallSInt(v) => write!(f, "{v:#x}")?, InsnFieldType::InternedBigInt(v) => write!(f, "{v:#x}")?, @@ -775,911 +800,6 @@ impl fmt::Debug for Insns { } } -pub(crate) trait StatePartKind: - Send + Sync + Ord + Hash + fmt::Debug + 'static + Copy + Default -{ - const NAME: &'static str; - type DebugData: Send + Sync + Eq + Hash + fmt::Debug + 'static + Copy; - type LayoutData: Send + Sync + Eq + Hash + fmt::Debug + 'static + Copy; - type State: fmt::Debug + 'static + Clone; - type BorrowedState<'a>: 'a; - fn new_state(layout_data: &[Self::LayoutData]) -> Self::State; - fn borrow_state<'a>(state: &'a mut Self::State) -> Self::BorrowedState<'a>; - fn part_debug_data( - state_layout: &StateLayout, - part_index: StatePartIndex, - ) -> Option<&Self::DebugData>; - fn debug_fmt_state_value( - state: &State, - index: StatePartIndex, - f: &mut impl fmt::Write, - ) -> fmt::Result; -} - -pub(crate) trait StatePartsValue { - type Value; -} - -macro_rules! impl_state_parts_traits { - ( - struct $Struct:ident<$V:ident: $StatePartsValue:ident> { - $(#[flatten] - $flattened_field:ident: $flattened_field_ty:ty, - $(#[field_in_flattened] - $field_in_flattened:ident: $field_in_flattened_ty:ty,)* - )? - $($field:ident: $field_ty:ty,)* - } - ) => { - impl<$V: $StatePartsValue> Copy for $Struct<$V> - where - $($flattened_field_ty: Copy,)? - $($field_ty: Copy,)* - { - } - - impl<$V: $StatePartsValue> Clone for $Struct<$V> - where - $($flattened_field_ty: Clone,)? - $($field_ty: Clone,)* - { - fn clone(&self) -> Self { - Self { - $($flattened_field: self.$flattened_field.clone(),)? - $($field: self.$field.clone(),)* - } - } - } - - impl<$V: $StatePartsValue> fmt::Debug for $Struct<$V> - where - $($($field_in_flattened_ty: fmt::Debug,)*)? - $($field_ty: fmt::Debug,)* - { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct(stringify!($Struct)) - $($(.field(stringify!($field_in_flattened), &self.$flattened_field.$field_in_flattened))*)? - $(.field(stringify!($field), &self.$field))* - .finish() - } - } - - impl<$V: $StatePartsValue> PartialEq for $Struct<$V> - where - $($flattened_field_ty: PartialEq,)? - $($field_ty: PartialEq,)* - { - fn eq(&self, other: &Self) -> bool { - true $(&& self.$flattened_field == other.$flattened_field)? $(&& self.$field == other.$field)* - } - } - - impl<$V: $StatePartsValue> Eq for $Struct<$V> - where - $($flattened_field_ty: Eq,)? - $($field_ty: Eq,)* - { - } - - impl<$V: $StatePartsValue> Hash for $Struct<$V> - where - $($flattened_field_ty: Hash,)? - $($field_ty: Hash,)* - { - fn hash(&self, h: &mut H) { - $(self.$flattened_field.hash(h);)? - $(self.$field.hash(h);)* - } - } - - impl<$V: $StatePartsValue> Default for $Struct<$V> - where - $($flattened_field_ty: Default,)? - $($field_ty: Default,)* - { - fn default() -> Self { - Self { - $($flattened_field: Default::default(),)? - $($field: Default::default(),)* - } - } - } - }; -} - -macro_rules! make_state_part_kinds { - ( - $( - #[state, field = $state_field:ident] - impl $StateStatePartKind:ident for $StateKind:ident $state_impl_body:tt - )* - $( - #[type, field = $type_field:ident] - impl $TypeStatePartKind:ident for $TypeKind:ident $type_impl_body:tt - )* - ) => { - $( - #[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Clone, Copy, Default)] - pub(crate) struct $StateKind; - - impl $StateStatePartKind for $StateKind $state_impl_body - )* - - $( - #[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Clone, Copy, Default)] - pub(crate) struct $TypeKind; - - impl $TypeStatePartKind for $TypeKind $type_impl_body - )* - - pub(crate) struct StateParts { - pub(crate) ty: TypeParts, - $(pub(crate) $state_field: V::Value<$StateKind>,)* - } - - impl_state_parts_traits! { - struct StateParts { - #[flatten] - ty: TypeParts, - $(#[field_in_flattened] - $type_field: V::Value<$TypeKind>,)* - $($state_field: V::Value<$StateKind>,)* - } - } - - pub(crate) struct TypeParts { - $(pub(crate) $type_field: V::Value<$TypeKind>,)* - } - - impl_state_parts_traits! { - struct TypeParts { - $($type_field: V::Value<$TypeKind>,)* - } - } - - #[derive(Clone, PartialEq, Eq, Hash, Debug)] - pub(crate) struct StateLayout { - pub(crate) ty: TypeLayout, - $(pub(crate) $state_field: StatePartLayout<$StateKind, BK>,)* - } - - impl Copy for StateLayout {} - - impl StateLayout { - pub(crate) fn len(&self) -> StateLen { - StateLen { - ty: self.ty.len(), - $($state_field: self.$state_field.len(),)* - } - } - pub(crate) fn is_empty(&self) -> bool { - self.ty.is_empty() $(&& self.$state_field.is_empty())* - } - pub(crate) fn empty() -> Self { - Self { - ty: TypeLayout::empty(), - $($state_field: StatePartLayout::empty(),)* - } - } - } - - impl StateLayout { - pub(crate) fn next_index(&self) -> StateIndex { - StateIndex { - ty: self.ty.next_index(), - $($state_field: self.$state_field.next_index(),)* - } - } - pub(crate) fn allocate( - &mut self, - layout: &StateLayout, - ) -> StateIndexRange { - StateIndexRange { - ty: self.ty.allocate(&layout.ty), - $($state_field: self.$state_field.allocate(&layout.$state_field),)* - } - } - } - - impl From> for StateLayout { - fn from(v: StateLayout) -> Self { - Self { - ty: v.ty.into(), - $($state_field: v.$state_field.into(),)* - } - } - } - - #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] - pub(crate) struct StateIndexRange { - pub(crate) ty: TypeIndexRange, - $(pub(crate) $state_field: StatePartIndexRange<$StateKind>,)* - } - - impl StateIndexRange { - pub(crate) fn start(self) -> StateIndex { - StateIndex { - ty: self.ty.start(), - $($state_field: self.$state_field.start(),)* - } - } - pub(crate) fn len(self) -> StateLen { - StateLen { - ty: self.ty.len(), - $($state_field: self.$state_field.len(),)* - } - } - pub(crate) fn is_empty(self) -> bool { - self.ty.is_empty() $(&& self.$state_field.is_empty())* - } - } - - #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] - pub(crate) struct StateLen { - pub(crate) ty: TypeLen, - $(pub(crate) $state_field: StatePartLen<$StateKind>,)* - } - - #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] - pub(crate) struct StateIndex { - pub(crate) ty: TypeIndex, - $(pub(crate) $state_field: StatePartIndex<$StateKind>,)* - } - - #[derive(Clone, PartialEq, Eq, Hash, Debug)] - pub(crate) struct TypeLayout { - $(pub(crate) $type_field: StatePartLayout<$TypeKind, BK>,)* - } - - impl Copy for TypeLayout {} - - impl TypeLayout { - pub(crate) fn len(&self) -> TypeLen { - TypeLen { - $($type_field: self.$type_field.len(),)* - } - } - pub(crate) fn is_empty(&self) -> bool { - $(self.$type_field.is_empty())&&+ - } - pub(crate) fn empty() -> Self { - Self { - $($type_field: StatePartLayout::empty(),)* - } - } - } - - impl TypeLayout { - pub(crate) fn next_index(&self) -> TypeIndex { - TypeIndex { - $($type_field: self.$type_field.next_index(),)* - } - } - pub(crate) fn allocate( - &mut self, - layout: &TypeLayout, - ) -> TypeIndexRange { - TypeIndexRange { - $($type_field: self.$type_field.allocate(&layout.$type_field),)* - } - } - } - - impl From> for TypeLayout { - fn from(v: TypeLayout) -> Self { - Self { - $($type_field: v.$type_field.into(),)* - } - } - } - - #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] - pub(crate) struct TypeIndexRange { - $(pub(crate) $type_field: StatePartIndexRange<$TypeKind>,)* - } - - impl TypeIndexRange { - pub(crate) fn new(start: TypeIndex, len: TypeLen) -> Self { - Self { - $($type_field: StatePartIndexRange { - start: start.$type_field, - len: len.$type_field, - },)* - } - } - pub(crate) fn start(self) -> TypeIndex { - TypeIndex { - $($type_field: self.$type_field.start(),)* - } - } - pub(crate) fn len(self) -> TypeLen { - TypeLen { - $($type_field: self.$type_field.len(),)* - } - } - pub(crate) fn is_empty(self) -> bool { - $(self.$type_field.is_empty()) &&+ - } - pub(crate) fn slice(self, index: TypeIndexRange) -> Self { - Self { - $($type_field: self.$type_field.slice(index.$type_field),)* - } - } - pub(crate) fn index_array(self, element_size: TypeLen, index: usize) -> Self { - Self { - $($type_field: self.$type_field.index_array(element_size.$type_field, index),)* - } - } - pub(crate) fn offset(self, offset: TypeIndex) -> Self { - Self { - $($type_field: self.$type_field.offset(offset.$type_field),)* - } - } - } - - #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] - pub(crate) struct TypeLen { - $(pub(crate) $type_field: StatePartLen<$TypeKind>,)* - } - - impl TypeLen { - pub(crate) const fn as_index(self) -> TypeIndex { - TypeIndex { - $($type_field: self.$type_field.as_index(),)* - } - } - } - - #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] - pub(crate) struct TypeIndex { - $(pub(crate) $type_field: StatePartIndex<$TypeKind>,)* - } - - impl TypeIndex { - pub(crate) const ZERO: Self = Self { - $($type_field: StatePartIndex::ZERO,)* - }; - pub(crate) fn offset(self, offset: TypeIndex) -> Self { - Self { - $($type_field: self.$type_field.offset(offset.$type_field),)* - } - } - } - - pub(crate) struct State { - pub(crate) insns: Interned>, - pub(crate) pc: usize, - pub(crate) memory_write_log: Vec<(StatePartIndex, usize)>, - $(pub(crate) $state_field: StatePart<$StateKind>,)* - $(pub(crate) $type_field: StatePart<$TypeKind>,)* - } - - impl fmt::Debug for State { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let Self { - insns: _, - pc, - memory_write_log, - $($state_field,)* - $($type_field,)* - } = self; - f.debug_struct("State") - .field("insns", &InsnsOfState(self)) - .field("pc", pc) - .field("memory_write_log", memory_write_log) - $(.field(stringify!($state_field), $state_field))* - $(.field(stringify!($type_field), $type_field))* - .finish() - } - } - - impl State { - pub(crate) fn new(insns: Interned>) -> Self { - Self { - insns, - pc: 0, - memory_write_log: Vec::with_capacity(32), - $($state_field: StatePart::new(&insns.state_layout.$state_field.layout_data),)* - $($type_field: StatePart::new(&insns.state_layout.ty.$type_field.layout_data),)* - } - } - pub(crate) fn borrow(&mut self) -> BorrowedState<'_> { - BorrowedState { - orig_insns: self.insns, - insns: &self.insns.insns, - pc: self.pc, - orig_pc: &mut self.pc, - memory_write_log: &mut self.memory_write_log, - $($state_field: self.$state_field.borrow(),)* - $($type_field: self.$type_field.borrow(),)* - } - } - } - - #[derive(Debug)] - pub(crate) struct BorrowedState<'a> { - pub(crate) orig_insns: Interned>, - pub(crate) insns: &'a [Insn], - pub(crate) orig_pc: &'a mut usize, - pub(crate) pc: usize, - pub(crate) memory_write_log: &'a mut Vec<(StatePartIndex, usize)>, - $(pub(crate) $state_field: BorrowedStatePart<'a, $StateKind>,)* - $(pub(crate) $type_field: BorrowedStatePart<'a, $TypeKind>,)* - } - - impl Drop for BorrowedState<'_> { - fn drop(&mut self) { - *self.orig_pc = self.pc; - } - } - - #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, Default)] - pub(crate) struct TypeArrayIndexes { - $(pub(crate) $type_field: Interned<[StatePartArrayIndex<$TypeKind>]>,)* - } - - impl TypeArrayIndexes { - pub(crate) fn as_ref(&self) -> TypeArrayIndexesRef<'_> { - TypeArrayIndexesRef { - $($type_field: &self.$type_field,)* - } - } - #[must_use] - pub(crate) fn join(self, next: TypeArrayIndex) -> TypeArrayIndexes { - TypeArrayIndexes { - $($type_field: Interned::from_iter(self.$type_field.iter().copied().chain([next.$type_field])),)* - } - } - } - - #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] - pub(crate) struct TypeArrayIndex { - $(pub(crate) $type_field: StatePartArrayIndex<$TypeKind>,)* - } - - impl TypeArrayIndex { - pub(crate) fn from_parts(index: StatePartIndex, len: usize, stride: TypeLen) -> Self { - Self { - $($type_field: StatePartArrayIndex { - index, - len, - stride: stride.$type_field, - },)* - } - } - pub(crate) fn len(self) -> usize { - let len = self.small_slots.len; - $(assert_eq!(self.$type_field.len, len, "array length mismatch");)* - len - } - pub(crate) fn index(self) -> StatePartIndex { - let index = self.small_slots.index; - $(assert_eq!(self.$type_field.index, index, "array index mismatch");)* - index - } - pub(crate) fn is_empty(self) -> bool { - self.len() == 0 - } - pub(crate) fn stride(self) -> TypeLen { - TypeLen { - $($type_field: self.$type_field.stride,)* - } - } - } - - #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, Default)] - pub(crate) struct TypeArrayIndexesRef<'a> { - $(pub(crate) $type_field: &'a [StatePartArrayIndex<$TypeKind>],)* - } - - impl<'a> TypeArrayIndexesRef<'a> { - pub(crate) fn len(self) -> usize { - let len = self.small_slots.len(); - $(assert_eq!(self.$type_field.len(), len, "indexes count mismatch");)* - len - } - pub(crate) fn is_empty(self) -> bool { - self.len() == 0 - } - pub(crate) fn iter(self) -> impl Iterator + 'a { - (0..self.len()).map(move |i| TypeArrayIndex { - $($type_field: self.$type_field[i],)* - }) - } - pub(crate) fn for_each_offset( - self, - mut f: impl FnMut(TypeIndex), - ) { - self.for_each_offset2(TypeIndex { - $($type_field: StatePartIndex { - value: 0, - _phantom: PhantomData, - },)* - }, &mut f); - } - pub(crate) fn split_first(self) -> Option<(TypeArrayIndex, Self)> { - $(let $type_field = self.$type_field.split_first()?;)* - let next = TypeArrayIndex { - $($type_field: *$type_field.0,)* - }; - let rest = TypeArrayIndexesRef { - $($type_field: $type_field.1,)* - }; - Some((next, rest)) - } - pub(crate) fn for_each_offset2( - self, - base_offset: TypeIndex, - f: &mut (impl FnMut(TypeIndex) + ?Sized), - ) { - if let Some((next, rest)) = self.split_first() { - let stride = next.stride(); - for index in 0..next.len().try_into().expect("array too big") { - let mut offset = TypeIndex { - $($type_field: StatePartIndex { - value: stride - .$type_field - .value - .checked_mul(index) - .expect("array too big"), - _phantom: PhantomData, - },)* - }; - $(offset.$type_field.value = - base_offset - .$type_field - .value - .checked_add(offset.$type_field.value) - .expect("array too big");)* - rest.for_each_offset2(offset, f); - } - } else { - $(assert!(self.$type_field.is_empty(), "indexes count mismatch");)* - f(base_offset); - } - } - } - - #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] - pub(crate) struct TypeArrayIndexed { - $(pub(crate) $type_field: StatePartArrayIndexed<$TypeKind>,)* - } - - impl TypeArrayIndexed { - pub(crate) fn from_parts(base: TypeIndex, indexes: TypeArrayIndexes) -> Self { - Self { - $($type_field: StatePartArrayIndexed { - base: base.$type_field, - indexes: indexes.$type_field, - },)* - } - } - pub(crate) fn base(self) -> TypeIndex { - TypeIndex { - $($type_field: self.$type_field.base,)* - } - } - pub(crate) fn indexes(self) -> TypeArrayIndexes { - TypeArrayIndexes { - $($type_field: self.$type_field.indexes,)* - } - } - } - - impl From for TypeArrayIndexed { - fn from(value: TypeIndex) -> Self { - TypeArrayIndexed { - $($type_field: value.$type_field.into(),)* - } - } - } - }; -} - -#[derive(Copy, Clone, Hash, PartialEq, Eq)] -pub(crate) struct MemoryData> { - pub(crate) array_type: Array, - pub(crate) data: T, -} - -impl> fmt::Debug for MemoryData { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let Self { array_type, data } = self; - f.debug_struct("MemoryData") - .field("array_type", array_type) - .field( - "data", - &crate::memory::DebugMemoryData::from_bit_slice(*array_type, data), - ) - .finish() - } -} - -make_state_part_kinds! { - /*#[state, field = small_stack] - impl StatePartKind for StatePartKindSmallStack { - const NAME: &'static str = "SmallStack"; - type DebugData = (); - type LayoutData = (); - type State = Stack; - type BorrowedState<'a> = BorrowedStack<'a, SmallUInt>; - fn new_state(layout_data: &[Self::LayoutData]) -> Self::State { - Stack::new(layout_data.len()) - } - fn borrow_state<'a>(state: &'a mut Self::State) -> Self::BorrowedState<'a> { - state.borrow() - } - fn part_debug_data( - state_layout: &StateLayout, - part_index: StatePartIndex, - ) -> Option<&Self::DebugData> { - state_layout.small_stack.debug_data.get(part_index.as_usize()) - } - } - #[state, field = big_stack] - impl StatePartKind for StatePartKindBigStack { - const NAME: &'static str = "BigStack"; - type DebugData = (); - type LayoutData = (); - type State = Stack; - type BorrowedState<'a> = BorrowedStack<'a, BigInt>; - fn new_state(layout_data: &[Self::LayoutData]) -> Self::State { - Stack::new(layout_data.len()) - } - fn borrow_state<'a>(state: &'a mut Self::State) -> Self::BorrowedState<'a> { - state.borrow() - } - fn part_debug_data( - state_layout: &StateLayout, - part_index: StatePartIndex, - ) -> Option<&Self::DebugData> { - state_layout.big_stack.debug_data.get(part_index.as_usize()) - } - }*/ - #[state, field = memories] - impl StatePartKind for StatePartKindMemories { - const NAME: &'static str = "Memories"; - type DebugData = (); - type LayoutData = MemoryData>; - type State = Box<[MemoryData]>; - type BorrowedState<'a> = &'a mut [MemoryData]; - fn new_state(layout_data: &[Self::LayoutData]) -> Self::State { - layout_data.iter().map(|MemoryData { array_type, data }| MemoryData { - array_type: *array_type, - data: BitBox::from_bitslice(data), - }).collect() - } - fn borrow_state<'a>(state: &'a mut Self::State) -> Self::BorrowedState<'a> { - state - } - fn part_debug_data( - state_layout: &StateLayout, - part_index: StatePartIndex, - ) -> Option<&Self::DebugData> { - state_layout.memories.debug_data.get(part_index.as_usize()) - } - fn debug_fmt_state_value( - state: &State, - index: StatePartIndex, - f: &mut impl fmt::Write, - ) -> fmt::Result { - write!(f, "{:#?}", &state.memories[index]) - } - } - #[type, field = small_slots] - impl StatePartKind for StatePartKindSmallSlots { - const NAME: &'static str = "SmallSlots"; - type DebugData = SlotDebugData; - type LayoutData = (); - type State = Box<[SmallUInt]>; - type BorrowedState<'a> = &'a mut [SmallUInt]; - fn new_state(layout_data: &[Self::LayoutData]) -> Self::State { - vec![0; layout_data.len()].into_boxed_slice() - } - fn borrow_state<'a>(state: &'a mut Self::State) -> Self::BorrowedState<'a> { - state - } - fn part_debug_data( - state_layout: &StateLayout, - part_index: StatePartIndex, - ) -> Option<&Self::DebugData> { - state_layout.ty.small_slots.debug_data.get(part_index.as_usize()) - } - fn debug_fmt_state_value( - state: &State, - index: StatePartIndex, - f: &mut impl fmt::Write, - ) -> fmt::Result { - let value = state.small_slots[index]; - write!(f, "{value:#x} {}", value as SmallSInt)?; - Ok(()) - } - } - #[type, field = big_slots] - impl StatePartKind for StatePartKindBigSlots { - const NAME: &'static str = "BigSlots"; - type DebugData = SlotDebugData; - type LayoutData = (); - type State = Box<[BigInt]>; - type BorrowedState<'a> = &'a mut [BigInt]; - fn new_state(layout_data: &[Self::LayoutData]) -> Self::State { - layout_data.iter().map(|_| BigInt::default()).collect() - } - fn borrow_state<'a>(state: &'a mut Self::State) -> Self::BorrowedState<'a> { - state - } - fn part_debug_data( - state_layout: &StateLayout, - part_index: StatePartIndex, - ) -> Option<&Self::DebugData> { - state_layout.ty.big_slots.debug_data.get(part_index.as_usize()) - } - fn debug_fmt_state_value( - state: &State, - index: StatePartIndex, - f: &mut impl fmt::Write, - ) -> fmt::Result { - write!(f, "{:#x}", state.big_slots[index]) - } - } -} - -impl TypeLen { - pub(crate) fn only_small(self) -> Option> { - let Self { - small_slots, - big_slots: - StatePartLen { - value: 0, - _phantom: _, - }, - } = self - else { - return None; - }; - Some(small_slots) - } - pub(crate) const A_SMALL_SLOT: Self = TypeLen { - small_slots: StatePartLen { - value: 1, - _phantom: PhantomData, - }, - big_slots: StatePartLen { - value: 0, - _phantom: PhantomData, - }, - }; - pub(crate) const A_BIG_SLOT: Self = TypeLen { - small_slots: StatePartLen { - value: 0, - _phantom: PhantomData, - }, - big_slots: StatePartLen { - value: 1, - _phantom: PhantomData, - }, - }; -} - -#[derive(Debug, Clone)] -pub(crate) struct Stack { - storage: Box<[T]>, - cur_len: usize, -} - -impl Stack { - pub(crate) fn new(len: usize) -> Self - where - T: Default, - { - Self { - storage: std::iter::repeat_with(T::default).take(len).collect(), - cur_len: 0, - } - } - pub(crate) fn borrow(&mut self) -> BorrowedStack<'_, T> { - BorrowedStack { - storage: &mut self.storage, - cur_len: self.cur_len, - stack_cur_len: &mut self.cur_len, - } - } - pub(crate) fn clear(&mut self) { - self.cur_len = 0; - } -} - -#[derive(Debug)] -pub(crate) struct BorrowedStack<'a, T> { - storage: &'a mut [T], - cur_len: usize, - stack_cur_len: &'a mut usize, -} - -impl<'a, T> BorrowedStack<'a, T> { - pub(crate) fn push(&mut self, value: T) { - self.storage[self.cur_len] = value; - self.cur_len += 1; - } - pub(crate) fn pop(&mut self) -> T - where - T: Default, - { - self.cur_len -= 1; - std::mem::take(&mut self.storage[self.cur_len]) - } -} - -impl Drop for BorrowedStack<'_, T> { - fn drop(&mut self) { - *self.stack_cur_len = self.cur_len; - } -} - -#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] -pub(crate) struct SlotDebugData { - pub(crate) name: Interned, - pub(crate) ty: CanonicalType, -} - -impl SlotDebugData { - pub(crate) fn with_prefixed_debug_names(&self, prefix: &str) -> Self { - let mut name = String::with_capacity(self.name.len() + prefix.len()); - name.push_str(prefix); - name.push_str(&self.name); - Self { - name: Intern::intern_owned(name), - ty: self.ty, - } - } - pub(crate) fn with_anonymized_debug_info(&self) -> Self { - Self { - name: Interned::default(), - ty: self.ty, - } - } -} - -impl, BK: InsnsBuildingKind> StatePartLayout { - pub(crate) fn with_prefixed_debug_names(&self, prefix: &str) -> Self { - Self { - debug_data: self - .debug_data - .iter() - .map(|v| v.with_prefixed_debug_names(prefix)) - .collect(), - layout_data: self.layout_data.clone(), - _phantom: PhantomData, - } - } - pub(crate) fn with_anonymized_debug_info(&self) -> Self { - Self { - debug_data: self - .debug_data - .iter() - .map(|v| v.with_anonymized_debug_info()) - .collect(), - layout_data: self.layout_data.clone(), - _phantom: PhantomData, - } - } -} - -impl TypeLayout { - pub(crate) fn with_prefixed_debug_names(&self, prefix: &str) -> Self { - Self { - small_slots: self.small_slots.with_prefixed_debug_names(prefix), - big_slots: self.big_slots.with_prefixed_debug_names(prefix), - } - } - pub(crate) fn with_anonymized_debug_info(&self) -> Self { - Self { - small_slots: self.small_slots.with_anonymized_debug_info(), - big_slots: self.big_slots.with_anonymized_debug_info(), - } - } -} - #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] pub(crate) struct StatePartArrayIndex { pub(crate) index: StatePartIndex, @@ -1751,13 +871,11 @@ impl StatePartArrayIndexed { if let [next, rest @ ..] = indexes { for i in 0..next.len.try_into().expect("array too big") { Self::for_each_target2( - StatePartIndex { - value: base - .value + StatePartIndex::new( + base.value .checked_add(next.stride.value.checked_mul(i).expect("array too big")) .expect("array too big"), - _phantom: PhantomData, - }, + ), rest, f, ); @@ -1771,285 +889,6 @@ impl StatePartArrayIndexed { } } -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub(crate) struct StatePartIndex { - pub(crate) value: u32, - pub(crate) _phantom: PhantomData, -} - -impl StatePartIndex { - pub(crate) const ZERO: Self = Self { - value: 0, - _phantom: PhantomData, - }; - pub(crate) fn as_usize(self) -> usize { - self.value.try_into().expect("index too big") - } - pub(crate) fn offset(self, offset: StatePartIndex) -> Self { - Self { - value: self - .value - .checked_add(offset.value) - .expect("offset too big"), - _phantom: PhantomData, - } - } - pub(crate) fn debug_fmt( - &self, - f: &mut impl fmt::Write, - before_debug_info_text: &str, - comment_start: &str, - comment_line_start: &str, - comment_end: &str, - state_layout: Option<&StateLayout>, - state: Option<&State>, - ) -> fmt::Result { - write!(f, "StatePartIndex<{}>({})", K::NAME, self.value)?; - f.write_str(before_debug_info_text)?; - let debug_data = - state_layout.and_then(|state_layout| K::part_debug_data(state_layout, *self)); - if state.is_some() || debug_data.is_some() { - f.write_str(comment_start)?; - } - let mut f = PrefixLinesWrapper { - writer: f, - at_beginning_of_line: false, - blank_line_prefix: comment_line_start.trim_end(), - line_prefix: comment_line_start, - }; - if let Some(state) = state { - f.write_str("(")?; - K::debug_fmt_state_value(state, *self, &mut f)?; - f.write_str(")")?; - } - if state.is_some() && debug_data.is_some() { - f.write_str(" ")?; - } - if let Some(debug_data) = debug_data { - write!(f, "{debug_data:?}")?; - } - if state.is_some() || debug_data.is_some() { - f.writer.write_str(comment_end)?; - } - Ok(()) - } -} - -impl fmt::Debug for StatePartIndex { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.debug_fmt::(f, "", "", "", "", None, None) - } -} - -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub(crate) struct StatePartLen { - pub(crate) value: u32, - pub(crate) _phantom: PhantomData, -} - -impl StatePartLen { - pub(crate) const fn new(value: u32) -> Self { - Self { - value, - _phantom: PhantomData, - } - } - pub(crate) const fn as_index(self) -> StatePartIndex { - StatePartIndex { - value: self.value, - _phantom: PhantomData, - } - } -} - -impl fmt::Debug for StatePartLen { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "StatePartLen<{}>({})", K::NAME, self.value) - } -} - -#[derive(Clone, PartialEq, Eq, Hash)] -pub(crate) struct StatePartLayout { - pub(crate) debug_data: BK::Vec, - pub(crate) layout_data: BK::Vec, - pub(crate) _phantom: PhantomData, -} - -impl Copy for StatePartLayout {} - -impl StatePartLayout { - pub(crate) fn len(&self) -> StatePartLen { - StatePartLen::new( - self.debug_data - .len() - .try_into() - .expect("state part allocation layout is too big"), - ) - } - pub(crate) fn is_empty(&self) -> bool { - self.debug_data.is_empty() - } - pub(crate) fn empty() -> Self { - Self { - debug_data: Default::default(), - layout_data: Default::default(), - _phantom: PhantomData, - } - } - pub(crate) fn debug_data(&self, index: StatePartIndex) -> &K::DebugData { - &self.debug_data[index.as_usize()] - } -} - -impl From> - for StatePartLayout -{ - fn from(value: StatePartLayout) -> Self { - Self { - debug_data: Intern::intern_owned(value.debug_data), - layout_data: Intern::intern_owned(value.layout_data), - _phantom: PhantomData, - } - } -} - -impl StatePartLayout { - pub(crate) fn scalar(debug_data: K::DebugData, layout_data: K::LayoutData) -> Self { - Self { - debug_data: vec![debug_data], - layout_data: vec![layout_data], - _phantom: PhantomData, - } - } - pub(crate) fn next_index(&self) -> StatePartIndex { - StatePartIndex { - value: self.len().value, - _phantom: PhantomData, - } - } - pub(crate) fn allocate( - &mut self, - layout: &StatePartLayout, - ) -> StatePartIndexRange { - let start = self.next_index(); - let len = layout.len(); - let Self { - debug_data, - layout_data, - _phantom: _, - } = self; - debug_data.extend_from_slice(&layout.debug_data); - layout_data.extend_from_slice(&layout.layout_data); - StatePartIndexRange { start, len } - } -} - -impl fmt::Debug for StatePartLayout { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let Self { - debug_data, - layout_data, - _phantom: _, - } = self; - write!(f, "StatePartLayout<{}>", K::NAME)?; - let mut debug_struct = f.debug_struct(""); - debug_struct - .field("len", &debug_data.len()) - .field("debug_data", debug_data); - if TypeId::of::() != TypeId::of::<()>() { - debug_struct.field("layout_data", layout_data); - } - debug_struct.finish_non_exhaustive() - } -} - -#[derive(Copy, Clone, PartialEq, Eq, Hash)] -pub(crate) struct StatePartIndexRange { - pub(crate) start: StatePartIndex, - pub(crate) len: StatePartLen, -} - -impl StatePartIndexRange { - pub(crate) fn start(self) -> StatePartIndex { - self.start - } - pub(crate) fn end(self) -> StatePartIndex { - StatePartIndex { - value: self - .start - .value - .checked_add(self.len.value) - .expect("state part allocation layout is too big"), - _phantom: PhantomData, - } - } - pub(crate) fn len(self) -> StatePartLen { - self.len - } - pub(crate) fn is_empty(self) -> bool { - self.len.value == 0 - } - pub(crate) fn iter(self) -> impl Iterator> { - (self.start.value..self.end().value).map(|value| StatePartIndex { - value, - _phantom: PhantomData, - }) - } - pub(crate) fn offset(self, offset: StatePartIndex) -> Self { - self.end().offset(offset); // check for overflow - Self { - start: self.start.offset(offset), - len: self.len, - } - } - pub(crate) fn slice(self, index: StatePartIndexRange) -> Self { - assert!(index.end().value <= self.len.value, "index out of range"); - Self { - start: StatePartIndex { - value: self.start.value + index.start.value, - _phantom: PhantomData, - }, - len: index.len, - } - } - pub(crate) fn index_array(self, element_size: StatePartLen, index: usize) -> Self { - if element_size.value == 0 { - assert_eq!( - self.len.value, 0, - "array with zero-sized element must also be zero-sized", - ); - return self; - } - assert!( - self.len.value % element_size.value == 0, - "array's size must be a multiple of its element size", - ); - self.slice(StatePartIndexRange { - start: StatePartIndex { - value: index - .try_into() - .ok() - .and_then(|index| element_size.value.checked_mul(index)) - .expect("index out of range"), - _phantom: PhantomData, - }, - len: element_size, - }) - } -} - -impl fmt::Debug for StatePartIndexRange { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!( - f, - "StatePartIndexRange<{}> {{ start: {}, len: {} }}", - K::NAME, - self.start.value, - self.len.value - ) - } -} - #[derive(Clone, PartialEq, Eq, Hash, Debug)] pub(crate) struct StatePart { pub(crate) value: K::State, @@ -2074,21 +913,21 @@ pub(crate) struct BorrowedStatePart<'a, K: StatePartKind> { } impl< - 'a, - K: StatePartKind< - BorrowedState<'a>: DerefMut + BorrowMut<[T]>>, - >, - T, - > BorrowedStatePart<'a, K> + 'a, + K: StatePartKind< + BorrowedState<'a>: DerefMut + BorrowMut<[T]>>, + >, + T, +> BorrowedStatePart<'a, K> { - pub(crate) fn get_many_mut( + pub(crate) fn get_disjoint_mut( &mut self, indexes: [StatePartIndex; N], ) -> [&mut T; N] { - get_many_mut( - (*self.value).borrow_mut(), - indexes.map(|v| v.value as usize), - ) + (*self.value) + .borrow_mut() + .get_disjoint_mut(indexes.map(|v| v.value as usize)) + .expect("indexes are disjoint") } } @@ -2126,17 +965,84 @@ impl<'a, K: StatePartKind: DerefMut = BorrowedStack<'a, T>>, T: 'a> - BorrowedStatePart<'a, K> -{ - pub(crate) fn push(&mut self, value: T) { - self.value.push(value) - } - pub(crate) fn pop(&mut self) -> T - where - T: Default, - { - self.value.pop() +macro_rules! make_state { + ( + state_plural_fields = [$(#[state] $state_plural_field:ident,)* $(#[type] $type_plural_field:ident,)*]; + state_kinds = [$(#[state] $state_kind:ident,)* $(#[type] $type_kind:ident,)*]; + ) => { + pub(crate) struct State { + pub(crate) insns: Interned>, + pub(crate) pc: usize, + pub(crate) memory_write_log: Vec<(StatePartIndex, usize)>, + $(pub(crate) $state_plural_field: StatePart<$state_kind>,)* + $(pub(crate) $type_plural_field: StatePart<$type_kind>,)* + } + + impl fmt::Debug for State { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let Self { + insns: _, + pc, + memory_write_log, + $($state_plural_field,)* + $($type_plural_field,)* + } = self; + f.debug_struct("State") + .field("insns", &InsnsOfState(self)) + .field("pc", pc) + .field("memory_write_log", memory_write_log) + $(.field(stringify!($state_plural_field), $state_plural_field))* + $(.field(stringify!($type_plural_field), $type_plural_field))* + .finish() + } + } + + impl State { + pub(crate) fn new(insns: Interned>) -> Self { + Self { + insns, + pc: 0, + memory_write_log: Vec::with_capacity(32), + $($state_plural_field: StatePart::new(&insns.state_layout.$state_plural_field.layout_data),)* + $($type_plural_field: StatePart::new(&insns.state_layout.ty.$type_plural_field.layout_data),)* + } + } + pub(crate) fn borrow(&mut self) -> BorrowedState<'_> { + BorrowedState { + orig_insns: self.insns, + insns: &self.insns.insns, + pc: self.pc, + orig_pc: &mut self.pc, + memory_write_log: &mut self.memory_write_log, + $($state_plural_field: self.$state_plural_field.borrow(),)* + $($type_plural_field: self.$type_plural_field.borrow(),)* + } + } + } + + #[derive(Debug)] + pub(crate) struct BorrowedState<'a> { + pub(crate) orig_insns: Interned>, + pub(crate) insns: &'a [Insn], + pub(crate) orig_pc: &'a mut usize, + pub(crate) pc: usize, + pub(crate) memory_write_log: &'a mut Vec<(StatePartIndex, usize)>, + $(pub(crate) $state_plural_field: BorrowedStatePart<'a, $state_kind>,)* + $(pub(crate) $type_plural_field: BorrowedStatePart<'a, $type_kind>,)* + } + + impl Drop for BorrowedState<'_> { + fn drop(&mut self) { + *self.orig_pc = self.pc; + } + } + }; +} + +get_state_part_kinds! { + make_state! { + state_plural_fields; + state_kinds; } } @@ -2173,10 +1079,7 @@ impl StatePartIndexMap { self.map.get_mut(key.as_usize()) } pub(crate) fn keys(&self) -> impl Iterator> + '_ { - self.map.keys().map(|k| StatePartIndex { - value: k as u32, - _phantom: PhantomData, - }) + self.map.keys().map(|v| StatePartIndex::new(v as _)) } pub(crate) fn values(&self) -> vec_map::Values<'_, V> { self.map.values() @@ -2185,26 +1088,14 @@ impl StatePartIndexMap { self.map.values_mut() } pub(crate) fn iter(&self) -> impl Iterator, &V)> + '_ { - self.map.iter().map(|(k, v)| { - ( - StatePartIndex { - value: k as u32, - _phantom: PhantomData, - }, - v, - ) - }) + self.map + .iter() + .map(|(k, v)| (StatePartIndex::new(k as u32), v)) } pub(crate) fn iter_mut(&mut self) -> impl Iterator, &mut V)> + '_ { - self.map.iter_mut().map(|(k, v)| { - ( - StatePartIndex { - value: k as u32, - _phantom: PhantomData, - }, - v, - ) - }) + self.map + .iter_mut() + .map(|(k, v)| (StatePartIndex::new(k as u32), v)) } pub(crate) fn len(&self) -> usize { self.map.len() @@ -2359,8 +1250,10 @@ impl From> for Insns { InsnFieldType::Memory(_) | InsnFieldType::SmallSlot(_) | InsnFieldType::BigSlot(_) + | InsnFieldType::SimOnlySlot(_) | InsnFieldType::SmallSlotArrayIndexed(_) | InsnFieldType::BigSlotArrayIndexed(_) + | InsnFieldType::SimOnlySlotArrayIndexed(_) | InsnFieldType::SmallUInt(_) | InsnFieldType::SmallSInt(_) | InsnFieldType::InternedBigInt(_) @@ -2394,6 +1287,7 @@ impl State { memories: _, small_slots: _, big_slots: _, + sim_only_slots: _, } = self; *pc = entry_pc; } @@ -2431,31 +1325,6 @@ impl BorrowedState<'_> { } } -impl TypeIndexRange { - #[must_use] - pub(crate) fn insns_for_copy_to(self, dest: TypeIndexRange) -> impl Iterator { - assert_eq!(self.len(), dest.len()); - let Self { - small_slots, - big_slots, - } = self; - small_slots - .iter() - .zip(dest.small_slots.iter()) - .map(|(src, dest)| Insn::CopySmall { dest, src }) - .chain( - big_slots - .iter() - .zip(dest.big_slots.iter()) - .map(|(src, dest)| Insn::Copy { dest, src }), - ) - } - #[must_use] - pub(crate) fn insns_for_copy_from(self, src: TypeIndexRange) -> impl Iterator { - src.insns_for_copy_to(self) - } -} - fn bigint_pow2(width: usize) -> Interned { #[derive(Copy, Clone, PartialEq, Eq, Hash)] struct MyMemoize; @@ -2562,18 +1431,6 @@ impl_insns! { main_loop!(); cleanup! {} } - Copy { - #[kind = Output] - dest: StatePartIndex, - #[kind = Input] - src: StatePartIndex, - } => { - if dest != src { - let [dest, src] = state.big_slots.get_many_mut([dest, src]); - dest.clone_from(src); - } - next!(); - } CopySmall { #[kind = Output] dest: StatePartIndex, @@ -2583,19 +1440,27 @@ impl_insns! { state.small_slots[dest] = state.small_slots[src]; next!(); } - ReadIndexed { + Copy { #[kind = Output] dest: StatePartIndex, #[kind = Input] - src: StatePartArrayIndexed, + src: StatePartIndex, } => { - if let Some(src) = state.eval_array_indexed(src) { - if dest != src { - let [dest, src] = state.big_slots.get_many_mut([dest, src]); - dest.clone_from(src); - } - } else { - state.big_slots[dest] = BigInt::ZERO; + if dest != src { + let [dest, src] = state.big_slots.get_disjoint_mut([dest, src]); + dest.clone_from(src); + } + next!(); + } + CloneSimOnly { + #[kind = Output] + dest: StatePartIndex, + #[kind = Input] + src: StatePartIndex, + } => { + if dest != src { + let [dest, src] = state.sim_only_slots.get_disjoint_mut([dest, src]); + dest.clone_from(src); } next!(); } @@ -2612,17 +1477,35 @@ impl_insns! { } next!(); } - WriteIndexed { + ReadIndexed { #[kind = Output] - dest: StatePartArrayIndexed, + dest: StatePartIndex, #[kind = Input] - src: StatePartIndex, + src: StatePartArrayIndexed, } => { - if let Some(dest) = state.eval_array_indexed(dest) { + if let Some(src) = state.eval_array_indexed(src) { if dest != src { - let [dest, src] = state.big_slots.get_many_mut([dest, src]); + let [dest, src] = state.big_slots.get_disjoint_mut([dest, src]); dest.clone_from(src); } + } else { + state.big_slots[dest] = BigInt::ZERO; + } + next!(); + } + ReadSimOnlyIndexed { + #[kind = Output] + dest: StatePartIndex, + #[kind = Input] + src: StatePartArrayIndexed, + } => { + if let Some(src) = state.eval_array_indexed(src) { + if dest != src { + let [dest, src] = state.sim_only_slots.get_disjoint_mut([dest, src]); + dest.clone_from(src); + } + } else { + state.sim_only_slots[dest] = state.sim_only_slots[dest].ty().default_value(); } next!(); } @@ -2637,6 +1520,34 @@ impl_insns! { } next!(); } + WriteIndexed { + #[kind = Output] + dest: StatePartArrayIndexed, + #[kind = Input] + src: StatePartIndex, + } => { + if let Some(dest) = state.eval_array_indexed(dest) { + if dest != src { + let [dest, src] = state.big_slots.get_disjoint_mut([dest, src]); + dest.clone_from(src); + } + } + next!(); + } + WriteSimOnlyIndexed { + #[kind = Output] + dest: StatePartArrayIndexed, + #[kind = Input] + src: StatePartIndex, + } => { + if let Some(dest) = state.eval_array_indexed(dest) { + if dest != src { + let [dest, src] = state.sim_only_slots.get_disjoint_mut([dest, src]); + dest.clone_from(src); + } + } + next!(); + } CastBigToArrayIndex { #[kind = Output] dest: StatePartIndex, diff --git a/crates/fayalite/src/sim/interpreter/parts.rs b/crates/fayalite/src/sim/interpreter/parts.rs new file mode 100644 index 0000000..8732146 --- /dev/null +++ b/crates/fayalite/src/sim/interpreter/parts.rs @@ -0,0 +1,1052 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// See Notices.txt for copyright information + +use crate::{ + array::Array, + intern::{Intern, Interned}, + sim::{ + interpreter::{ + Insn, InsnsBuilding, InsnsBuildingDone, InsnsBuildingKind, PrefixLinesWrapper, + SmallSInt, SmallUInt, State, + }, + value::{DynSimOnlyValue, DynSimOnly}, + }, + ty::CanonicalType, + util::{chain, const_str_cmp}, +}; +use bitvec::{boxed::BitBox, slice::BitSlice}; +use num_bigint::BigInt; +use std::{ + any::TypeId, + fmt::{self, Write}, + hash::Hash, + marker::PhantomData, + ops::Deref, +}; + +#[rustfmt::skip] +macro_rules! make_get_state_part_kinds { + ( + #![dollar = $d:tt] + $(state_part! { + singular_field = $state_singular_fields:ident; + plural_field = $state_plural_fields:ident; + kind = $state_kinds:ident; + singular_variant = $state_singular_variants:ident; + plural_variant = $state_plural_variants:ident; + })* + $(type_part! { + singular_field = $type_singular_fields:ident; + plural_field = $type_plural_fields:ident; + kind = $type_kinds:ident; + singular_variant = $type_singular_variants:ident; + plural_variant = $type_plural_variants:ident; + copy_insn = $copy_insn:ident; + read_indexed_insn = $read_indexed_insn:ident; + write_indexed_insn = $write_indexed_insn:ident; + array_indexed_variant = $array_indexed_variants:ident; + })* + ) => { + make_get_state_part_kinds! { + #![dollar = $d] + parts! { + #![part_names = [$(#[state] $state_singular_fields,)* $(#[type] $type_singular_fields,)*]] + [$(state_singular_fields = (#[state] $state_singular_fields),)* $(state_singular_fields = (#[type] $type_singular_fields),)*]; + [$(type_singular_fields = ($type_singular_fields),)*]; + [$(state_plural_fields = (#[state] $state_plural_fields),)* $(state_plural_fields = (#[type] $type_plural_fields),)*]; + [$(type_plural_fields = ($type_plural_fields),)*]; + [$(state_kinds = (#[state] $state_kinds),)* $(state_kinds = (#[type] $type_kinds),)*]; + [$(type_kinds = ($type_kinds),)*]; + [$(state_singular_variants = (#[state] $state_singular_variants),)* $(state_singular_variants = (#[type] $type_singular_variants),)*]; + [$(type_singular_variants = ($type_singular_variants),)*]; + [$(state_plural_variants = (#[state] $state_plural_variants),)* $(state_plural_variants = (#[type] $type_plural_variants),)*]; + [$(type_plural_variants = ($type_plural_variants),)*]; + [$(copy_insns = ($copy_insn),)*]; + [$(read_indexed_insns = ($read_indexed_insn),)*]; + [$(write_indexed_insns = ($write_indexed_insn),)*]; + [$(array_indexed_variants = ($array_indexed_variants),)*]; + } + } + }; + ( + #![dollar = $d:tt] + parts! { + #![part_names = [$(#[state] $state_part_name:ident,)* $(#[type] $type_part_name:ident,)*]] + $([ + $first_name:ident = ($($first_value:tt)*), + $($name:ident = ($($value:tt)*),)* + ];)* + } + ) => { + const _: () = { + $($(assert!(const_str_cmp(stringify!($first_name), stringify!($name)).is_eq());)*)* + }; + macro_rules! get_state_part_kinds { + ($d macro_ident:ident! {$d ($d args:tt)*}) => { + get_state_part_kinds! { + @expand + $d macro_ident! {} + ($d ($d args)*) + () + } + }; + ($d macro_ident:ident!($d ($d args:tt)*)) => { + get_state_part_kinds! { + @expand + $d macro_ident!() + ($d ($d args)*) + () + } + }; + ( + @expand + $d macro_ident:ident! {} + () + ($d ($d args:tt)*) + ) => { + $d macro_ident! { + $d ($d args)* + } + }; + ( + @expand + $d macro_ident:ident!() + () + ($d ($d args:tt)*) + ) => { + $d macro_ident!( + $d ($d args)* + ) + }; + ( + @expand + $d macro_ident:ident! $d group:tt + (, $d ($d rest:tt)*) + ($d ($d prev_args:tt)*) + ) => { + get_state_part_kinds! { + @expand + $d macro_ident! $d group + ($d ($d rest)*) + ($d ($d prev_args)*,) + } + }; + ( + @expand + $d macro_ident:ident! $d group:tt + (; $d ($d rest:tt)*) + ($d ($d prev_args:tt)*) + ) => { + get_state_part_kinds! { + @expand + $d macro_ident! $d group + ($d ($d rest)*) + ($d ($d prev_args)*;) + } + }; + ( + @expand + $d macro_ident:ident! $d group:tt + (#[custom] $d name:ident = [ + $d ($($state_part_name = $d $state_part_name:tt,)*)? + $($type_part_name = $d $type_part_name:tt,)* + ] + $d ($d rest:tt)* + ) + ($d ($d prev_args:tt)*) + ) => { + get_state_part_kinds! { + @expand + $d macro_ident! $d group + ($d ($d rest)*) + ($d ($d prev_args)* $d name = [ + $d ($($d $state_part_name,)*)? + $($d $type_part_name,)* + ]) + } + }; + $(( + @expand + $d macro_ident:ident! $d group:tt + ($first_name $d ($d rest:tt)*) + ($d ($d prev_args:tt)*) + ) => { + get_state_part_kinds! { + @expand + $d macro_ident! $d group + ($d ($d rest)*) + ($d ($d prev_args)* $first_name = [$($first_value)*, $($($value)*,)*]) + } + };)* + } + + pub(crate) use get_state_part_kinds; + }; +} + +make_get_state_part_kinds! { + #![dollar = $] + state_part! { + singular_field = memory; + plural_field = memories; + kind = StatePartKindMemories; + singular_variant = Memory; + plural_variant = Memories; + } + type_part! { + singular_field = small_slot; + plural_field = small_slots; + kind = StatePartKindSmallSlots; + singular_variant = SmallSlot; + plural_variant = SmallSlots; + copy_insn = CopySmall; + read_indexed_insn = ReadSmallIndexed; + write_indexed_insn = WriteSmallIndexed; + array_indexed_variant = SmallSlotArrayIndexed; + } + type_part! { + singular_field = big_slot; + plural_field = big_slots; + kind = StatePartKindBigSlots; + singular_variant = BigSlot; + plural_variant = BigSlots; + copy_insn = Copy; + read_indexed_insn = ReadIndexed; + write_indexed_insn = WriteIndexed; + array_indexed_variant = BigSlotArrayIndexed; + } + type_part! { + singular_field = sim_only_slot; + plural_field = sim_only_slots; + kind = StatePartKindSimOnlySlots; + singular_variant = SimOnlySlot; + plural_variant = SimOnlySlots; + copy_insn = CloneSimOnly; + read_indexed_insn = ReadSimOnlyIndexed; + write_indexed_insn = WriteSimOnlyIndexed; + array_indexed_variant = SimOnlySlotArrayIndexed; + } +} + +pub(crate) trait StatePartKind: + Send + Sync + Ord + Hash + fmt::Debug + 'static + Copy + Default +{ + const NAME: &'static str; + type DebugData: Send + Sync + Eq + Hash + fmt::Debug + 'static + Copy; + type LayoutData: Send + Sync + Eq + Hash + fmt::Debug + 'static + Copy; + type State: fmt::Debug + 'static + Clone; + type BorrowedState<'a>: 'a; + fn new_state(layout_data: &[Self::LayoutData]) -> Self::State; + fn borrow_state<'a>(state: &'a mut Self::State) -> Self::BorrowedState<'a>; + fn part_debug_data( + state_layout: &StateLayout, + part_index: StatePartIndex, + ) -> Option<&Self::DebugData>; + fn debug_fmt_state_value( + state: &State, + index: StatePartIndex, + f: &mut impl fmt::Write, + ) -> fmt::Result; +} + +macro_rules! make_state_part_kinds { + ( + state_kinds = [$(#[$kind_attr:ident] $kind:ident,)*]; + ) => { + $( + #[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Clone, Copy, Default)] + pub(crate) struct $kind; + )* + }; +} + +get_state_part_kinds! { + make_state_part_kinds! { + state_kinds; + } +} + +impl StatePartKind for StatePartKindMemories { + const NAME: &'static str = "Memories"; + type DebugData = (); + type LayoutData = MemoryData>; + type State = Box<[MemoryData]>; + type BorrowedState<'a> = &'a mut [MemoryData]; + fn new_state(layout_data: &[Self::LayoutData]) -> Self::State { + layout_data + .iter() + .map(|MemoryData { array_type, data }| MemoryData { + array_type: *array_type, + data: BitBox::from_bitslice(data), + }) + .collect() + } + fn borrow_state<'a>(state: &'a mut Self::State) -> Self::BorrowedState<'a> { + state + } + fn part_debug_data( + state_layout: &StateLayout, + part_index: StatePartIndex, + ) -> Option<&Self::DebugData> { + state_layout.memories.debug_data.get(part_index.as_usize()) + } + fn debug_fmt_state_value( + state: &State, + index: StatePartIndex, + f: &mut impl fmt::Write, + ) -> fmt::Result { + write!(f, "{:#?}", &state.memories[index]) + } +} + +impl StatePartKind for StatePartKindSmallSlots { + const NAME: &'static str = "SmallSlots"; + type DebugData = SlotDebugData; + type LayoutData = (); + type State = Box<[SmallUInt]>; + type BorrowedState<'a> = &'a mut [SmallUInt]; + fn new_state(layout_data: &[Self::LayoutData]) -> Self::State { + vec![0; layout_data.len()].into_boxed_slice() + } + fn borrow_state<'a>(state: &'a mut Self::State) -> Self::BorrowedState<'a> { + state + } + fn part_debug_data( + state_layout: &StateLayout, + part_index: StatePartIndex, + ) -> Option<&Self::DebugData> { + state_layout + .ty + .small_slots + .debug_data + .get(part_index.as_usize()) + } + fn debug_fmt_state_value( + state: &State, + index: StatePartIndex, + f: &mut impl fmt::Write, + ) -> fmt::Result { + let value = state.small_slots[index]; + write!(f, "{value:#x} {}", value as SmallSInt)?; + Ok(()) + } +} + +impl StatePartKind for StatePartKindBigSlots { + const NAME: &'static str = "BigSlots"; + type DebugData = SlotDebugData; + type LayoutData = (); + type State = Box<[BigInt]>; + type BorrowedState<'a> = &'a mut [BigInt]; + fn new_state(layout_data: &[Self::LayoutData]) -> Self::State { + layout_data.iter().map(|_| BigInt::default()).collect() + } + fn borrow_state<'a>(state: &'a mut Self::State) -> Self::BorrowedState<'a> { + state + } + fn part_debug_data( + state_layout: &StateLayout, + part_index: StatePartIndex, + ) -> Option<&Self::DebugData> { + state_layout + .ty + .big_slots + .debug_data + .get(part_index.as_usize()) + } + fn debug_fmt_state_value( + state: &State, + index: StatePartIndex, + f: &mut impl fmt::Write, + ) -> fmt::Result { + write!(f, "{:#x}", state.big_slots[index]) + } +} + +impl StatePartKind for StatePartKindSimOnlySlots { + const NAME: &'static str = "SimOnlySlots"; + type DebugData = SlotDebugData; + type LayoutData = DynSimOnly; + type State = Box<[DynSimOnlyValue]>; + type BorrowedState<'a> = &'a mut [DynSimOnlyValue]; + fn new_state(layout_data: &[Self::LayoutData]) -> Self::State { + layout_data.iter().map(|ty| ty.default_value()).collect() + } + fn borrow_state<'a>(state: &'a mut Self::State) -> Self::BorrowedState<'a> { + state + } + fn part_debug_data( + state_layout: &StateLayout, + part_index: StatePartIndex, + ) -> Option<&Self::DebugData> { + state_layout + .ty + .sim_only_slots + .debug_data + .get(part_index.as_usize()) + } + fn debug_fmt_state_value( + state: &State, + index: StatePartIndex, + f: &mut impl fmt::Write, + ) -> fmt::Result { + write!(f, "{:?}", state.sim_only_slots[index]) + } +} + +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub(crate) struct StatePartIndex { + pub(crate) value: u32, + pub(crate) _phantom: PhantomData, +} + +impl StatePartIndex { + pub(crate) const ZERO: Self = Self::new(0); + pub(crate) const fn new(value: u32) -> Self { + Self { + value, + _phantom: PhantomData, + } + } + pub(crate) fn as_usize(self) -> usize { + self.value.try_into().expect("index too big") + } + pub(crate) fn offset(self, offset: StatePartIndex) -> Self { + Self::new( + self.value + .checked_add(offset.value) + .expect("offset too big"), + ) + } + pub(crate) fn debug_fmt( + &self, + f: &mut impl fmt::Write, + before_debug_info_text: &str, + comment_start: &str, + comment_line_start: &str, + comment_end: &str, + state_layout: Option<&StateLayout>, + state: Option<&State>, + ) -> fmt::Result { + write!(f, "StatePartIndex<{}>({})", K::NAME, self.value)?; + f.write_str(before_debug_info_text)?; + let debug_data = + state_layout.and_then(|state_layout| K::part_debug_data(state_layout, *self)); + if state.is_some() || debug_data.is_some() { + f.write_str(comment_start)?; + } + let mut f = PrefixLinesWrapper { + writer: f, + at_beginning_of_line: false, + blank_line_prefix: comment_line_start.trim_end(), + line_prefix: comment_line_start, + }; + if let Some(state) = state { + f.write_str("(")?; + K::debug_fmt_state_value(state, *self, &mut f)?; + f.write_str(")")?; + } + if state.is_some() && debug_data.is_some() { + f.write_str(" ")?; + } + if let Some(debug_data) = debug_data { + write!(f, "{debug_data:?}")?; + } + if state.is_some() || debug_data.is_some() { + f.writer.write_str(comment_end)?; + } + Ok(()) + } +} + +impl fmt::Debug for StatePartIndex { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.debug_fmt::(f, "", "", "", "", None, None) + } +} + +#[derive(Copy, Clone, PartialEq, Eq, Hash)] +pub(crate) struct StatePartIndexRange { + pub(crate) start: StatePartIndex, + pub(crate) len: StatePartLen, +} + +impl StatePartIndexRange { + pub(crate) fn start(self) -> StatePartIndex { + self.start + } + pub(crate) fn end(self) -> StatePartIndex { + StatePartIndex::new( + self.start + .value + .checked_add(self.len.value) + .expect("state part allocation layout is too big"), + ) + } + pub(crate) fn len(self) -> StatePartLen { + self.len + } + pub(crate) fn is_empty(self) -> bool { + self.len.value == 0 + } + pub(crate) fn iter(self) -> impl Iterator> { + (self.start.value..self.end().value).map(StatePartIndex::new) + } + pub(crate) fn offset(self, offset: StatePartIndex) -> Self { + self.end().offset(offset); // check for overflow + Self { + start: self.start.offset(offset), + len: self.len, + } + } + pub(crate) fn slice(self, index: StatePartIndexRange) -> Self { + assert!(index.end().value <= self.len.value, "index out of range"); + Self { + start: StatePartIndex::new(self.start.value + index.start.value), + len: index.len, + } + } + pub(crate) fn index_array(self, element_size: StatePartLen, index: usize) -> Self { + if element_size.value == 0 { + assert_eq!( + self.len.value, 0, + "array with zero-sized element must also be zero-sized", + ); + return self; + } + assert!( + self.len.value % element_size.value == 0, + "array's size must be a multiple of its element size", + ); + self.slice(StatePartIndexRange { + start: StatePartIndex::new( + index + .try_into() + .ok() + .and_then(|index| element_size.value.checked_mul(index)) + .expect("index out of range"), + ), + len: element_size, + }) + } +} + +impl fmt::Debug for StatePartIndexRange { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "StatePartIndexRange<{}> {{ start: {}, len: {} }}", + K::NAME, + self.start.value, + self.len.value + ) + } +} + +#[derive(Copy, Clone, Hash, PartialEq, Eq)] +pub(crate) struct MemoryData> { + pub(crate) array_type: Array, + pub(crate) data: T, +} + +impl> fmt::Debug for MemoryData { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let Self { array_type, data } = self; + f.debug_struct("MemoryData") + .field("array_type", array_type) + .field( + "data", + &crate::memory::DebugMemoryData::from_bit_slice(*array_type, data), + ) + .finish() + } +} + +macro_rules! make_state_layout { + ( + state_plural_fields = [$(#[state] $state_plural_field:ident,)* $(#[type] $type_plural_field:ident,)*]; + state_kinds = [$(#[state] $state_kind:ident,)* $(#[type] $type_kind:ident,)*]; + ) => { + #[derive(Clone, PartialEq, Eq, Hash, Debug)] + pub(crate) struct StateLayout { + pub(crate) ty: TypeLayout, + $(pub(crate) $state_plural_field: StatePartLayout<$state_kind, BK>,)* + } + + impl Copy for StateLayout {} + + impl StateLayout { + pub(crate) fn len(&self) -> StateLen { + StateLen { + ty: self.ty.len(), + $($state_plural_field: self.$state_plural_field.len(),)* + } + } + pub(crate) fn is_empty(&self) -> bool { + self.ty.is_empty() $(&& self.$state_plural_field.is_empty())* + } + pub(crate) fn empty() -> Self { + Self { + ty: TypeLayout::empty(), + $($state_plural_field: StatePartLayout::empty(),)* + } + } + } + + impl StateLayout { + pub(crate) fn next_index(&self) -> StateIndex { + StateIndex { + ty: self.ty.next_index(), + $($state_plural_field: self.$state_plural_field.next_index(),)* + } + } + pub(crate) fn allocate( + &mut self, + layout: &StateLayout, + ) -> StateIndexRange { + StateIndexRange { + ty: self.ty.allocate(&layout.ty), + $($state_plural_field: self.$state_plural_field.allocate(&layout.$state_plural_field),)* + } + } + } + + impl From> for StateLayout { + fn from(v: StateLayout) -> Self { + Self { + ty: v.ty.into(), + $($state_plural_field: v.$state_plural_field.into(),)* + } + } + } + + #[derive(Clone, PartialEq, Eq, Hash, Debug)] + pub(crate) struct TypeLayout { + $(pub(crate) $type_plural_field: StatePartLayout<$type_kind, BK>,)* + } + + impl Copy for TypeLayout {} + + impl TypeLayout { + pub(crate) fn len(&self) -> TypeLen { + TypeLen { + $($type_plural_field: self.$type_plural_field.len(),)* + } + } + pub(crate) fn is_empty(&self) -> bool { + $(self.$type_plural_field.is_empty())&&+ + } + pub(crate) fn empty() -> Self { + Self { + $($type_plural_field: StatePartLayout::empty(),)* + } + } + pub(crate) fn with_prefixed_debug_names(&self, prefix: &str) -> Self { + Self { + $($type_plural_field: self.$type_plural_field.with_prefixed_debug_names(prefix),)* + } + } + pub(crate) fn with_anonymized_debug_info(&self) -> Self { + Self { + $($type_plural_field: self.$type_plural_field.with_anonymized_debug_info(),)* + } + } + } + + impl TypeLayout { + pub(crate) fn next_index(&self) -> TypeIndex { + TypeIndex { + $($type_plural_field: self.$type_plural_field.next_index(),)* + } + } + pub(crate) fn allocate( + &mut self, + layout: &TypeLayout, + ) -> TypeIndexRange { + TypeIndexRange { + $($type_plural_field: self.$type_plural_field.allocate(&layout.$type_plural_field),)* + } + } + } + + impl From> for TypeLayout { + fn from(v: TypeLayout) -> Self { + Self { + $($type_plural_field: v.$type_plural_field.into(),)* + } + } + } + }; +} + +get_state_part_kinds! { + make_state_layout! { + state_plural_fields; + state_kinds; + } +} + +#[derive(Clone, PartialEq, Eq, Hash)] +pub(crate) struct StatePartLayout { + pub(crate) debug_data: BK::Vec, + pub(crate) layout_data: BK::Vec, + pub(crate) _phantom: PhantomData, +} + +impl Copy for StatePartLayout {} + +impl StatePartLayout { + pub(crate) fn len(&self) -> StatePartLen { + StatePartLen::new( + self.debug_data + .len() + .try_into() + .expect("state part allocation layout is too big"), + ) + } + pub(crate) fn is_empty(&self) -> bool { + self.debug_data.is_empty() + } + pub(crate) fn empty() -> Self { + Self { + debug_data: Default::default(), + layout_data: Default::default(), + _phantom: PhantomData, + } + } + pub(crate) fn debug_data(&self, index: StatePartIndex) -> &K::DebugData { + &self.debug_data[index.as_usize()] + } +} + +impl From> + for StatePartLayout +{ + fn from(value: StatePartLayout) -> Self { + Self { + debug_data: Intern::intern_owned(value.debug_data), + layout_data: Intern::intern_owned(value.layout_data), + _phantom: PhantomData, + } + } +} + +impl StatePartLayout { + pub(crate) fn scalar(debug_data: K::DebugData, layout_data: K::LayoutData) -> Self { + Self { + debug_data: vec![debug_data], + layout_data: vec![layout_data], + _phantom: PhantomData, + } + } + pub(crate) fn next_index(&self) -> StatePartIndex { + StatePartIndex::new(self.len().value) + } + pub(crate) fn allocate( + &mut self, + layout: &StatePartLayout, + ) -> StatePartIndexRange { + let start = self.next_index(); + let len = layout.len(); + let Self { + debug_data, + layout_data, + _phantom: _, + } = self; + debug_data.extend_from_slice(&layout.debug_data); + layout_data.extend_from_slice(&layout.layout_data); + StatePartIndexRange { start, len } + } +} + +impl fmt::Debug for StatePartLayout { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let Self { + debug_data, + layout_data, + _phantom: _, + } = self; + write!(f, "StatePartLayout<{}>", K::NAME)?; + let mut debug_struct = f.debug_struct(""); + debug_struct + .field("len", &debug_data.len()) + .field("debug_data", debug_data); + if TypeId::of::() != TypeId::of::<()>() { + debug_struct.field("layout_data", layout_data); + } + debug_struct.finish_non_exhaustive() + } +} + +impl, BK: InsnsBuildingKind> StatePartLayout { + pub(crate) fn with_prefixed_debug_names(&self, prefix: &str) -> Self { + Self { + debug_data: self + .debug_data + .iter() + .map(|v| v.with_prefixed_debug_names(prefix)) + .collect(), + layout_data: self.layout_data.clone(), + _phantom: PhantomData, + } + } + pub(crate) fn with_anonymized_debug_info(&self) -> Self { + Self { + debug_data: self + .debug_data + .iter() + .map(|v| v.with_anonymized_debug_info()) + .collect(), + layout_data: self.layout_data.clone(), + _phantom: PhantomData, + } + } +} + +macro_rules! make_state_len { + ( + state_plural_fields = [$(#[state] $state_plural_field:ident,)* $(#[type] $type_plural_field:ident,)*]; + state_kinds = [$(#[state] $state_kind:ident,)* $(#[type] $type_kind:ident,)*]; + type_singular_variants = [$($type_singular_variant:ident,)*]; + type_singular_fields = [$($type_singular_field:ident,)*]; + ) => { + #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] + pub(crate) struct StateLen { + pub(crate) ty: TypeLen, + $(pub(crate) $state_plural_field: StatePartLen<$state_kind>,)* + } + + #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] + pub(crate) struct TypeLen { + $(pub(crate) $type_plural_field: StatePartLen<$type_kind>,)* + } + + #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] + pub(crate) enum TypeLenSingle { + $($type_singular_variant,)* + } + + impl TypeLen { + pub(crate) const fn as_index(self) -> TypeIndex { + TypeIndex { + $($type_plural_field: self.$type_plural_field.as_index(),)* + } + } + pub(crate) const fn empty() -> Self { + Self { + $($type_plural_field: StatePartLen::new(0),)* + } + } + $(pub(crate) const fn $type_singular_field() -> Self { + let mut retval = Self::empty(); + retval.$type_plural_field.value = 1; + retval + })* + pub(crate) const fn as_single(self) -> Option { + $({ + const SINGLE: TypeLen = TypeLen::$type_singular_field(); + if let SINGLE = self { + return Some(TypeLenSingle::$type_singular_variant); + } + })* + None + } + pub(crate) const fn only_small(mut self) -> Option> { + const EMPTY: TypeLen = TypeLen::empty(); + let retval = self.small_slots; + self.small_slots.value = 0; + if let EMPTY = self { + Some(retval) + } else { + None + } + } + } + }; +} + +get_state_part_kinds! { + make_state_len! { + state_plural_fields; + state_kinds; + type_singular_variants; + type_singular_fields; + } +} + +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub(crate) struct StatePartLen { + pub(crate) value: u32, + pub(crate) _phantom: PhantomData, +} + +impl StatePartLen { + pub(crate) const fn new(value: u32) -> Self { + Self { + value, + _phantom: PhantomData, + } + } + pub(crate) const fn as_index(self) -> StatePartIndex { + StatePartIndex::new(self.value) + } +} + +impl fmt::Debug for StatePartLen { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "StatePartLen<{}>({})", K::NAME, self.value) + } +} + +#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] +pub(crate) struct SlotDebugData { + pub(crate) name: Interned, + pub(crate) ty: CanonicalType, +} + +impl SlotDebugData { + pub(crate) fn with_prefixed_debug_names(&self, prefix: &str) -> Self { + let mut name = String::with_capacity(self.name.len() + prefix.len()); + name.push_str(prefix); + name.push_str(&self.name); + Self { + name: Intern::intern_owned(name), + ty: self.ty, + } + } + pub(crate) fn with_anonymized_debug_info(&self) -> Self { + Self { + name: Interned::default(), + ty: self.ty, + } + } +} + +macro_rules! make_state_index { + ( + state_plural_fields = [$(#[state] $state_plural_field:ident,)* $(#[type] $type_plural_field:ident,)*]; + state_kinds = [$(#[state] $state_kind:ident,)* $(#[type] $type_kind:ident,)*]; + ) => { + #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] + pub(crate) struct StateIndex { + pub(crate) ty: TypeIndex, + $(pub(crate) $state_plural_field: StatePartIndex<$state_kind>,)* + } + + #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] + pub(crate) struct TypeIndex { + $(pub(crate) $type_plural_field: StatePartIndex<$type_kind>,)* + } + + impl TypeIndex { + pub(crate) const ZERO: Self = Self { + $($type_plural_field: StatePartIndex::ZERO,)* + }; + pub(crate) fn offset(self, offset: TypeIndex) -> Self { + Self { + $($type_plural_field: self.$type_plural_field.offset(offset.$type_plural_field),)* + } + } + } + }; +} + +get_state_part_kinds! { + make_state_index! { + state_plural_fields; + state_kinds; + } +} + +macro_rules! make_state_index_range { + ( + state_plural_fields = [$(#[state] $state_plural_field:ident,)* $(#[type] $type_plural_field:ident,)*]; + state_kinds = [$(#[state] $state_kind:ident,)* $(#[type] $type_kind:ident,)*]; + copy_insns = [$($copy_insn:ident,)*]; + ) => { + #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] + pub(crate) struct StateIndexRange { + pub(crate) ty: TypeIndexRange, + $(pub(crate) $state_plural_field: StatePartIndexRange<$state_kind>,)* + } + + impl StateIndexRange { + pub(crate) fn start(self) -> StateIndex { + StateIndex { + ty: self.ty.start(), + $($state_plural_field: self.$state_plural_field.start(),)* + } + } + pub(crate) fn len(self) -> StateLen { + StateLen { + ty: self.ty.len(), + $($state_plural_field: self.$state_plural_field.len(),)* + } + } + pub(crate) fn is_empty(self) -> bool { + self.ty.is_empty() $(&& self.$state_plural_field.is_empty())* + } + } + + #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] + pub(crate) struct TypeIndexRange { + $(pub(crate) $type_plural_field: StatePartIndexRange<$type_kind>,)* + } + + impl TypeIndexRange { + pub(crate) fn new(start: TypeIndex, len: TypeLen) -> Self { + Self { + $($type_plural_field: StatePartIndexRange { + start: start.$type_plural_field, + len: len.$type_plural_field, + },)* + } + } + pub(crate) fn start(self) -> TypeIndex { + TypeIndex { + $($type_plural_field: self.$type_plural_field.start(),)* + } + } + pub(crate) fn len(self) -> TypeLen { + TypeLen { + $($type_plural_field: self.$type_plural_field.len(),)* + } + } + pub(crate) fn is_empty(self) -> bool { + $(self.$type_plural_field.is_empty()) &&+ + } + pub(crate) fn slice(self, index: TypeIndexRange) -> Self { + Self { + $($type_plural_field: self.$type_plural_field.slice(index.$type_plural_field),)* + } + } + pub(crate) fn index_array(self, element_size: TypeLen, index: usize) -> Self { + Self { + $($type_plural_field: self.$type_plural_field.index_array(element_size.$type_plural_field, index),)* + } + } + pub(crate) fn offset(self, offset: TypeIndex) -> Self { + Self { + $($type_plural_field: self.$type_plural_field.offset(offset.$type_plural_field),)* + } + } + #[must_use] + pub(crate) fn insns_for_copy_to(self, dest: TypeIndexRange) -> impl Iterator { + assert_eq!(self.len(), dest.len()); + chain!($(self.$type_plural_field.iter().zip(dest.$type_plural_field.iter()).map(|(src, dest)| Insn::$copy_insn { dest, src })),*) + } + #[must_use] + pub(crate) fn insns_for_copy_from(self, src: TypeIndexRange) -> impl Iterator { + src.insns_for_copy_to(self) + } + } + }; +} + +get_state_part_kinds! { + make_state_index_range! { + state_plural_fields; + state_kinds; + copy_insns; + } +} diff --git a/crates/fayalite/src/sim/value.rs b/crates/fayalite/src/sim/value.rs new file mode 100644 index 0000000..9717417 --- /dev/null +++ b/crates/fayalite/src/sim/value.rs @@ -0,0 +1,1344 @@ +// 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}, + source_location::SourceLocation, + ty::{ + CanonicalType, OpaqueSimValue, OpaqueSimValueSize, OpaqueSimValueSlice, + OpaqueSimValueWriter, StaticType, Type, TypeProperties, impl_match_variant_as_self, + }, + util::{ + ConstUsize, HashMap, + alternating_cell::{AlternatingCell, AlternatingCellMethods}, + }, +}; +use bitvec::{slice::BitSlice, vec::BitVec}; +use hashbrown::hash_map::Entry; +use serde::{Deserialize, Deserializer, Serialize, Serializer, de::Error as _, ser::Error as _}; +use std::{ + borrow::Cow, + fmt::{self, Write}, + hash::{BuildHasher, Hash, Hasher, RandomState}, + ops::{Deref, DerefMut}, + sync::{Arc, Mutex}, +}; + +pub(crate) mod sim_only_value_unsafe; + +pub use sim_only_value_unsafe::{ + DynSimOnly, DynSimOnlyValue, SimOnly, SimOnlyValue, SimOnlyValueTrait, +}; + +#[derive(Copy, Clone, Eq, PartialEq)] +enum ValidFlags { + BothValid = 0, + OnlyValueValid = 1, + OnlyOpaqueValid = 2, +} + +#[derive(Clone)] +struct SimValueInner { + value: T::SimValue, + opaque: OpaqueSimValue, + valid_flags: ValidFlags, + ty: T, + sim_only_values_len: usize, +} + +impl SimValueInner { + fn size(&self) -> OpaqueSimValueSize { + OpaqueSimValueSize { + bit_width: self.opaque.bit_width(), + sim_only_values_len: self.sim_only_values_len, + } + } + fn fill_opaque(&mut self) { + match self.valid_flags { + ValidFlags::BothValid | ValidFlags::OnlyOpaqueValid => {} + ValidFlags::OnlyValueValid => { + OpaqueSimValueWriter::rewrite_with(self.size(), &mut self.opaque, |writer| { + self.ty.sim_value_to_opaque(&self.value, writer) + }); + self.valid_flags = ValidFlags::BothValid; + } + } + } + fn into_opaque(mut self) -> OpaqueSimValue { + self.fill_opaque(); + self.opaque + } + fn opaque_mut(&mut self) -> &mut OpaqueSimValue { + self.fill_opaque(); + self.valid_flags = ValidFlags::OnlyOpaqueValid; + &mut self.opaque + } + fn fill_value(&mut self) { + match self.valid_flags { + ValidFlags::BothValid | ValidFlags::OnlyValueValid => {} + ValidFlags::OnlyOpaqueValid => { + self.ty + .sim_value_clone_from_opaque(&mut self.value, self.opaque.as_slice()); + self.valid_flags = ValidFlags::BothValid; + } + } + } + fn into_value(mut self) -> T::SimValue { + self.fill_value(); + self.value + } + fn value_mut(&mut self) -> &mut T::SimValue { + self.fill_value(); + self.valid_flags = ValidFlags::OnlyValueValid; + &mut self.value + } +} + +impl AlternatingCellMethods for SimValueInner { + fn unique_to_shared(&mut self) { + match self.valid_flags { + ValidFlags::BothValid => return, + ValidFlags::OnlyValueValid => { + OpaqueSimValueWriter::rewrite_with(self.size(), &mut self.opaque, |writer| { + self.ty.sim_value_to_opaque(&self.value, writer) + }) + } + ValidFlags::OnlyOpaqueValid => self + .ty + .sim_value_clone_from_opaque(&mut self.value, self.opaque.as_slice()), + } + self.valid_flags = ValidFlags::BothValid; + } + + fn shared_to_unique(&mut self) {} +} + +#[derive(Serialize, Deserialize)] +#[serde(rename = "SimValue")] +#[serde(bound( + serialize = "T: Type + Serialize", + deserialize = "T: Type> + Deserialize<'de>" +))] +struct SerdeSimValue<'a, T: Type> { + ty: T, + value: std::borrow::Cow<'a, T::SimValue>, +} + +impl + Serialize> Serialize for SimValue { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + SerdeSimValue { + ty: SimValue::ty(self), + value: std::borrow::Cow::Borrowed(&*self), + } + .serialize(serializer) + } +} + +impl<'de, T: Type> + Deserialize<'de>> Deserialize<'de> for SimValue { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let SerdeSimValue { ty, value } = SerdeSimValue::::deserialize(deserializer)?; + Ok(SimValue::from_value(ty, value.into_owned())) + } +} + +pub struct SimValue { + inner: AlternatingCell>, +} + +impl Clone for SimValue { + fn clone(&self) -> Self { + Self { + inner: AlternatingCell::new_unique(self.inner.share().clone()), + } + } +} + +impl SimValue { + #[track_caller] + pub fn from_opaque(ty: T, opaque: OpaqueSimValue) -> Self { + let size = ty.canonical().size(); + assert_eq!(size, opaque.size()); + let inner = SimValueInner { + value: ty.sim_value_from_opaque(opaque.as_slice()), + opaque, + valid_flags: ValidFlags::BothValid, + ty, + sim_only_values_len: size.sim_only_values_len, + }; + Self { + inner: AlternatingCell::new_shared(inner), + } + } + #[track_caller] + pub fn from_bitslice(ty: T, bits: &BitSlice) -> Self { + Self::from_bitslice_and_sim_only_values(ty, bits, Vec::new()) + } + #[track_caller] + pub fn from_bitslice_and_sim_only_values( + ty: T, + bits: &BitSlice, + sim_only_values: Vec, + ) -> Self { + Self::from_opaque( + ty, + OpaqueSimValue::from_bitslice_and_sim_only_values(bits, sim_only_values), + ) + } + pub fn from_value(ty: T, value: T::SimValue) -> Self { + let type_properties = ty.canonical().type_properties(); + let inner = SimValueInner { + opaque: OpaqueSimValue::from_bits_and_sim_only_values( + UIntValue::new_dyn(Arc::new(BitVec::repeat(false, type_properties.bit_width))), + Vec::with_capacity(type_properties.sim_only_values_len), + ), + value, + valid_flags: ValidFlags::OnlyValueValid, + ty, + sim_only_values_len: type_properties.sim_only_values_len, + }; + Self { + inner: AlternatingCell::new_unique(inner), + } + } + pub fn ty(this: &Self) -> T { + this.inner.share().ty + } + pub fn into_opaque(this: Self) -> OpaqueSimValue { + this.inner.into_inner().into_opaque() + } + pub fn into_ty_and_opaque(this: Self) -> (T, OpaqueSimValue) { + let inner = this.inner.into_inner(); + (inner.ty, inner.into_opaque()) + } + pub fn opaque(this: &Self) -> &OpaqueSimValue { + &this.inner.share().opaque + } + pub fn opaque_mut(this: &mut Self) -> &mut OpaqueSimValue { + this.inner.unique().opaque_mut() + } + pub fn bits(this: &Self) -> &UIntValue { + Self::opaque(this).bits() + } + pub fn bits_mut(this: &mut Self) -> &mut UIntValue { + Self::opaque_mut(this).bits_mut() + } + pub fn into_value(this: Self) -> T::SimValue { + this.inner.into_inner().into_value() + } + pub fn value(this: &Self) -> &T::SimValue { + &this.inner.share().value + } + pub fn value_mut(this: &mut Self) -> &mut T::SimValue { + this.inner.unique().value_mut() + } + #[track_caller] + pub fn from_canonical(v: SimValue) -> Self { + let (ty, opaque) = SimValue::into_ty_and_opaque(v); + Self::from_opaque(T::from_canonical(ty), opaque) + } + pub fn into_canonical(this: Self) -> SimValue { + let (ty, opaque) = Self::into_ty_and_opaque(this); + SimValue::from_opaque(ty.canonical(), opaque) + } + pub fn canonical(this: &Self) -> SimValue { + SimValue::from_opaque(Self::ty(this).canonical(), Self::opaque(this).clone()) + } + #[track_caller] + pub fn from_dyn_int(v: SimValue) -> Self + where + T: IntType, + { + let (ty, opaque) = SimValue::into_ty_and_opaque(v); + SimValue::from_opaque(T::from_dyn_int(ty), opaque) + } + pub fn into_dyn_int(this: Self) -> SimValue + where + T: IntType, + { + let (ty, opaque) = Self::into_ty_and_opaque(this); + SimValue::from_opaque(ty.as_dyn_int(), opaque) + } + pub fn to_dyn_int(this: &Self) -> SimValue + where + T: IntType, + { + SimValue::from_opaque(Self::ty(this).as_dyn_int(), Self::opaque(&this).clone()) + } + #[track_caller] + pub fn from_bundle(v: SimValue) -> Self + where + T: BundleType, + { + let (ty, opaque) = SimValue::into_ty_and_opaque(v); + SimValue::from_opaque(T::from_canonical(CanonicalType::Bundle(ty)), opaque) + } + pub fn into_bundle(this: Self) -> SimValue + where + T: BundleType, + { + let (ty, opaque) = Self::into_ty_and_opaque(this); + SimValue::from_opaque(Bundle::from_canonical(ty.canonical()), opaque) + } + pub fn to_bundle(this: &Self) -> SimValue + where + T: BundleType, + { + SimValue::from_opaque( + Bundle::from_canonical(Self::ty(this).canonical()), + Self::opaque(&this).clone(), + ) + } + #[track_caller] + pub fn from_enum(v: SimValue) -> Self + where + T: EnumType, + { + let (ty, opaque) = SimValue::into_ty_and_opaque(v); + SimValue::from_opaque(T::from_canonical(CanonicalType::Enum(ty)), opaque) + } + pub fn into_enum(this: Self) -> SimValue + where + T: EnumType, + { + let (ty, opaque) = Self::into_ty_and_opaque(this); + SimValue::from_opaque(Enum::from_canonical(ty.canonical()), opaque) + } + pub fn to_enum(this: &Self) -> SimValue + where + T: EnumType, + { + SimValue::from_opaque( + Enum::from_canonical(Self::ty(this).canonical()), + Self::opaque(&this).clone(), + ) + } +} + +impl Deref for SimValue { + type Target = T::SimValue; + + fn deref(&self) -> &Self::Target { + Self::value(self) + } +} + +impl DerefMut for SimValue { + fn deref_mut(&mut self) -> &mut Self::Target { + Self::value_mut(self) + } +} + +impl fmt::Debug for SimValue { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let inner = self.inner.share(); + f.debug_struct("SimValue") + .field("ty", &inner.ty) + .field("value", &inner.value) + .finish() + } +} + +impl ToExpr for SimValue { + type Type = T; + + #[track_caller] + fn to_expr(&self) -> Expr { + let inner = self.inner.share(); + assert_eq!( + inner.sim_only_values_len, 0, + "can't convert sim-only values to Expr" + ); + inner.opaque.bits().cast_bits_to(inner.ty) + } +} + +pub trait SimValuePartialEq: Type { + #[track_caller] + fn sim_value_eq(this: &SimValue, other: &SimValue) -> bool; +} + +impl, U: Type> PartialEq> for SimValue { + #[track_caller] + fn eq(&self, other: &SimValue) -> bool { + T::sim_value_eq(self, other) + } +} + +impl SimValuePartialEq for UIntType { + fn sim_value_eq(this: &SimValue, other: &SimValue) -> bool { + **this == **other + } +} + +impl SimValuePartialEq for SIntType { + fn sim_value_eq(this: &SimValue, other: &SimValue) -> bool { + **this == **other + } +} + +impl SimValuePartialEq for Bool { + fn sim_value_eq(this: &SimValue, other: &SimValue) -> bool { + **this == **other + } +} + +pub trait ToSimValue: ToSimValueWithType<::Type> { + type Type: Type; + + #[track_caller] + fn to_sim_value(&self) -> SimValue; + #[track_caller] + fn into_sim_value(self) -> SimValue + where + Self: Sized, + { + self.to_sim_value() + } + #[track_caller] + fn arc_into_sim_value(self: Arc) -> SimValue { + self.to_sim_value() + } + #[track_caller] + fn arc_to_sim_value(self: &Arc) -> SimValue { + self.to_sim_value() + } +} + +pub trait ToSimValueWithType { + #[track_caller] + fn to_sim_value_with_type(&self, ty: T) -> SimValue; + #[track_caller] + fn into_sim_value_with_type(self, ty: T) -> SimValue + where + Self: Sized, + { + self.to_sim_value_with_type(ty) + } + #[track_caller] + fn arc_into_sim_value_with_type(self: Arc, ty: T) -> SimValue { + self.to_sim_value_with_type(ty) + } + #[track_caller] + fn arc_to_sim_value_with_type(self: &Arc, ty: T) -> SimValue { + self.to_sim_value_with_type(ty) + } +} + +macro_rules! forward_to_sim_value_with_type { + ([$($generics:tt)*] $ty:ty) => { + impl<$($generics)*> ToSimValueWithType<::Type> for $ty { + fn to_sim_value_with_type(&self, ty: ::Type) -> SimValue<::Type> { + let retval = Self::to_sim_value(self); + assert_eq!(SimValue::ty(&retval), ty); + retval + } + #[track_caller] + fn into_sim_value_with_type(self, ty: ::Type) -> SimValue<::Type> + where + Self: Sized, + { + let retval = Self::into_sim_value(self); + assert_eq!(SimValue::ty(&retval), ty); + retval + } + #[track_caller] + fn arc_into_sim_value_with_type(self: Arc, ty: ::Type) -> SimValue<::Type> { + let retval = Self::arc_into_sim_value(self); + assert_eq!(SimValue::ty(&retval), ty); + retval + } + #[track_caller] + fn arc_to_sim_value_with_type(self: &Arc, ty: ::Type) -> SimValue<::Type> { + let retval = Self::arc_to_sim_value(self); + assert_eq!(SimValue::ty(&retval), ty); + retval + } + } + }; +} + +impl ToSimValue for SimValue { + type Type = T; + fn to_sim_value(&self) -> SimValue { + self.clone() + } + + fn into_sim_value(self) -> SimValue { + self + } +} + +forward_to_sim_value_with_type!([T: Type] SimValue); + +impl ToSimValueWithType for BitVec { + #[track_caller] + fn to_sim_value_with_type(&self, ty: T) -> SimValue { + self.clone().into_sim_value_with_type(ty) + } + + #[track_caller] + fn into_sim_value_with_type(self, ty: T) -> SimValue { + Arc::new(self).arc_into_sim_value_with_type(ty) + } + + #[track_caller] + fn arc_into_sim_value_with_type(self: Arc, ty: T) -> SimValue { + SimValue::from_opaque(ty, OpaqueSimValue::from_bits(UIntValue::new_dyn(self))) + } + + #[track_caller] + fn arc_to_sim_value_with_type(self: &Arc, ty: T) -> SimValue { + SimValue::from_opaque( + ty, + OpaqueSimValue::from_bits(UIntValue::new_dyn(self.clone())), + ) + } +} + +impl ToSimValueWithType for bitvec::boxed::BitBox { + #[track_caller] + fn to_sim_value_with_type(&self, ty: T) -> SimValue { + self.clone().into_sim_value_with_type(ty) + } + + #[track_caller] + fn into_sim_value_with_type(self, ty: T) -> SimValue { + self.into_bitvec().into_sim_value_with_type(ty) + } +} + +impl ToSimValueWithType for BitSlice { + #[track_caller] + fn to_sim_value_with_type(&self, ty: T) -> SimValue { + self.to_bitvec().into_sim_value_with_type(ty) + } +} + +impl ToSimValue for &'_ This { + type Type = This::Type; + + fn to_sim_value(&self) -> SimValue { + This::to_sim_value(self) + } +} + +impl, T: Type> ToSimValueWithType for &'_ This { + fn to_sim_value_with_type(&self, ty: T) -> SimValue { + This::to_sim_value_with_type(self, ty) + } +} + +impl ToSimValue for &'_ mut This { + type Type = This::Type; + + fn to_sim_value(&self) -> SimValue { + This::to_sim_value(self) + } +} + +impl, T: Type> ToSimValueWithType for &'_ mut This { + fn to_sim_value_with_type(&self, ty: T) -> SimValue { + This::to_sim_value_with_type(self, ty) + } +} + +impl ToSimValue for Arc { + type Type = This::Type; + + fn to_sim_value(&self) -> SimValue { + This::arc_to_sim_value(self) + } + fn into_sim_value(self) -> SimValue { + This::arc_into_sim_value(self) + } +} + +impl, T: Type> ToSimValueWithType for Arc { + fn to_sim_value_with_type(&self, ty: T) -> SimValue { + This::arc_to_sim_value_with_type(self, ty) + } + fn into_sim_value_with_type(self, ty: T) -> SimValue { + This::arc_into_sim_value_with_type(self, ty) + } +} + +impl ToSimValue + for crate::intern::Interned +{ + type Type = This::Type; + fn to_sim_value(&self) -> SimValue { + This::to_sim_value(self) + } +} + +impl + Send + Sync + 'static, T: Type> ToSimValueWithType + for crate::intern::Interned +{ + fn to_sim_value_with_type(&self, ty: T) -> SimValue { + This::to_sim_value_with_type(self, ty) + } +} + +impl ToSimValue for Box { + type Type = This::Type; + + fn to_sim_value(&self) -> SimValue { + This::to_sim_value(self) + } + fn into_sim_value(self) -> SimValue { + This::into_sim_value(*self) + } +} + +impl, T: Type> ToSimValueWithType for Box { + fn to_sim_value_with_type(&self, ty: T) -> SimValue { + This::to_sim_value_with_type(self, ty) + } + fn into_sim_value_with_type(self, ty: T) -> SimValue { + This::into_sim_value_with_type(*self, ty) + } +} + +impl SimValue> { + #[track_caller] + pub fn from_array_elements>>( + ty: ArrayType, + 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, T: Type> ToSimValueWithType> for [Element] { + #[track_caller] + fn to_sim_value_with_type(&self, ty: Array) -> SimValue> { + SimValue::from_array_elements(ty, self) + } +} + +impl> ToSimValue for [Element] { + type Type = Array; + + #[track_caller] + fn to_sim_value(&self) -> SimValue { + SimValue::from_array_elements(ArrayType::new_dyn(StaticType::TYPE, self.len()), self) + } +} + +impl> ToSimValueWithType for [Element] { + #[track_caller] + fn to_sim_value_with_type(&self, ty: CanonicalType) -> SimValue { + SimValue::into_canonical(SimValue::from_array_elements( + ::from_canonical(ty), + self, + )) + } +} + +impl, T: Type, const N: usize> ToSimValueWithType> + for [Element; N] +where + ConstUsize: KnownSize, +{ + #[track_caller] + fn to_sim_value_with_type(&self, ty: Array) -> SimValue> { + SimValue::from_array_elements(ty, self) + } + #[track_caller] + fn into_sim_value_with_type(self, ty: Array) -> SimValue> { + SimValue::from_array_elements(ty, self) + } +} + +impl, const N: usize> ToSimValue for [Element; N] +where + ConstUsize: KnownSize, +{ + type Type = Array; + + fn to_sim_value(&self) -> SimValue { + SimValue::from_array_elements(StaticType::TYPE, self) + } + + fn into_sim_value(self) -> SimValue { + SimValue::from_array_elements(StaticType::TYPE, self) + } +} + +impl, T: Type, const N: usize> ToSimValueWithType> + for [Element; N] +{ + #[track_caller] + fn to_sim_value_with_type(&self, ty: Array) -> SimValue> { + SimValue::from_array_elements(ty, self) + } + #[track_caller] + fn into_sim_value_with_type(self, ty: Array) -> SimValue> { + SimValue::from_array_elements(ty, self) + } +} + +impl, const N: usize> ToSimValueWithType + for [Element; N] +{ + #[track_caller] + fn to_sim_value_with_type(&self, ty: CanonicalType) -> SimValue { + SimValue::into_canonical(SimValue::from_array_elements( + ::from_canonical(ty), + self, + )) + } + #[track_caller] + fn into_sim_value_with_type(self, ty: CanonicalType) -> SimValue { + SimValue::into_canonical(SimValue::from_array_elements( + ::from_canonical(ty), + self, + )) + } +} + +impl, T: Type> ToSimValueWithType> for Vec { + #[track_caller] + fn to_sim_value_with_type(&self, ty: Array) -> SimValue> { + SimValue::from_array_elements(ty, self) + } + #[track_caller] + fn into_sim_value_with_type(self, ty: Array) -> SimValue> { + SimValue::from_array_elements(ty, self) + } +} + +impl> ToSimValue for Vec { + type Type = Array; + + fn to_sim_value(&self) -> SimValue { + SimValue::from_array_elements(ArrayType::new_dyn(StaticType::TYPE, self.len()), self) + } + + fn into_sim_value(self) -> SimValue { + SimValue::from_array_elements(ArrayType::new_dyn(StaticType::TYPE, self.len()), self) + } +} + +impl> ToSimValueWithType + for Vec +{ + #[track_caller] + fn to_sim_value_with_type(&self, ty: CanonicalType) -> SimValue { + SimValue::into_canonical(SimValue::from_array_elements( + ::from_canonical(ty), + self, + )) + } + #[track_caller] + fn into_sim_value_with_type(self, ty: CanonicalType) -> SimValue { + SimValue::into_canonical(SimValue::from_array_elements( + ::from_canonical(ty), + self, + )) + } +} + +impl, T: Type> ToSimValueWithType> for Box<[Element]> { + #[track_caller] + fn to_sim_value_with_type(&self, ty: Array) -> SimValue> { + SimValue::from_array_elements(ty, self) + } + #[track_caller] + fn into_sim_value_with_type(self, ty: Array) -> SimValue> { + SimValue::from_array_elements(ty, self) + } +} + +impl> ToSimValue for Box<[Element]> { + type Type = Array; + + fn to_sim_value(&self) -> SimValue { + SimValue::from_array_elements(ArrayType::new_dyn(StaticType::TYPE, self.len()), self) + } + + fn into_sim_value(self) -> SimValue { + SimValue::from_array_elements(ArrayType::new_dyn(StaticType::TYPE, self.len()), self) + } +} + +impl> ToSimValueWithType + for Box<[Element]> +{ + #[track_caller] + fn to_sim_value_with_type(&self, ty: CanonicalType) -> SimValue { + SimValue::into_canonical(SimValue::from_array_elements( + ::from_canonical(ty), + self, + )) + } + #[track_caller] + fn into_sim_value_with_type(self, ty: CanonicalType) -> SimValue { + SimValue::into_canonical(SimValue::from_array_elements( + ::from_canonical(ty), + self, + )) + } +} + +impl ToSimValue for Expr { + type Type = T; + #[track_caller] + fn to_sim_value(&self) -> SimValue { + SimValue::from_bitslice( + Expr::ty(*self), + &crate::expr::ToLiteralBits::to_literal_bits(self) + .expect("must be a literal expression"), + ) + } +} + +forward_to_sim_value_with_type!([T: Type] Expr); + +macro_rules! impl_to_sim_value_for_bool_like { + ($ty:ident) => { + impl ToSimValueWithType<$ty> for bool { + fn to_sim_value_with_type(&self, ty: $ty) -> SimValue<$ty> { + SimValue::from_value(ty, *self) + } + } + }; +} + +impl ToSimValue for bool { + type Type = Bool; + + fn to_sim_value(&self) -> SimValue { + SimValue::from_value(Bool, *self) + } +} + +impl_to_sim_value_for_bool_like!(Bool); +impl_to_sim_value_for_bool_like!(AsyncReset); +impl_to_sim_value_for_bool_like!(SyncReset); +impl_to_sim_value_for_bool_like!(Reset); +impl_to_sim_value_for_bool_like!(Clock); + +impl ToSimValueWithType for bool { + #[track_caller] + fn to_sim_value_with_type(&self, ty: CanonicalType) -> SimValue { + match ty { + CanonicalType::UInt(_) + | CanonicalType::SInt(_) + | CanonicalType::Array(_) + | CanonicalType::Enum(_) + | CanonicalType::Bundle(_) + | CanonicalType::PhantomConst(_) + | CanonicalType::DynSimOnly(_) => { + panic!("can't create SimValue from bool: expected value of type: {ty:?}"); + } + CanonicalType::Bool(_) + | CanonicalType::AsyncReset(_) + | CanonicalType::SyncReset(_) + | CanonicalType::Reset(_) + | CanonicalType::Clock(_) => SimValue::from_opaque( + ty, + OpaqueSimValue::from_bits(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 { + 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 for $prim { + #[track_caller] + fn to_sim_value_with_type(&self, ty: CanonicalType) -> SimValue { + let ty: <<$prim as ToExpr>::Type as IntType>::Dyn = Type::from_canonical(ty); + SimValue::into_canonical(self.to_sim_value_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 ToSimValue for $IntValue { + type Type = $IntType; + + fn to_sim_value(&self) -> SimValue { + SimValue::from_value(self.ty(), self.clone()) + } + + fn into_sim_value(self) -> SimValue { + SimValue::from_value(self.ty(), self) + } + } + + impl ToSimValueWithType<$IntType> for $IntValue { + fn to_sim_value_with_type(&self, ty: $IntType) -> SimValue<$IntType> { + SimValue::from_value(ty, self.clone()) + } + + fn into_sim_value_with_type(self, ty: $IntType) -> SimValue<$IntType> { + SimValue::from_value(ty, self) + } + } + + impl ToSimValueWithType<$Int> for $IntValue { + fn to_sim_value_with_type(&self, ty: $Int) -> SimValue<$Int> { + self.bits().to_sim_value_with_type(ty) + } + + fn into_sim_value_with_type(self, ty: $Int) -> SimValue<$Int> { + self.into_bits().into_sim_value_with_type(ty) + } + } + + impl ToSimValueWithType for $IntValue { + #[track_caller] + fn to_sim_value_with_type(&self, ty: CanonicalType) -> SimValue { + SimValue::into_canonical( + self.to_sim_value_with_type($IntType::::from_canonical(ty)), + ) + } + + #[track_caller] + fn into_sim_value_with_type(self, ty: CanonicalType) -> SimValue { + SimValue::into_canonical( + self.into_sim_value_with_type($IntType::::from_canonical(ty)), + ) + } + } + }; +} + +impl_to_sim_value_for_int_value!(UIntValue, UInt, UIntType); +impl_to_sim_value_for_int_value!(SIntValue, SInt, SIntType); + +#[derive(Default)] +struct DynSimOnlySerdeTableRest { + from_serde: HashMap, + serde_id_random_state: RandomState, + buffer: String, +} + +impl DynSimOnlySerdeTableRest { + #[cold] + fn add_new(&mut self, ty: DynSimOnly) -> DynSimOnlySerdeId { + let mut try_number = 0u64; + let mut hasher = self.serde_id_random_state.build_hasher(); + // extract more bits of randomness from TypeId -- its Hash impl only hashes 64-bits + write!(self.buffer, "{:?}", ty.type_id()).expect("shouldn't ever fail"); + self.buffer.hash(&mut hasher); + loop { + let mut hasher = hasher.clone(); + try_number.hash(&mut hasher); + try_number += 1; + let retval = DynSimOnlySerdeId(std::array::from_fn(|i| { + let mut hasher = hasher.clone(); + i.hash(&mut hasher); + hasher.finish() as u32 + })); + match self.from_serde.entry(retval) { + Entry::Occupied(_) => continue, + Entry::Vacant(e) => { + e.insert(ty); + return retval; + } + } + } + } +} + +#[derive(Default)] +struct DynSimOnlySerdeTable { + to_serde: HashMap, + rest: DynSimOnlySerdeTableRest, +} + +static DYN_SIM_ONLY_VALUE_TYPE_SERDE_TABLE: Mutex> = Mutex::new(None); + +#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, Serialize, Deserialize)] +#[serde(transparent)] +struct DynSimOnlySerdeId([u32; 4]); + +impl From for DynSimOnlySerdeId { + fn from(ty: DynSimOnly) -> Self { + let mut locked = DYN_SIM_ONLY_VALUE_TYPE_SERDE_TABLE + .lock() + .expect("shouldn't be poison"); + let DynSimOnlySerdeTable { to_serde, rest } = locked.get_or_insert_default(); + match to_serde.entry(ty) { + Entry::Occupied(occupied_entry) => *occupied_entry.get(), + Entry::Vacant(vacant_entry) => *vacant_entry.insert(rest.add_new(ty)), + } + } +} + +impl DynSimOnlySerdeId { + fn ty(self) -> Option { + let locked = DYN_SIM_ONLY_VALUE_TYPE_SERDE_TABLE + .lock() + .expect("shouldn't be poison"); + Some(*locked.as_ref()?.rest.from_serde.get(&self)?) + } +} + +#[derive(Clone, Eq, PartialEq, Hash, Debug, Serialize, Deserialize)] +struct DynSimOnlySerde<'a> { + random_id: DynSimOnlySerdeId, + #[serde(borrow)] + type_name: Cow<'a, str>, +} + +impl Serialize for DynSimOnly { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + DynSimOnlySerde { + random_id: (*self).into(), + type_name: Cow::Borrowed(self.type_name()), + } + .serialize(serializer) + } +} + +impl<'de> Deserialize<'de> for DynSimOnly { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let deserialized = DynSimOnlySerde::deserialize(deserializer)?; + let retval = deserialized + .random_id + .ty() + .filter(|ty| ty.type_name() == deserialized.type_name); + retval.ok_or_else(|| { + D::Error::custom( + "doesn't match any DynSimOnly that was serialized this time this program was run", + ) + }) + } +} + +impl DynSimOnly { + pub const fn type_properties(self) -> TypeProperties { + TypeProperties { + is_passive: true, + is_storable: true, + is_castable_from_bits: false, + bit_width: 0, + sim_only_values_len: 1, + } + } + pub fn can_connect(self, other: Self) -> bool { + self == other + } +} + +impl Type for DynSimOnly { + type BaseType = DynSimOnly; + type MaskType = Bool; + type SimValue = DynSimOnlyValue; + + impl_match_variant_as_self!(); + + fn mask_type(&self) -> Self::MaskType { + Bool + } + + fn canonical(&self) -> CanonicalType { + CanonicalType::DynSimOnly(*self) + } + + fn from_canonical(canonical_type: CanonicalType) -> Self { + let CanonicalType::DynSimOnly(v) = canonical_type else { + panic!("expected DynSimOnly"); + }; + v + } + + fn source_location() -> SourceLocation { + SourceLocation::builtin() + } + + fn sim_value_from_opaque(&self, opaque: OpaqueSimValueSlice<'_>) -> Self::SimValue { + assert_eq!(opaque.size(), self.type_properties().size()); + opaque.sim_only_values()[0].clone() + } + + fn sim_value_clone_from_opaque( + &self, + value: &mut Self::SimValue, + opaque: OpaqueSimValueSlice<'_>, + ) { + assert_eq!(opaque.size(), self.type_properties().size()); + value.clone_from(&opaque.sim_only_values()[0]); + } + + fn sim_value_to_opaque<'w>( + &self, + value: &Self::SimValue, + writer: OpaqueSimValueWriter<'w>, + ) -> crate::ty::OpaqueSimValueWritten<'w> { + writer.fill_cloned_from_slice(OpaqueSimValueSlice::from_parts( + BitSlice::empty(), + std::array::from_ref(value), + )) + } +} + +impl Type for SimOnly { + type BaseType = DynSimOnly; + type MaskType = Bool; + type SimValue = SimOnlyValue; + + impl_match_variant_as_self!(); + + fn mask_type(&self) -> Self::MaskType { + Bool + } + + fn canonical(&self) -> CanonicalType { + DynSimOnly::from(*self).canonical() + } + + fn from_canonical(canonical_type: CanonicalType) -> Self { + DynSimOnly::from_canonical(canonical_type) + .downcast() + .expect("got wrong SimOnly") + } + + fn source_location() -> SourceLocation { + SourceLocation::builtin() + } + + fn sim_value_from_opaque(&self, opaque: OpaqueSimValueSlice<'_>) -> Self::SimValue { + assert_eq!(Self::TYPE_PROPERTIES.size(), opaque.size()); + SimOnlyValue::new( + opaque.sim_only_values()[0] + .downcast_ref::() + .expect("type mismatch") + .clone(), + ) + } + + fn sim_value_clone_from_opaque( + &self, + value: &mut Self::SimValue, + opaque: OpaqueSimValueSlice<'_>, + ) { + assert_eq!(Self::TYPE_PROPERTIES.size(), opaque.size()); + *value = match opaque.sim_only_values()[0].clone().downcast::() { + Ok(v) => v, + Err(v) => panic!( + "type mismatch: expected {:?}, got {:?}", + DynSimOnly::of::(), + v.ty() + ), + }; + } + + fn sim_value_to_opaque<'w>( + &self, + value: &Self::SimValue, + writer: OpaqueSimValueWriter<'w>, + ) -> crate::ty::OpaqueSimValueWritten<'w> { + SimOnlyValue::with_dyn_ref(value, |value| { + writer.fill_cloned_from_slice(OpaqueSimValueSlice::from_parts( + BitSlice::empty(), + std::array::from_ref(value), + )) + }) + } +} + +impl StaticType for SimOnly { + const TYPE: Self = Self::new(); + + const MASK_TYPE: Self::MaskType = Bool; + + const TYPE_PROPERTIES: TypeProperties = DynSimOnly::of::().type_properties(); + + const MASK_TYPE_PROPERTIES: TypeProperties = Bool::TYPE_PROPERTIES; +} + +impl fmt::Debug for SimOnlyValue { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + Self::with_dyn_ref(self, |this| fmt::Debug::fmt(this, f)) + } +} + +#[derive(Serialize, Deserialize)] +#[serde(rename = "SimOnlyValue")] +struct SerdeSimOnlyValue<'a> { + ty: DynSimOnly, + #[serde(borrow)] + value: Cow<'a, str>, +} + +impl Serialize for DynSimOnlyValue { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + SerdeSimOnlyValue { + ty: self.ty(), + value: Cow::Owned(self.serialize_to_json_string().map_err(S::Error::custom)?), + } + .serialize(serializer) + } +} + +impl<'de> Deserialize<'de> for DynSimOnlyValue { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let SerdeSimOnlyValue { ty, value } = Deserialize::deserialize(deserializer)?; + ty.deserialize_from_json_string(&value) + .map_err(D::Error::custom) + } +} + +impl ToSimValueWithType for DynSimOnlyValue { + #[track_caller] + fn to_sim_value_with_type(&self, ty: DynSimOnly) -> SimValue { + assert_eq!(self.ty(), ty, "mismatched type"); + SimValue::from_value(ty, self.clone()) + } + #[track_caller] + fn into_sim_value_with_type(self, ty: DynSimOnly) -> SimValue { + assert_eq!(self.ty(), ty, "mismatched type"); + SimValue::from_value(ty, self) + } +} + +impl ToSimValueWithType> for SimOnlyValue { + fn to_sim_value_with_type(&self, ty: SimOnly) -> SimValue> { + SimValue::from_value(ty, self.clone()) + } + fn into_sim_value_with_type(self, ty: SimOnly) -> SimValue> { + SimValue::from_value(ty, self) + } +} + +impl ToSimValue for DynSimOnlyValue { + type Type = DynSimOnly; + + fn to_sim_value(&self) -> SimValue { + SimValue::from_value(self.ty(), self.clone()) + } + + fn into_sim_value(self) -> SimValue { + SimValue::from_value(self.ty(), self) + } +} + +impl ToSimValue for SimOnlyValue { + type Type = SimOnly; + + fn to_sim_value(&self) -> SimValue { + SimValue::from_value(Default::default(), self.clone()) + } + + fn into_sim_value(self) -> SimValue { + SimValue::from_value(Default::default(), self) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use std::{any, collections::BTreeMap}; + + #[test] + fn test_sim_value_serde() -> eyre::Result<()> { + type Inner = Option>>; + let original = SimOnlyValue::::new(Some(BTreeMap::from_iter([ + (String::new(), Some(3)), + (String::from("foo"), None), + ]))); + let json = SimOnlyValue::with_dyn_ref(&original, serde_json::to_string_pretty)?; + #[derive(Deserialize)] + #[serde(deny_unknown_fields)] + struct ExpectedTy { + random_id: [u32; 4], + type_name: String, + } + #[derive(Deserialize)] + #[serde(deny_unknown_fields)] + struct Expected { + ty: ExpectedTy, + value: String, + } + let Expected { + ty: ExpectedTy { + random_id, + type_name, + }, + value, + } = serde_json::from_str(&json)?; + let _ = random_id; + assert_eq!(type_name, any::type_name::()); + assert_eq!(value, r#"{"":3,"foo":null}"#); + let deserialized: DynSimOnlyValue = serde_json::from_str(&json)?; + assert_eq!(Some(&*original), deserialized.downcast_ref::()); + Ok(()) + } +} diff --git a/crates/fayalite/src/sim/value/sim_only_value_unsafe.rs b/crates/fayalite/src/sim/value/sim_only_value_unsafe.rs new file mode 100644 index 0000000..98a199c --- /dev/null +++ b/crates/fayalite/src/sim/value/sim_only_value_unsafe.rs @@ -0,0 +1,304 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// See Notices.txt for copyright information + +//! `unsafe` parts of [`DynSimOnlyValue`] + +use serde::{Serialize, de::DeserializeOwned}; +use std::{ + any::{self, TypeId}, + fmt, + hash::{Hash, Hasher}, + marker::PhantomData, + mem::ManuallyDrop, + rc::Rc, +}; + +pub trait SimOnlyValueTrait: + 'static + Eq + Hash + fmt::Debug + Serialize + DeserializeOwned + Clone + Default +{ +} + +impl + SimOnlyValueTrait for T +{ +} + +/// Safety: `type_id_dyn` must return `TypeId::of::()` where `Self = SimOnly` +unsafe trait DynSimOnlyTrait: 'static + Send + Sync { + fn type_id_dyn(&self) -> TypeId; + fn type_name(&self) -> &'static str; + fn default_value(&self) -> Rc; + fn deserialize_from_json_string( + &self, + json_str: &str, + ) -> serde_json::Result>; +} + +/// Safety: `type_id_dyn` is implemented correctly +unsafe impl DynSimOnlyTrait for SimOnly { + fn type_id_dyn(&self) -> TypeId { + TypeId::of::() + } + + fn type_name(&self) -> &'static str { + any::type_name::() + } + + fn default_value(&self) -> Rc { + Rc::new(T::default()) + } + + fn deserialize_from_json_string( + &self, + json_str: &str, + ) -> serde_json::Result> { + Ok(Rc::::new(serde_json::from_str(json_str)?)) + } +} + +/// Safety: +/// * `type_id_dyn()` must return `TypeId::of::()`. +/// * `ty().type_id()` must return `TypeId::of::()`. +unsafe trait DynSimOnlyValueTrait: 'static + fmt::Debug { + fn type_id_dyn(&self) -> TypeId; + fn ty(&self) -> DynSimOnly; + fn eq_dyn(&self, other: &dyn DynSimOnlyValueTrait) -> bool; + fn serialize_to_json_string(&self) -> serde_json::Result; + fn hash_dyn(&self, state: &mut dyn Hasher); +} + +impl dyn DynSimOnlyValueTrait { + fn is(&self) -> bool { + Self::type_id_dyn(self) == TypeId::of::() + } + + fn downcast_ref(&self) -> Option<&T> { + if Self::is::(self) { + // Safety: checked that `Self` is really `T` + Some(unsafe { &*(self as *const Self as *const T) }) + } else { + None + } + } + + fn downcast_rc(self: Rc) -> Result, Rc> { + if Self::is::(&*self) { + // Safety: checked that `Self` is really `T` + Ok(unsafe { Rc::from_raw(Rc::into_raw(self) as *const T) }) + } else { + Err(self) + } + } +} + +/// Safety: +/// * `type_id_dyn()` returns `TypeId::of::()`. +/// * `ty().type_id()` returns `TypeId::of::()`. +unsafe impl DynSimOnlyValueTrait for T { + fn type_id_dyn(&self) -> TypeId { + TypeId::of::() + } + + fn ty(&self) -> DynSimOnly { + DynSimOnly::of::() + } + + fn eq_dyn(&self, other: &dyn DynSimOnlyValueTrait) -> bool { + other.downcast_ref::().is_some_and(|other| self == other) + } + + fn serialize_to_json_string(&self) -> serde_json::Result { + serde_json::to_string(self) + } + + fn hash_dyn(&self, mut state: &mut dyn Hasher) { + self.hash(&mut state); + } +} + +#[derive(Copy, Clone)] +pub struct DynSimOnly { + ty: &'static dyn DynSimOnlyTrait, +} + +impl DynSimOnly { + pub const fn of() -> Self { + Self { + ty: &const { SimOnly::::new() }, + } + } + pub fn type_id(self) -> TypeId { + self.ty.type_id_dyn() + } + pub fn type_name(self) -> &'static str { + self.ty.type_name() + } + pub fn is(self) -> bool { + self.type_id() == TypeId::of::() + } + pub fn downcast(self) -> Option> { + self.is::().then_some(SimOnly::default()) + } + pub fn deserialize_from_json_string( + self, + json_str: &str, + ) -> serde_json::Result { + self.ty + .deserialize_from_json_string(json_str) + .map(DynSimOnlyValue) + } + pub fn default_value(self) -> DynSimOnlyValue { + DynSimOnlyValue(self.ty.default_value()) + } +} + +impl PartialEq for DynSimOnly { + fn eq(&self, other: &Self) -> bool { + Self::type_id(*self) == Self::type_id(*other) + } +} + +impl Eq for DynSimOnly {} + +impl Hash for DynSimOnly { + fn hash(&self, state: &mut H) { + Self::type_id(*self).hash(state); + } +} + +impl fmt::Debug for DynSimOnly { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "SimOnly<{}>", self.ty.type_name()) + } +} + +impl From> for DynSimOnly { + fn from(value: SimOnly) -> Self { + let SimOnly(PhantomData) = value; + Self::of::() + } +} + +/// the [`Type`][Type] for a value that can only be used in a Fayalite simulation, it can't be converted to FIRRTL +/// +/// [Type]: crate::ty::Type +#[derive(Clone, Eq, PartialEq, Hash)] +pub struct SimOnly(PhantomData T>); + +impl fmt::Debug for SimOnly { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + DynSimOnly::of::().fmt(f) + } +} + +impl SimOnly { + pub const fn new() -> Self { + Self(PhantomData) + } +} + +impl Copy for SimOnly {} + +impl Default for SimOnly { + fn default() -> Self { + Self::new() + } +} + +/// a value that can only be used in a Fayalite simulation, it can't be converted to FIRRTL +#[derive(Clone, Eq, PartialEq, Hash, Default, PartialOrd, Ord)] +pub struct SimOnlyValue(Rc); + +impl SimOnlyValue { + pub fn with_dyn_ref R, R>(&self, f: F) -> R { + // Safety: creating a copied `Rc` is safe as long as the copy isn't dropped and isn't changed + // to point somewhere else, `f` can't change `dyn_ref` because it's only given a shared reference. + let dyn_ref = + unsafe { ManuallyDrop::new(DynSimOnlyValue(Rc::::from_raw(Rc::as_ptr(&self.0)))) }; + f(&dyn_ref) + } + pub fn from_rc(v: Rc) -> Self { + Self(v) + } + pub fn new(v: T) -> Self { + Self(Rc::new(v)) + } + pub fn into_inner(this: Self) -> Rc { + this.0 + } + pub fn inner_mut(this: &mut Self) -> &mut Rc { + &mut this.0 + } + pub fn inner(this: &Self) -> &Rc { + &this.0 + } + pub fn into_dyn(this: Self) -> DynSimOnlyValue { + DynSimOnlyValue::from(this) + } +} + +impl std::ops::Deref for SimOnlyValue { + type Target = T; + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl std::ops::DerefMut for SimOnlyValue { + fn deref_mut(&mut self) -> &mut Self::Target { + Rc::make_mut(&mut self.0) + } +} + +#[derive(Clone)] +pub struct DynSimOnlyValue(Rc); + +impl fmt::Debug for DynSimOnlyValue { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + ::fmt(&*self.0, f) + } +} + +impl PartialEq for DynSimOnlyValue { + fn eq(&self, other: &Self) -> bool { + DynSimOnlyValueTrait::eq_dyn(&*self.0, &*other.0) + } +} + +impl Eq for DynSimOnlyValue {} + +impl Hash for DynSimOnlyValue { + fn hash(&self, state: &mut H) { + DynSimOnlyValueTrait::hash_dyn(&*self.0, state); + } +} + +impl From> for DynSimOnlyValue { + fn from(value: SimOnlyValue) -> Self { + Self(value.0) + } +} + +impl DynSimOnlyValue { + pub fn ty(&self) -> DynSimOnly { + self.0.ty() + } + pub fn type_id(&self) -> TypeId { + self.0.type_id_dyn() + } + pub fn is(&self) -> bool { + self.0.is::() + } + pub fn downcast(self) -> Result, DynSimOnlyValue> { + match ::downcast_rc(self.0) { + Ok(v) => Ok(SimOnlyValue(v)), + Err(v) => Err(Self(v)), + } + } + pub fn downcast_ref(&self) -> Option<&T> { + ::downcast_ref(&*self.0) + } + pub fn serialize_to_json_string(&self) -> serde_json::Result { + self.0.serialize_to_json_string() + } +} diff --git a/crates/fayalite/src/sim/vcd.rs b/crates/fayalite/src/sim/vcd.rs index b8248e3..e66c3ee 100644 --- a/crates/fayalite/src/sim/vcd.rs +++ b/crates/fayalite/src/sim/vcd.rs @@ -5,22 +5,86 @@ use crate::{ enum_::{Enum, EnumType}, expr::Flow, int::UInt, + intern::{Intern, Interned}, sim::{ - time::{SimDuration, SimInstant}, TraceArray, TraceAsyncReset, TraceBool, TraceBundle, TraceClock, TraceDecl, TraceEnumDiscriminant, TraceEnumWithFields, TraceFieldlessEnum, TraceInstance, TraceLocation, TraceMem, TraceMemPort, TraceMemoryId, TraceMemoryLocation, TraceModule, - TraceModuleIO, TraceReg, TraceSInt, TraceScalar, TraceScalarId, TraceScope, TraceSyncReset, - TraceUInt, TraceWire, TraceWriter, TraceWriterDecls, + TraceModuleIO, TraceReg, TraceSInt, TraceScalar, TraceScalarId, TraceScope, TraceSimOnly, + TraceSyncReset, TraceUInt, TraceWire, TraceWriter, TraceWriterDecls, + time::{SimDuration, SimInstant}, + value::DynSimOnlyValue, }, + util::HashMap, }; use bitvec::{order::Lsb0, slice::BitSlice}; +use hashbrown::hash_map::Entry; use std::{ - fmt, - io::{self, Write}, - mem, + fmt::{self, Write as _}, + io, mem, }; +#[derive(Default)] +struct Scope { + last_inserted: HashMap, usize>, +} + +#[derive(Copy, Clone)] +struct VerilogIdentifier { + unescaped_name: Interned, +} + +impl VerilogIdentifier { + fn needs_escape(self) -> bool { + // we only allow ascii, so we can just check bytes + let Some((&first, rest)) = self.unescaped_name.as_bytes().split_first() else { + unreachable!("Scope::new_identifier guarantees a non-empty name"); + }; + if !first.is_ascii_alphabetic() && first != b'_' { + true + } else { + rest.iter() + .any(|&ch| !ch.is_ascii_alphanumeric() && ch != b'_' && ch != b'$') + } + } +} + +impl fmt::Display for VerilogIdentifier { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + if self.needs_escape() { + f.write_str("\\")?; + } + write!(f, "{}", Escaped(self.unescaped_name)) + } +} + +impl Scope { + fn new_identifier(&mut self, unescaped_name: Interned) -> VerilogIdentifier { + let next_disambiguator = match self.last_inserted.entry(unescaped_name) { + Entry::Vacant(entry) => { + entry.insert(1); + return VerilogIdentifier { unescaped_name }; + } + Entry::Occupied(entry) => entry.get() + 1, + }; + let mut disambiguated_name = String::from(&*unescaped_name); + for disambiguator in next_disambiguator.. { + disambiguated_name.truncate(unescaped_name.len()); + write!(disambiguated_name, "_{disambiguator}").expect("can't fail"); + if let Entry::Vacant(entry) = self.last_inserted.entry((*disambiguated_name).intern()) { + let retval = VerilogIdentifier { + unescaped_name: *entry.key(), + }; + entry.insert(1); + // speed up future searches + self.last_inserted.insert(unescaped_name, disambiguator); + return retval; + } + } + panic!("too many names"); + } +} + pub struct VcdWriterDecls { writer: W, timescale: SimDuration, @@ -97,14 +161,20 @@ impl fmt::Debug for VcdWriterDecls { } } +/// pass in scope to ensure it's not available in child scope fn write_vcd_scope( writer: &mut W, scope_type: &str, - scope_name: &str, - f: impl FnOnce(&mut W) -> io::Result, + scope_name: Interned, + scope: &mut Scope, + f: impl FnOnce(&mut W, &mut Scope) -> io::Result, ) -> io::Result { - writeln!(writer, "$scope {scope_type} {scope_name} $end")?; - let retval = f(writer)?; + writeln!( + writer, + "$scope {scope_type} {} $end", + scope.new_identifier(scope_name), + )?; + let retval = f(writer, &mut Scope::default())?; writeln!(writer, "$upscope $end")?; Ok(retval) } @@ -143,24 +213,28 @@ trait_arg! { struct ArgModule<'a> { properties: &'a mut VcdWriterProperties, + scope: &'a mut Scope, } impl<'a> ArgModule<'a> { fn reborrow(&mut self) -> ArgModule<'_> { ArgModule { properties: self.properties, + scope: self.scope, } } } struct ArgModuleBody<'a> { properties: &'a mut VcdWriterProperties, + scope: &'a mut Scope, } impl<'a> ArgModuleBody<'a> { fn reborrow(&mut self) -> ArgModuleBody<'_> { ArgModuleBody { properties: self.properties, + scope: self.scope, } } } @@ -170,6 +244,7 @@ struct ArgInType<'a> { sink_var_type: &'static str, duplex_var_type: &'static str, properties: &'a mut VcdWriterProperties, + scope: &'a mut Scope, } impl<'a> ArgInType<'a> { @@ -179,6 +254,7 @@ impl<'a> ArgInType<'a> { sink_var_type: self.sink_var_type, duplex_var_type: self.duplex_var_type, properties: self.properties, + scope: self.scope, } } } @@ -207,6 +283,7 @@ impl WriteTrace for TraceScalar { Self::Clock(v) => v.write_trace(writer, arg), Self::SyncReset(v) => v.write_trace(writer, arg), Self::AsyncReset(v) => v.write_trace(writer, arg), + Self::SimOnly(v) => v.write_trace(writer, arg), } } } @@ -226,55 +303,42 @@ fn write_vcd_id(writer: &mut W, mut id: usize) -> io::Result<()> { Ok(()) } -fn write_escaped(writer: &mut W, value: impl fmt::Display) -> io::Result<()> { - // escaping rules from function GTKWave uses to decode VCD strings: - // https://github.com/gtkwave/gtkwave/blob/491f24d7e8619cfc1fcc65704ee5c967d1083c18/lib/libfst/fstapi.c#L7090 - struct Wrapper(W); - impl io::Write for Wrapper { - fn write(&mut self, buf: &[u8]) -> io::Result { - if buf.is_empty() { - return self.0.write(buf); - } - let mut retval = 0; - for &byte in buf { - match byte { - b'\\' | b'\'' | b'"' | b'?' => self.0.write_all(&[b'\\', byte])?, - b'\n' => self.0.write_all(br"\n")?, - b'\r' => self.0.write_all(br"\r")?, - b'\t' => self.0.write_all(br"\t")?, - 0x7 => self.0.write_all(br"\a")?, - 0x8 => self.0.write_all(br"\b")?, - 0xC => self.0.write_all(br"\f")?, - 0xB => self.0.write_all(br"\v")?, - _ => { - if byte.is_ascii_graphic() { - self.0.write_all(&[byte])?; - } else { - write!(self.0, r"\x{byte:02x}")?; +struct Escaped(T); + +impl fmt::Display for Escaped { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + // escaping rules from function GTKWave uses to decode VCD strings: + // https://github.com/gtkwave/gtkwave/blob/491f24d7e8619cfc1fcc65704ee5c967d1083c18/lib/libfst/fstapi.c#L7090 + struct Wrapper(W); + impl fmt::Write for Wrapper { + fn write_str(&mut self, s: &str) -> fmt::Result { + for byte in s.bytes() { + match byte { + b'\\' | b'\'' | b'"' | b'?' => { + self.0.write_str("\\")?; + self.0.write_char(byte as char)?; + } + b'\n' => self.0.write_str(r"\n")?, + b'\r' => self.0.write_str(r"\r")?, + b'\t' => self.0.write_str(r"\t")?, + 0x7 => self.0.write_str(r"\a")?, + 0x8 => self.0.write_str(r"\b")?, + 0xC => self.0.write_str(r"\f")?, + 0xB => self.0.write_str(r"\v")?, + _ => { + if byte.is_ascii_graphic() { + self.0.write_char(byte as char)?; + } else { + write!(self.0, r"\x{byte:02x}")?; + } } } } - retval += 1; + Ok(()) } - Ok(retval) - } - - fn flush(&mut self) -> io::Result<()> { - self.0.flush() } + write!(Wrapper(f), "{}", self.0) } - write!(Wrapper(writer), "{value}") -} - -fn is_unescaped_verilog_identifier(ident: &str) -> bool { - // we only allow ascii, so we can just check bytes - let Some((&first, rest)) = ident.as_bytes().split_first() else { - return false; // empty string is not an identifier - }; - (first.is_ascii_alphabetic() || first == b'_') - && rest - .iter() - .all(|&ch| ch.is_ascii_alphanumeric() || ch == b'_' || ch == b'$') } fn write_vcd_var( @@ -284,7 +348,7 @@ fn write_vcd_var( var_type: &str, size: usize, location: TraceLocation, - name: &str, + name: VerilogIdentifier, ) -> io::Result<()> { let id = match location { TraceLocation::Scalar(id) => id.as_usize(), @@ -319,12 +383,7 @@ fn write_vcd_var( }; write!(writer, "$var {var_type} {size} ")?; write_vcd_id(writer, id)?; - writer.write_all(b" ")?; - if !is_unescaped_verilog_identifier(name) { - writer.write_all(b"\\")?; - } - write_escaped(writer, name)?; - writer.write_all(b" $end\n") + writeln!(writer, " {name} $end") } impl WriteTrace for TraceUInt { @@ -334,6 +393,7 @@ impl WriteTrace for TraceUInt { sink_var_type, duplex_var_type, properties, + scope, } = arg.in_type(); let Self { location, @@ -356,7 +416,7 @@ impl WriteTrace for TraceUInt { var_type, ty.width(), location, - &name, + scope.new_identifier(name), ) } } @@ -421,6 +481,7 @@ impl WriteTrace for TraceEnumDiscriminant { sink_var_type: _, duplex_var_type: _, properties, + scope, } = arg.in_type(); let Self { location, @@ -435,7 +496,7 @@ impl WriteTrace for TraceEnumDiscriminant { "string", 1, location, - &name, + scope.new_identifier(name), ) } } @@ -488,6 +549,33 @@ impl WriteTrace for TraceAsyncReset { } } +impl WriteTrace for TraceSimOnly { + fn write_trace(self, writer: &mut W, mut arg: A) -> io::Result<()> { + let ArgInType { + source_var_type: _, + sink_var_type: _, + duplex_var_type: _, + properties, + scope, + } = arg.in_type(); + let Self { + location, + name, + ty: _, + flow: _, + } = self; + write_vcd_var( + properties, + MemoryElementPartBody::Scalar, + writer, + "string", + 1, + location, + scope.new_identifier(name), + ) + } +} + impl WriteTrace for TraceScope { fn write_trace(self, writer: &mut W, arg: A) -> io::Result<()> { match self { @@ -507,11 +595,11 @@ impl WriteTrace for TraceScope { impl WriteTrace for TraceModule { fn write_trace(self, writer: &mut W, mut arg: A) -> io::Result<()> { - let ArgModule { properties } = arg.module(); + let ArgModule { properties, scope } = arg.module(); let Self { name, children } = self; - write_vcd_scope(writer, "module", &name, |writer| { + write_vcd_scope(writer, "module", name, scope, |writer, scope| { for child in children { - child.write_trace(writer, ArgModuleBody { properties })?; + child.write_trace(writer, ArgModuleBody { properties, scope })?; } Ok(()) }) @@ -520,7 +608,7 @@ impl WriteTrace for TraceModule { impl WriteTrace for TraceInstance { fn write_trace(self, writer: &mut W, mut arg: A) -> io::Result<()> { - let ArgModuleBody { properties } = arg.module_body(); + let ArgModuleBody { properties, scope } = arg.module_body(); let Self { name: _, instance_io, @@ -534,15 +622,16 @@ impl WriteTrace for TraceInstance { sink_var_type: "wire", duplex_var_type: "wire", properties, + scope, }, )?; - module.write_trace(writer, ArgModule { properties }) + module.write_trace(writer, ArgModule { properties, scope }) } } impl WriteTrace for TraceMem { fn write_trace(self, writer: &mut W, mut arg: A) -> io::Result<()> { - let ArgModuleBody { properties } = arg.module_body(); + let ArgModuleBody { properties, scope } = arg.module_body(); let Self { id, name, @@ -551,27 +640,41 @@ impl WriteTrace for TraceMem { ports, array_type, } = self; - write_vcd_scope(writer, "struct", &*name, |writer| { - write_vcd_scope(writer, "struct", "contents", |writer| { - for element_index in 0..array_type.len() { - write_vcd_scope(writer, "struct", &format!("[{element_index}]"), |writer| { - properties.memory_properties[id.as_usize()].element_index = element_index; - properties.memory_properties[id.as_usize()].element_part_index = 0; - element_type.write_trace( + write_vcd_scope(writer, "struct", name, scope, |writer, scope| { + write_vcd_scope( + writer, + "struct", + "contents".intern(), + scope, + |writer, scope| { + for element_index in 0..array_type.len() { + write_vcd_scope( writer, - ArgInType { - source_var_type: "reg", - sink_var_type: "reg", - duplex_var_type: "reg", - properties, + "struct", + Intern::intern_owned(format!("[{element_index}]")), + scope, + |writer, scope| { + properties.memory_properties[id.as_usize()].element_index = + element_index; + properties.memory_properties[id.as_usize()].element_part_index = 0; + element_type.write_trace( + writer, + ArgInType { + source_var_type: "reg", + sink_var_type: "reg", + duplex_var_type: "reg", + properties, + scope, + }, + ) }, - ) - })?; - } - Ok(()) - })?; + )?; + } + Ok(()) + }, + )?; for port in ports { - port.write_trace(writer, ArgModuleBody { properties })?; + port.write_trace(writer, ArgModuleBody { properties, scope })?; } Ok(()) }) @@ -580,7 +683,7 @@ impl WriteTrace for TraceMem { impl WriteTrace for TraceMemPort { fn write_trace(self, writer: &mut W, mut arg: A) -> io::Result<()> { - let ArgModuleBody { properties } = arg.module_body(); + let ArgModuleBody { properties, scope } = arg.module_body(); let Self { name: _, bundle, @@ -593,6 +696,7 @@ impl WriteTrace for TraceMemPort { sink_var_type: "wire", duplex_var_type: "wire", properties, + scope, }, ) } @@ -600,7 +704,7 @@ impl WriteTrace for TraceMemPort { impl WriteTrace for TraceWire { fn write_trace(self, writer: &mut W, mut arg: A) -> io::Result<()> { - let ArgModuleBody { properties } = arg.module_body(); + let ArgModuleBody { properties, scope } = arg.module_body(); let Self { name: _, child, @@ -613,6 +717,7 @@ impl WriteTrace for TraceWire { sink_var_type: "wire", duplex_var_type: "wire", properties, + scope, }, ) } @@ -620,7 +725,7 @@ impl WriteTrace for TraceWire { impl WriteTrace for TraceReg { fn write_trace(self, writer: &mut W, mut arg: A) -> io::Result<()> { - let ArgModuleBody { properties } = arg.module_body(); + let ArgModuleBody { properties, scope } = arg.module_body(); let Self { name: _, child, @@ -633,6 +738,7 @@ impl WriteTrace for TraceReg { sink_var_type: "reg", duplex_var_type: "reg", properties, + scope, }, ) } @@ -640,7 +746,7 @@ impl WriteTrace for TraceReg { impl WriteTrace for TraceModuleIO { fn write_trace(self, writer: &mut W, mut arg: A) -> io::Result<()> { - let ArgModuleBody { properties } = arg.module_body(); + let ArgModuleBody { properties, scope } = arg.module_body(); let Self { name: _, child, @@ -654,6 +760,7 @@ impl WriteTrace for TraceModuleIO { sink_var_type: "wire", duplex_var_type: "wire", properties, + scope, }, ) } @@ -661,16 +768,31 @@ impl WriteTrace for TraceModuleIO { impl WriteTrace for TraceBundle { fn write_trace(self, writer: &mut W, mut arg: A) -> io::Result<()> { - let mut arg = arg.in_type(); + let ArgInType { + source_var_type, + sink_var_type, + duplex_var_type, + properties, + scope, + } = arg.in_type(); let Self { name, fields, ty: _, flow: _, } = self; - write_vcd_scope(writer, "struct", &name, |writer| { + write_vcd_scope(writer, "struct", name, scope, |writer, scope| { for field in fields { - field.write_trace(writer, arg.reborrow())?; + field.write_trace( + writer, + ArgInType { + source_var_type, + sink_var_type, + duplex_var_type, + properties, + scope, + }, + )?; } Ok(()) }) @@ -679,16 +801,31 @@ impl WriteTrace for TraceBundle { impl WriteTrace for TraceArray { fn write_trace(self, writer: &mut W, mut arg: A) -> io::Result<()> { - let mut arg = arg.in_type(); + let ArgInType { + source_var_type, + sink_var_type, + duplex_var_type, + properties, + scope, + } = arg.in_type(); let Self { name, elements, ty: _, flow: _, } = self; - write_vcd_scope(writer, "struct", &name, |writer| { + write_vcd_scope(writer, "struct", name, scope, |writer, scope| { for element in elements { - element.write_trace(writer, arg.reborrow())?; + element.write_trace( + writer, + ArgInType { + source_var_type, + sink_var_type, + duplex_var_type, + properties, + scope, + }, + )?; } Ok(()) }) @@ -697,7 +834,13 @@ impl WriteTrace for TraceArray { impl WriteTrace for TraceEnumWithFields { fn write_trace(self, writer: &mut W, mut arg: A) -> io::Result<()> { - let mut arg = arg.in_type(); + let ArgInType { + source_var_type, + sink_var_type, + duplex_var_type, + properties, + scope, + } = arg.in_type(); let Self { name, discriminant, @@ -705,10 +848,28 @@ impl WriteTrace for TraceEnumWithFields { ty: _, flow: _, } = self; - write_vcd_scope(writer, "struct", &name, |writer| { - discriminant.write_trace(writer, arg.reborrow())?; + write_vcd_scope(writer, "struct", name, scope, |writer, scope| { + discriminant.write_trace( + writer, + ArgInType { + source_var_type, + sink_var_type, + duplex_var_type, + properties, + scope, + }, + )?; for field in non_empty_fields { - field.write_trace(writer, arg.reborrow())?; + field.write_trace( + writer, + ArgInType { + source_var_type, + sink_var_type, + duplex_var_type, + properties, + scope, + }, + )?; } Ok(()) }) @@ -744,6 +905,7 @@ impl TraceWriterDecls for VcdWriterDecls { &mut writer, ArgModule { properties: &mut properties, + scope: &mut Scope::default(), }, )?; writeln!(writer, "$enddefinitions $end")?; @@ -798,9 +960,7 @@ fn write_string_value_change( value: impl fmt::Display, id: usize, ) -> io::Result<()> { - writer.write_all(b"s")?; - write_escaped(writer, value)?; - writer.write_all(b" ")?; + write!(writer, "s{} ", Escaped(value))?; write_vcd_id(writer, id)?; writer.write_all(b"\n") } @@ -930,6 +1090,14 @@ impl TraceWriter for VcdWriter { ) -> Result<(), Self::Error> { write_enum_discriminant_value_change(&mut self.writer, variant_index, ty, id.as_usize()) } + + fn set_signal_sim_only_value( + &mut self, + id: TraceScalarId, + value: &DynSimOnlyValue, + ) -> Result<(), Self::Error> { + write_string_value_change(&mut self.writer, format_args!("{value:?}"), id.as_usize()) + } } impl fmt::Debug for VcdWriter { @@ -946,3 +1114,49 @@ impl fmt::Debug for VcdWriter { .finish_non_exhaustive() } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_scope() { + let mut scope = Scope::default(); + assert_eq!(&*scope.new_identifier("foo".intern()).unescaped_name, "foo"); + assert_eq!( + &*scope.new_identifier("foo_0".intern()).unescaped_name, + "foo_0" + ); + assert_eq!( + &*scope.new_identifier("foo_1".intern()).unescaped_name, + "foo_1" + ); + assert_eq!( + &*scope.new_identifier("foo_3".intern()).unescaped_name, + "foo_3" + ); + assert_eq!( + &*scope.new_identifier("foo".intern()).unescaped_name, + "foo_2" + ); + assert_eq!( + &*scope.new_identifier("foo".intern()).unescaped_name, + "foo_4" + ); + assert_eq!( + &*scope.new_identifier("foo_0".intern()).unescaped_name, + "foo_0_2" + ); + assert_eq!( + &*scope.new_identifier("foo_1".intern()).unescaped_name, + "foo_1_2" + ); + for i in 5..1000u64 { + // verify it actually picks the next available identifier with no skips or duplicates + assert_eq!( + *scope.new_identifier("foo".intern()).unescaped_name, + format!("foo_{i}"), + ); + } + } +} diff --git a/crates/fayalite/src/source_location.rs b/crates/fayalite/src/source_location.rs index d143f22..1a168b1 100644 --- a/crates/fayalite/src/source_location.rs +++ b/crates/fayalite/src/source_location.rs @@ -2,9 +2,8 @@ // See Notices.txt for copyright information use crate::{ intern::{Intern, Interned}, - util::DebugAsDisplay, + util::{DebugAsDisplay, HashMap}, }; -use hashbrown::HashMap; use std::{cell::RefCell, fmt, num::NonZeroUsize, panic, path::Path}; #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] @@ -97,7 +96,7 @@ impl NormalizeFilesForTestsState { fn new() -> Self { Self { test_position: panic::Location::caller(), - file_pattern_matches: HashMap::new(), + file_pattern_matches: HashMap::default(), } } } @@ -143,7 +142,7 @@ impl From<&'_ panic::Location<'_>> for SourceLocation { map.entry_ref(file) .or_insert_with(|| NormalizedFileForTestState { file_name_id: NonZeroUsize::new(len + 1).unwrap(), - positions_map: HashMap::new(), + positions_map: HashMap::default(), }); file_str = m.generate_file_name(file_state.file_name_id); file = &file_str; diff --git a/crates/fayalite/src/testing.rs b/crates/fayalite/src/testing.rs index 4517e34..b81bc3f 100644 --- a/crates/fayalite/src/testing.rs +++ b/crates/fayalite/src/testing.rs @@ -3,9 +3,9 @@ use crate::{ cli::{FormalArgs, FormalMode, FormalOutput, RunPhase}, firrtl::ExportOptions, + util::HashMap, }; use clap::Parser; -use hashbrown::HashMap; use serde::Deserialize; use std::{ fmt::Write, @@ -87,7 +87,7 @@ fn get_assert_formal_target_path(test_name: &dyn std::fmt::Display) -> PathBuf { let index = *DIRS .lock() .unwrap() - .get_or_insert_with(HashMap::new) + .get_or_insert_with(HashMap::default) .entry_ref(&dir) .and_modify(|v| *v += 1) .or_insert(0); diff --git a/crates/fayalite/src/ty.rs b/crates/fayalite/src/ty.rs index 69080c9..d9ea6b2 100644 --- a/crates/fayalite/src/ty.rs +++ b/crates/fayalite/src/ty.rs @@ -7,12 +7,27 @@ use crate::{ clock::Clock, enum_::Enum, expr::Expr, - int::{Bool, SInt, UInt}, + int::{Bool, SInt, UInt, UIntValue}, intern::{Intern, Interned}, + phantom_const::PhantomConst, reset::{AsyncReset, Reset, SyncReset}, + sim::value::{DynSimOnlyValue, DynSimOnly, SimValue, ToSimValueWithType}, source_location::SourceLocation, + util::{ConstUsize, slice_range, try_slice_range}, }; -use std::{fmt, hash::Hash, iter::FusedIterator, ops::Index}; +use bitvec::{slice::BitSlice, vec::BitVec}; +use serde::{Deserialize, Deserializer, Serialize, Serializer, de::DeserializeOwned}; +use std::{ + fmt, + hash::Hash, + iter::{FusedIterator, Sum}, + marker::PhantomData, + mem, + ops::{Add, AddAssign, Bound, Index, Mul, MulAssign, Range, Sub, SubAssign}, + sync::Arc, +}; + +pub(crate) mod serde_impls; #[derive(Copy, Clone, Hash, PartialEq, Eq, Debug)] #[non_exhaustive] @@ -21,6 +36,23 @@ pub struct TypeProperties { pub is_storable: bool, pub is_castable_from_bits: bool, pub bit_width: usize, + pub sim_only_values_len: usize, +} + +impl TypeProperties { + pub const fn size(self) -> OpaqueSimValueSize { + let Self { + is_passive: _, + is_storable: _, + is_castable_from_bits: _, + bit_width, + sim_only_values_len, + } = self; + OpaqueSimValueSize { + bit_width, + sim_only_values_len, + } + } } #[derive(Copy, Clone, Hash, PartialEq, Eq)] @@ -35,6 +67,8 @@ pub enum CanonicalType { SyncReset(SyncReset), Reset(Reset), Clock(Clock), + PhantomConst(PhantomConst), + DynSimOnly(DynSimOnly), } impl fmt::Debug for CanonicalType { @@ -50,10 +84,30 @@ impl fmt::Debug for CanonicalType { Self::SyncReset(v) => v.fmt(f), Self::Reset(v) => v.fmt(f), Self::Clock(v) => v.fmt(f), + Self::PhantomConst(v) => v.fmt(f), + Self::DynSimOnly(v) => v.fmt(f), } } } +impl Serialize for CanonicalType { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + serde_impls::SerdeCanonicalType::from(*self).serialize(serializer) + } +} + +impl<'de> Deserialize<'de> for CanonicalType { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + Ok(serde_impls::SerdeCanonicalType::deserialize(deserializer)?.into()) + } +} + impl CanonicalType { pub fn type_properties(self) -> TypeProperties { match self { @@ -67,6 +121,8 @@ impl CanonicalType { CanonicalType::SyncReset(v) => v.type_properties(), CanonicalType::Reset(v) => v.type_properties(), CanonicalType::Clock(v) => v.type_properties(), + CanonicalType::PhantomConst(v) => v.type_properties(), + CanonicalType::DynSimOnly(v) => v.type_properties(), } } pub fn is_passive(self) -> bool { @@ -81,6 +137,12 @@ impl CanonicalType { pub fn bit_width(self) -> usize { self.type_properties().bit_width } + pub fn sim_only_values_len(self) -> usize { + self.type_properties().sim_only_values_len + } + pub fn size(self) -> OpaqueSimValueSize { + self.type_properties().size() + } pub fn can_connect(self, rhs: Self) -> bool { match self { CanonicalType::UInt(lhs) => { @@ -143,8 +205,23 @@ impl CanonicalType { }; lhs.can_connect(rhs) } + CanonicalType::PhantomConst(lhs) => { + let CanonicalType::PhantomConst(rhs) = rhs else { + return false; + }; + lhs.can_connect(rhs) + } + CanonicalType::DynSimOnly(lhs) => { + let CanonicalType::DynSimOnly(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 { @@ -166,7 +243,7 @@ impl MatchVariantAndInactiveScope for MatchVariantWith } pub trait FillInDefaultedGenerics { - type Type: Type; + type Type; fn fill_in_defaulted_generics(self) -> Self::Type; } @@ -178,6 +255,22 @@ impl FillInDefaultedGenerics for T { } } +impl FillInDefaultedGenerics for usize { + type Type = usize; + + fn fill_in_defaulted_generics(self) -> Self::Type { + self + } +} + +impl FillInDefaultedGenerics for ConstUsize { + type Type = ConstUsize; + + fn fill_in_defaulted_generics(self) -> Self::Type { + self + } +} + mod sealed { pub trait TypeOrDefaultSealed {} pub trait BaseTypeSealed {} @@ -195,6 +288,34 @@ macro_rules! impl_base_type { }; } +macro_rules! impl_base_type_serde { + ($name:ident, $expected:literal) => { + impl Serialize for $name { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + self.canonical().serialize(serializer) + } + } + + impl<'de> Deserialize<'de> for $name { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + match CanonicalType::deserialize(deserializer)? { + CanonicalType::$name(retval) => Ok(retval), + ty => Err(serde::de::Error::invalid_value( + serde::de::Unexpected::Other(ty.as_serde_unexpected_str()), + &$expected, + )), + } + } + } + }; +} + impl_base_type!(UInt); impl_base_type!(SInt); impl_base_type!(Bool); @@ -205,6 +326,16 @@ impl_base_type!(AsyncReset); impl_base_type!(SyncReset); impl_base_type!(Reset); impl_base_type!(Clock); +impl_base_type!(PhantomConst); +impl_base_type!(DynSimOnly); + +impl_base_type_serde!(Bool, "a Bool"); +impl_base_type_serde!(Enum, "an Enum"); +impl_base_type_serde!(Bundle, "a Bundle"); +impl_base_type_serde!(AsyncReset, "an AsyncReset"); +impl_base_type_serde!(SyncReset, "a SyncReset"); +impl_base_type_serde!(Reset, "a Reset"); +impl_base_type_serde!(Clock, "a Clock"); impl sealed::BaseTypeSealed for CanonicalType {} @@ -240,26 +371,48 @@ pub trait Type: { type BaseType: BaseType; type MaskType: Type; + type SimValue: fmt::Debug + Clone + 'static + ToSimValueWithType; type MatchVariant: 'static + Send + Sync; type MatchActiveScope; type MatchVariantAndInactiveScope: MatchVariantAndInactiveScope< - MatchVariant = Self::MatchVariant, - MatchActiveScope = Self::MatchActiveScope, - >; + MatchVariant = Self::MatchVariant, + MatchActiveScope = Self::MatchActiveScope, + >; type MatchVariantsIter: Iterator + ExactSizeIterator + FusedIterator + DoubleEndedIterator; #[track_caller] fn match_variants(this: Expr, source_location: SourceLocation) - -> Self::MatchVariantsIter; + -> Self::MatchVariantsIter; fn mask_type(&self) -> Self::MaskType; fn canonical(&self) -> CanonicalType; fn from_canonical(canonical_type: CanonicalType) -> Self; fn source_location() -> SourceLocation; + fn sim_value_from_opaque(&self, opaque: OpaqueSimValueSlice<'_>) -> Self::SimValue; + fn sim_value_clone_from_opaque( + &self, + value: &mut Self::SimValue, + opaque: OpaqueSimValueSlice<'_>, + ); + fn sim_value_to_opaque<'w>( + &self, + value: &Self::SimValue, + writer: OpaqueSimValueWriter<'w>, + ) -> OpaqueSimValueWritten<'w>; } -pub trait BaseType: Type + sealed::BaseTypeSealed + Into {} +pub trait BaseType: + Type< + BaseType = Self, + MaskType: Serialize + DeserializeOwned, + SimValue: Serialize + DeserializeOwned, + > + sealed::BaseTypeSealed + + Into + + Serialize + + DeserializeOwned +{ +} macro_rules! impl_match_variant_as_self { () => { @@ -286,6 +439,7 @@ pub trait TypeWithDeref: Type { impl Type for CanonicalType { type BaseType = CanonicalType; type MaskType = CanonicalType; + type SimValue = OpaqueSimValue; impl_match_variant_as_self!(); fn mask_type(&self) -> Self::MaskType { match self { @@ -299,6 +453,8 @@ impl Type for CanonicalType { CanonicalType::SyncReset(v) => v.mask_type().canonical(), CanonicalType::Reset(v) => v.mask_type().canonical(), CanonicalType::Clock(v) => v.mask_type().canonical(), + CanonicalType::PhantomConst(v) => v.mask_type().canonical(), + CanonicalType::DynSimOnly(v) => v.mask_type().canonical(), } } fn canonical(&self) -> CanonicalType { @@ -310,9 +466,636 @@ impl Type for CanonicalType { fn source_location() -> SourceLocation { SourceLocation::builtin() } + fn sim_value_from_opaque(&self, opaque: OpaqueSimValueSlice<'_>) -> Self::SimValue { + assert_eq!(self.type_properties().size(), opaque.size()); + opaque.to_owned() + } + fn sim_value_clone_from_opaque( + &self, + value: &mut Self::SimValue, + opaque: OpaqueSimValueSlice<'_>, + ) { + assert_eq!(self.type_properties().size(), opaque.size()); + assert_eq!(value.size(), opaque.size()); + value.clone_from_slice(opaque); + } + fn sim_value_to_opaque<'w>( + &self, + value: &Self::SimValue, + writer: OpaqueSimValueWriter<'w>, + ) -> OpaqueSimValueWritten<'w> { + assert_eq!(self.type_properties().size(), writer.size()); + assert_eq!(value.size(), writer.size()); + writer.fill_cloned_from_slice(value.as_slice()) + } } -pub trait StaticType: Type { +#[derive(Clone, PartialEq, Eq, Hash, Debug, Serialize, Deserialize, Default)] +#[non_exhaustive] +pub struct OpaqueSimValueSizeRange { + pub bit_width: Range, + pub sim_only_values_len: Range, +} + +impl OpaqueSimValueSizeRange { + pub fn start(&self) -> OpaqueSimValueSize { + OpaqueSimValueSize { + bit_width: self.bit_width.start, + sim_only_values_len: self.sim_only_values_len.start, + } + } + pub fn end(&self) -> OpaqueSimValueSize { + OpaqueSimValueSize { + bit_width: self.bit_width.end, + sim_only_values_len: self.sim_only_values_len.end, + } + } + pub fn is_empty(&self) -> bool { + let Self { + bit_width, + sim_only_values_len, + } = self; + bit_width.is_empty() && sim_only_values_len.is_empty() + } +} + +impl From> for OpaqueSimValueSizeRange { + fn from(value: Range) -> Self { + Self { + bit_width: value.start.bit_width..value.end.bit_width, + sim_only_values_len: value.start.sim_only_values_len..value.end.sim_only_values_len, + } + } +} + +impl From for Range { + fn from(value: OpaqueSimValueSizeRange) -> Self { + value.start()..value.end() + } +} + +pub trait OpaqueSimValueSizeRangeBounds { + fn start_bound(&self) -> Bound; + fn end_bound(&self) -> Bound; +} + +impl OpaqueSimValueSizeRangeBounds for OpaqueSimValueSizeRange { + fn start_bound(&self) -> Bound { + Bound::Included(self.start()) + } + + fn end_bound(&self) -> Bound { + Bound::Excluded(self.end()) + } +} + +impl> OpaqueSimValueSizeRangeBounds for T { + fn start_bound(&self) -> Bound { + std::ops::RangeBounds::start_bound(self).cloned() + } + fn end_bound(&self) -> Bound { + std::ops::RangeBounds::end_bound(self).cloned() + } +} + +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, Serialize, Deserialize, Default)] +#[non_exhaustive] +pub struct OpaqueSimValueSize { + pub bit_width: usize, + pub sim_only_values_len: usize, +} + +impl OpaqueSimValueSize { + pub const fn from_bit_width(bit_width: usize) -> Self { + Self::from_bit_width_and_sim_only_values_len(bit_width, 0) + } + pub const fn from_bit_width_and_sim_only_values_len( + bit_width: usize, + sim_only_values_len: usize, + ) -> Self { + Self { + bit_width, + sim_only_values_len, + } + } + pub const fn only_bit_width(self) -> Option { + if let Self { + bit_width, + sim_only_values_len: 0, + } = self + { + Some(bit_width) + } else { + None + } + } + pub const fn empty() -> Self { + Self { + bit_width: 0, + sim_only_values_len: 0, + } + } + pub const fn is_empty(self) -> bool { + let Self { + bit_width, + sim_only_values_len, + } = self; + bit_width == 0 && sim_only_values_len == 0 + } + pub const fn checked_mul(self, factor: usize) -> Option { + let Some(bit_width) = self.bit_width.checked_mul(factor) else { + return None; + }; + let Some(sim_only_values_len) = self.sim_only_values_len.checked_mul(factor) else { + return None; + }; + Some(Self { + bit_width, + sim_only_values_len, + }) + } + pub const fn checked_add(self, rhs: Self) -> Option { + let Some(bit_width) = self.bit_width.checked_add(rhs.bit_width) else { + return None; + }; + let Some(sim_only_values_len) = self + .sim_only_values_len + .checked_add(rhs.sim_only_values_len) + else { + return None; + }; + Some(Self { + bit_width, + sim_only_values_len, + }) + } + pub const fn checked_sub(self, rhs: Self) -> Option { + let Some(bit_width) = self.bit_width.checked_sub(rhs.bit_width) else { + return None; + }; + let Some(sim_only_values_len) = self + .sim_only_values_len + .checked_sub(rhs.sim_only_values_len) + else { + return None; + }; + Some(Self { + bit_width, + sim_only_values_len, + }) + } + pub fn try_slice_range( + self, + range: R, + ) -> Option { + let start = range.start_bound(); + let end = range.end_bound(); + let bit_width = try_slice_range( + (start.map(|v| v.bit_width), end.map(|v| v.bit_width)), + self.bit_width, + )?; + let sim_only_values_len = try_slice_range( + ( + start.map(|v| v.sim_only_values_len), + end.map(|v| v.sim_only_values_len), + ), + self.sim_only_values_len, + )?; + Some(OpaqueSimValueSizeRange { + bit_width, + sim_only_values_len, + }) + } + pub fn slice_range( + self, + range: R, + ) -> OpaqueSimValueSizeRange { + self.try_slice_range(range).expect("range out of bounds") + } +} + +impl Mul for OpaqueSimValueSize { + type Output = OpaqueSimValueSize; + + fn mul(self, rhs: usize) -> Self::Output { + self.checked_mul(rhs).expect("multiplication overflowed") + } +} + +impl Mul for usize { + type Output = OpaqueSimValueSize; + + fn mul(self, rhs: OpaqueSimValueSize) -> Self::Output { + rhs.checked_mul(self).expect("multiplication overflowed") + } +} + +impl Add for OpaqueSimValueSize { + type Output = OpaqueSimValueSize; + + fn add(self, rhs: OpaqueSimValueSize) -> Self::Output { + rhs.checked_add(self).expect("addition overflowed") + } +} + +impl Sub for OpaqueSimValueSize { + type Output = OpaqueSimValueSize; + + fn sub(self, rhs: OpaqueSimValueSize) -> Self::Output { + rhs.checked_sub(self).expect("subtraction underflowed") + } +} + +impl MulAssign for OpaqueSimValueSize { + fn mul_assign(&mut self, rhs: usize) { + *self = *self * rhs; + } +} + +impl AddAssign for OpaqueSimValueSize { + fn add_assign(&mut self, rhs: OpaqueSimValueSize) { + *self = *self + rhs; + } +} + +impl SubAssign for OpaqueSimValueSize { + fn sub_assign(&mut self, rhs: OpaqueSimValueSize) { + *self = *self - rhs; + } +} + +impl Sum for OpaqueSimValueSize { + fn sum>(iter: I) -> Self { + iter.fold(OpaqueSimValueSize::empty(), Add::add) + } +} + +#[derive(Clone, PartialEq, Eq, Hash, Debug, Serialize, Deserialize)] +pub struct OpaqueSimValue { + bits: UIntValue, + #[serde(skip_serializing_if = "Vec::is_empty", default)] + sim_only_values: Vec, +} + +impl OpaqueSimValue { + pub fn empty() -> Self { + Self { + bits: UIntValue::new(Default::default()), + sim_only_values: Vec::new(), + } + } + pub fn with_capacity(capacity: OpaqueSimValueSize) -> Self { + Self { + bits: UIntValue::new(Arc::new(BitVec::with_capacity(capacity.bit_width))), + sim_only_values: Vec::with_capacity(capacity.sim_only_values_len), + } + } + pub fn size(&self) -> OpaqueSimValueSize { + OpaqueSimValueSize { + bit_width: self.bits.width(), + sim_only_values_len: self.sim_only_values.len(), + } + } + pub fn is_empty(&self) -> bool { + self.size().is_empty() + } + 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, + sim_only_values: Vec::new(), + } + } + pub fn from_bitslice(v: &BitSlice) -> Self { + Self::from_bitslice_and_sim_only_values(v, Vec::new()) + } + pub fn from_bitslice_and_sim_only_values( + bits: &BitSlice, + sim_only_values: Vec, + ) -> Self { + Self { + bits: UIntValue::new(Arc::new(bits.to_bitvec())), + sim_only_values, + } + } + pub fn from_bits_and_sim_only_values( + bits: UIntValue, + sim_only_values: Vec, + ) -> Self { + Self { + bits, + sim_only_values, + } + } + pub fn into_parts(self) -> (UIntValue, Vec) { + let Self { + bits, + sim_only_values, + } = self; + (bits, sim_only_values) + } + pub fn parts_mut(&mut self) -> (&mut UIntValue, &mut Vec) { + let Self { + bits, + sim_only_values, + } = self; + (bits, sim_only_values) + } + pub fn sim_only_values(&self) -> &[DynSimOnlyValue] { + &self.sim_only_values + } + pub fn sim_only_values_mut(&mut self) -> &mut Vec { + &mut self.sim_only_values + } + pub fn as_slice(&self) -> OpaqueSimValueSlice<'_> { + OpaqueSimValueSlice { + bits: self.bits.bits(), + sim_only_values: &self.sim_only_values, + } + } + pub fn slice(&self, range: R) -> OpaqueSimValueSlice<'_> { + self.as_slice().slice(range) + } + pub fn rewrite_with(&mut self, target_size: OpaqueSimValueSize, f: F) + where + F: for<'b> FnOnce(OpaqueSimValueWriter<'b>) -> OpaqueSimValueWritten<'b>, // 'b is used as a brand + { + OpaqueSimValueWriter::rewrite_with(target_size, self, f); + } + pub fn clone_from_slice(&mut self, slice: OpaqueSimValueSlice<'_>) { + let OpaqueSimValueSlice { + bits, + sim_only_values, + } = slice; + self.bits.bits_mut().copy_from_bitslice(bits); + self.sim_only_values.clone_from_slice(sim_only_values); + } + pub fn extend_from_slice(&mut self, slice: OpaqueSimValueSlice<'_>) { + let OpaqueSimValueSlice { + bits, + sim_only_values, + } = slice; + self.bits.bitvec_mut().extend_from_bitslice(bits); + self.sim_only_values.extend_from_slice(sim_only_values); + } +} + +impl<'a> Extend> for OpaqueSimValue { + fn extend>>(&mut self, iter: T) { + let Self { + bits, + sim_only_values, + } = self; + let bits = bits.bitvec_mut(); + for slice in iter { + bits.extend_from_bitslice(slice.bits); + sim_only_values.extend_from_slice(slice.sim_only_values); + } + } +} + +impl Extend for OpaqueSimValue { + fn extend>(&mut self, iter: T) { + let Self { + bits, + sim_only_values, + } = self; + let bits = bits.bitvec_mut(); + for value in iter { + bits.extend_from_bitslice(value.bits().bits()); + sim_only_values.extend_from_slice(value.sim_only_values()); + } + } +} + +impl> ToSimValueWithType for OpaqueSimValue { + fn to_sim_value_with_type(&self, ty: T) -> SimValue { + SimValue::from_value(ty, self.clone()) + } + fn into_sim_value_with_type(self, ty: T) -> SimValue { + SimValue::from_value(ty, self) + } +} + +#[derive(Copy, Clone, Debug)] +pub struct OpaqueSimValueSlice<'a> { + bits: &'a BitSlice, + sim_only_values: &'a [DynSimOnlyValue], +} + +impl<'a> Default for OpaqueSimValueSlice<'a> { + fn default() -> Self { + Self::empty() + } +} + +impl<'a> OpaqueSimValueSlice<'a> { + pub fn from_parts(bits: &'a BitSlice, sim_only_values: &'a [DynSimOnlyValue]) -> Self { + Self { + bits, + sim_only_values, + } + } + pub fn from_bitslice(bits: &'a BitSlice) -> Self { + Self::from_parts(bits, &[]) + } + pub fn empty() -> Self { + Self { + bits: BitSlice::empty(), + sim_only_values: &[], + } + } + pub fn size(self) -> OpaqueSimValueSize { + OpaqueSimValueSize { + bit_width: self.bit_width(), + sim_only_values_len: self.sim_only_values_len(), + } + } + pub fn is_empty(self) -> bool { + self.size().is_empty() + } + pub fn bit_width(self) -> usize { + self.bits.len() + } + pub fn bits(self) -> &'a BitSlice { + self.bits + } + pub fn sim_only_values(self) -> &'a [DynSimOnlyValue] { + self.sim_only_values + } + pub fn sim_only_values_len(self) -> usize { + self.sim_only_values.len() + } + pub fn to_owned(self) -> OpaqueSimValue { + OpaqueSimValue::from_bitslice_and_sim_only_values(self.bits, self.sim_only_values.to_vec()) + } + pub fn slice(self, range: R) -> OpaqueSimValueSlice<'a> { + let start = range.start_bound(); + let end = range.end_bound(); + let bits_range = slice_range( + (start.map(|v| v.bit_width), end.map(|v| v.bit_width)), + self.bit_width(), + ); + let sim_only_values_range = slice_range( + (start.map(|v| v.bit_width), end.map(|v| v.bit_width)), + self.sim_only_values_len(), + ); + Self { + bits: &self.bits[bits_range], + sim_only_values: &self.sim_only_values[sim_only_values_range], + } + } + pub fn split_at(self, index: OpaqueSimValueSize) -> (Self, Self) { + let bits = self.bits.split_at(index.bit_width); + let sim_only_values = self.sim_only_values.split_at(index.sim_only_values_len); + ( + Self { + bits: bits.0, + sim_only_values: sim_only_values.0, + }, + Self { + bits: bits.1, + sim_only_values: sim_only_values.1, + }, + ) + } +} + +#[derive(Debug)] +pub struct OpaqueSimValueWriter<'a> { + bits: &'a mut BitSlice, + sim_only_values: &'a mut Vec, + sim_only_values_range: std::ops::Range, +} + +#[derive(Debug)] +pub struct OpaqueSimValueWritten<'a> { + _phantom: PhantomData<&'a ()>, +} + +impl<'a> OpaqueSimValueWriter<'a> { + pub fn sim_only_values_range(&self) -> std::ops::Range { + self.sim_only_values_range.clone() + } + pub fn rewrite_with(target_size: OpaqueSimValueSize, value: &mut OpaqueSimValue, f: F) + where + F: for<'b> FnOnce(OpaqueSimValueWriter<'b>) -> OpaqueSimValueWritten<'b>, // 'b is used as a brand + { + let OpaqueSimValueWritten { + _phantom: PhantomData, + } = f(OpaqueSimValueWriter::rewrite_helper(target_size, value)); + } + pub(crate) fn rewrite_helper( + target_size: OpaqueSimValueSize, + value: &'a mut OpaqueSimValue, + ) -> Self { + let (bits, sim_only_values) = value.parts_mut(); + let OpaqueSimValueSize { + bit_width, + sim_only_values_len, + } = target_size; + let bits = bits.bitvec_mut(); + bits.resize(bit_width, false); + sim_only_values.truncate(sim_only_values_len); + sim_only_values.reserve_exact(sim_only_values_len - sim_only_values.len()); + Self { + bits, + sim_only_values, + sim_only_values_range: 0..sim_only_values_len, + } + } + pub fn size(&self) -> OpaqueSimValueSize { + OpaqueSimValueSize { + bit_width: self.bit_width(), + sim_only_values_len: self.sim_only_values_len(), + } + } + pub fn bit_width(&self) -> usize { + self.bits.len() + } + pub fn sim_only_values_len(&self) -> usize { + self.sim_only_values_range.len() + } + pub fn is_empty(&self) -> bool { + self.size().is_empty() + } + pub fn fill_cloned_from_slice( + self, + slice: OpaqueSimValueSlice<'_>, + ) -> OpaqueSimValueWritten<'a> { + assert_eq!(self.size(), slice.size()); + let Self { + bits, + sim_only_values, + sim_only_values_range, + } = self; + bits.copy_from_bitslice(slice.bits); + let (clone_from_src, clone_src) = slice.sim_only_values.split_at( + (sim_only_values.len() - sim_only_values_range.start).min(slice.sim_only_values.len()), + ); + sim_only_values[sim_only_values_range.start..][..clone_from_src.len()] + .clone_from_slice(clone_from_src); + sim_only_values.extend_from_slice(clone_src); + OpaqueSimValueWritten { + _phantom: PhantomData, + } + } + pub fn fill_with_bits_with(self, f: F) -> OpaqueSimValueWritten<'a> { + assert!(self.size().only_bit_width().is_some()); + let Self { + bits, + sim_only_values, + sim_only_values_range, + } = self; + f(bits); + assert_eq!(sim_only_values.len(), sim_only_values_range.end); + OpaqueSimValueWritten { + _phantom: PhantomData, + } + } + pub fn fill_with_zeros(self) -> OpaqueSimValueWritten<'a> { + assert!( + self.size().only_bit_width().is_some(), + "can't fill things other than bits with zeros", + ); + self.fill_with_bits_with(|bits| bits.fill(false)) + } + pub fn fill_prefix_with(&mut self, prefix_size: OpaqueSimValueSize, f: F) + where + F: for<'b> FnOnce(OpaqueSimValueWriter<'b>) -> OpaqueSimValueWritten<'b>, // 'b is used as a brand + { + let OpaqueSimValueSize { + bit_width, + sim_only_values_len, + } = prefix_size; + assert!(bit_width <= self.bit_width()); + assert!(sim_only_values_len <= self.sim_only_values_len()); + let next_start = self.sim_only_values_range.start + sim_only_values_len; + let OpaqueSimValueWritten { + _phantom: PhantomData, + } = f(OpaqueSimValueWriter { + bits: &mut self.bits[..bit_width], + sim_only_values: self.sim_only_values, + sim_only_values_range: self.sim_only_values_range.start..next_start, + }); + assert!(self.sim_only_values.len() >= next_start); + self.bits = &mut mem::take(&mut self.bits)[bit_width..]; + self.sim_only_values_range.start = next_start; + } +} + +pub trait StaticType: Type + Default { const TYPE: Self; const MASK_TYPE: Self::MaskType; const TYPE_PROPERTIES: TypeProperties; diff --git a/crates/fayalite/src/ty/serde_impls.rs b/crates/fayalite/src/ty/serde_impls.rs new file mode 100644 index 0000000..1ca916b --- /dev/null +++ b/crates/fayalite/src/ty/serde_impls.rs @@ -0,0 +1,135 @@ +// 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}, + sim::value::DynSimOnly, + ty::{BaseType, CanonicalType}, +}; +use serde::{Deserialize, Deserializer, Serialize, Serializer}; + +pub(crate) struct SerdePhantomConst(pub T); + +impl Serialize for SerdePhantomConst> { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + self.0.serialize(serializer) + } +} + +impl<'de, T: ?Sized + PhantomConstValue> Deserialize<'de> for SerdePhantomConst> { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + T::deserialize_value(deserializer).map(Self) + } +} + +#[derive(Serialize, Deserialize)] +#[serde(rename = "CanonicalType")] +pub(crate) enum SerdeCanonicalType< + ArrayElement = CanonicalType, + ThePhantomConst = SerdePhantomConst>, +> { + UInt { + width: usize, + }, + SInt { + width: usize, + }, + Bool, + Array { + element: ArrayElement, + len: usize, + }, + Enum { + variants: Interned<[crate::enum_::EnumVariant]>, + }, + Bundle { + fields: Interned<[crate::bundle::BundleField]>, + }, + AsyncReset, + SyncReset, + Reset, + Clock, + PhantomConst(ThePhantomConst), + DynSimOnly(DynSimOnly), +} + +impl SerdeCanonicalType { + pub(crate) fn as_serde_unexpected_str(&self) -> &'static str { + match self { + Self::UInt { .. } => "a UInt", + Self::SInt { .. } => "a SInt", + Self::Bool => "a Bool", + Self::Array { .. } => "an Array", + Self::Enum { .. } => "an Enum", + Self::Bundle { .. } => "a Bundle", + Self::AsyncReset => "an AsyncReset", + Self::SyncReset => "a SyncReset", + Self::Reset => "a Reset", + Self::Clock => "a Clock", + Self::PhantomConst(_) => "a PhantomConst", + Self::DynSimOnly(_) => "a SimOnlyValue", + } + } +} + +impl From for SerdeCanonicalType { + fn from(ty: T) -> Self { + let ty: CanonicalType = ty.into(); + match ty { + CanonicalType::UInt(ty) => Self::UInt { width: ty.width() }, + CanonicalType::SInt(ty) => Self::SInt { width: ty.width() }, + CanonicalType::Bool(Bool {}) => Self::Bool, + CanonicalType::Array(ty) => Self::Array { + element: ty.element(), + len: ty.len(), + }, + CanonicalType::Enum(ty) => Self::Enum { + variants: ty.variants(), + }, + CanonicalType::Bundle(ty) => Self::Bundle { + fields: ty.fields(), + }, + CanonicalType::AsyncReset(AsyncReset {}) => Self::AsyncReset, + CanonicalType::SyncReset(SyncReset {}) => Self::SyncReset, + CanonicalType::Reset(Reset {}) => Self::Reset, + CanonicalType::Clock(Clock {}) => Self::Clock, + CanonicalType::PhantomConst(ty) => Self::PhantomConst(SerdePhantomConst(ty.get())), + CanonicalType::DynSimOnly(ty) => Self::DynSimOnly(ty), + } + } +} + +impl From for CanonicalType { + fn from(ty: SerdeCanonicalType) -> Self { + match ty { + SerdeCanonicalType::UInt { width } => Self::UInt(UInt::new(width)), + SerdeCanonicalType::SInt { width } => Self::SInt(SInt::new(width)), + SerdeCanonicalType::Bool => Self::Bool(Bool), + SerdeCanonicalType::Array { element, len } => Self::Array(Array::new(element, len)), + SerdeCanonicalType::Enum { variants } => Self::Enum(Enum::new(variants)), + SerdeCanonicalType::Bundle { fields } => Self::Bundle(Bundle::new(fields)), + SerdeCanonicalType::AsyncReset => Self::AsyncReset(AsyncReset), + SerdeCanonicalType::SyncReset => Self::SyncReset(SyncReset), + SerdeCanonicalType::Reset => Self::Reset(Reset), + SerdeCanonicalType::Clock => Self::Clock(Clock), + SerdeCanonicalType::PhantomConst(value) => { + Self::PhantomConst(PhantomConst::new(value.0)) + } + SerdeCanonicalType::DynSimOnly(value) => Self::DynSimOnly(value), + } + } +} diff --git a/crates/fayalite/src/util.rs b/crates/fayalite/src/util.rs index fadc7af..e85bc9c 100644 --- a/crates/fayalite/src/util.rs +++ b/crates/fayalite/src/util.rs @@ -1,12 +1,23 @@ // SPDX-License-Identifier: LGPL-3.0-or-later // See Notices.txt for copyright information +pub(crate) mod alternating_cell; mod const_bool; mod const_cmp; mod const_usize; mod misc; mod scoped_ref; pub(crate) mod streaming_read_utf8; +mod test_hasher; + +// allow easily switching the hasher crate-wide for testing +#[cfg(feature = "unstable-test-hasher")] +pub type DefaultBuildHasher = test_hasher::DefaultBuildHasher; +#[cfg(not(feature = "unstable-test-hasher"))] +pub(crate) type DefaultBuildHasher = hashbrown::DefaultHashBuilder; + +pub(crate) type HashMap = hashbrown::HashMap; +pub(crate) type HashSet = hashbrown::HashSet; #[doc(inline)] pub use const_bool::{ConstBool, ConstBoolDispatch, ConstBoolDispatchTag, GenericConstBool}; @@ -22,11 +33,13 @@ pub use const_cmp::{ #[doc(inline)] pub use scoped_ref::ScopedRef; +pub(crate) use misc::chain; #[doc(inline)] pub use misc::{ - get_many_mut, interned_bit, iter_eq_by, BitSliceWriteWithBase, DebugAsDisplay, - DebugAsRawString, MakeMutSlice, RcWriter, + BitSliceWriteWithBase, DebugAsDisplay, DebugAsRawString, MakeMutSlice, RcWriter, interned_bit, + iter_eq_by, slice_range, try_slice_range, }; pub mod job_server; +pub mod prefix_sum; pub mod ready_valid; diff --git a/crates/fayalite/src/util/alternating_cell.rs b/crates/fayalite/src/util/alternating_cell.rs new file mode 100644 index 0000000..17e06a6 --- /dev/null +++ b/crates/fayalite/src/util/alternating_cell.rs @@ -0,0 +1,122 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// See Notices.txt for copyright information + +use crate::util::DebugAsDisplay; +use std::{ + cell::{Cell, UnsafeCell}, + fmt, +}; + +pub(crate) trait AlternatingCellMethods { + fn unique_to_shared(&mut self); + fn shared_to_unique(&mut self); +} + +#[derive(Copy, Clone, Debug)] +enum State { + Unique, + Shared, + Locked, +} + +pub(crate) struct AlternatingCell { + state: Cell, + value: UnsafeCell, +} + +impl fmt::Debug for AlternatingCell { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_tuple("AlternatingCell") + .field( + self.try_share() + .as_ref() + .map(|v| -> &dyn fmt::Debug { v }) + .unwrap_or(&DebugAsDisplay("<...>")), + ) + .finish() + } +} + +impl AlternatingCell { + pub(crate) const fn new_shared(value: T) -> Self + where + T: Sized, + { + Self { + state: Cell::new(State::Shared), + value: UnsafeCell::new(value), + } + } + pub(crate) const fn new_unique(value: T) -> Self + where + T: Sized, + { + Self { + state: Cell::new(State::Unique), + value: UnsafeCell::new(value), + } + } + pub(crate) fn is_unique(&self) -> bool { + matches!(self.state.get(), State::Unique) + } + pub(crate) fn is_shared(&self) -> bool { + matches!(self.state.get(), State::Shared) + } + pub(crate) fn into_inner(self) -> T + where + T: Sized, + { + self.value.into_inner() + } + pub(crate) fn try_share(&self) -> Option<&T> + where + T: AlternatingCellMethods, + { + match self.state.get() { + State::Shared => {} + State::Unique => { + struct Locked<'a>(&'a Cell); + impl Drop for Locked<'_> { + fn drop(&mut self) { + self.0.set(State::Shared); + } + } + self.state.set(State::Locked); + let lock = Locked(&self.state); + // Safety: state is Locked, so nothing else will + // access value while calling unique_to_shared. + unsafe { &mut *self.value.get() }.unique_to_shared(); + drop(lock); + } + State::Locked => return None, + } + + // Safety: state is Shared so nothing will create any mutable + // references until the returned reference's lifetime expires. + Some(unsafe { &*self.value.get() }) + } + #[track_caller] + pub(crate) fn share(&self) -> &T + where + T: AlternatingCellMethods, + { + let Some(retval) = self.try_share() else { + panic!("`share` called recursively"); + }; + retval + } + pub(crate) fn unique(&mut self) -> &mut T + where + T: AlternatingCellMethods, + { + match self.state.get() { + State::Shared => { + self.state.set(State::Unique); + self.value.get_mut().shared_to_unique(); + } + State::Unique => {} + State::Locked => unreachable!(), + } + self.value.get_mut() + } +} diff --git a/crates/fayalite/src/util/const_bool.rs b/crates/fayalite/src/util/const_bool.rs index 7033d6a..050f6a7 100644 --- a/crates/fayalite/src/util/const_bool.rs +++ b/crates/fayalite/src/util/const_bool.rs @@ -1,5 +1,9 @@ // SPDX-License-Identifier: LGPL-3.0-or-later // See Notices.txt for copyright information +use serde::{ + Deserialize, Deserializer, Serialize, Serializer, + de::{DeserializeOwned, Error, Unexpected}, +}; use std::{fmt::Debug, hash::Hash, mem::ManuallyDrop, ptr}; mod sealed { @@ -9,7 +13,17 @@ mod sealed { /// # Safety /// the only implementation is `ConstBool` pub unsafe trait GenericConstBool: - sealed::Sealed + Copy + Ord + Hash + Default + Debug + 'static + Send + Sync + sealed::Sealed + + Copy + + Ord + + Hash + + Default + + Debug + + 'static + + Send + + Sync + + Serialize + + DeserializeOwned { const VALUE: bool; } @@ -30,6 +44,32 @@ unsafe impl GenericConstBool for ConstBool { const VALUE: bool = VALUE; } +impl Serialize for ConstBool { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + VALUE.serialize(serializer) + } +} + +impl<'de, const VALUE: bool> Deserialize<'de> for ConstBool { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let value = bool::deserialize(deserializer)?; + if value == VALUE { + Ok(ConstBool) + } else { + Err(D::Error::invalid_value( + Unexpected::Bool(value), + &if VALUE { "true" } else { "false" }, + )) + } + } +} + pub trait ConstBoolDispatchTag { type Type; } diff --git a/crates/fayalite/src/util/const_usize.rs b/crates/fayalite/src/util/const_usize.rs index a605336..d76f7a7 100644 --- a/crates/fayalite/src/util/const_usize.rs +++ b/crates/fayalite/src/util/const_usize.rs @@ -1,5 +1,9 @@ // SPDX-License-Identifier: LGPL-3.0-or-later // See Notices.txt for copyright information +use serde::{ + Deserialize, Deserializer, Serialize, Serializer, + de::{DeserializeOwned, Error, Unexpected}, +}; use std::{fmt::Debug, hash::Hash}; mod sealed { @@ -8,7 +12,17 @@ mod sealed { /// the only implementation is `ConstUsize` pub trait GenericConstUsize: - sealed::Sealed + Copy + Ord + Hash + Default + Debug + 'static + Send + Sync + sealed::Sealed + + Copy + + Ord + + Hash + + Default + + Debug + + 'static + + Send + + Sync + + Serialize + + DeserializeOwned { const VALUE: usize; } @@ -27,3 +41,29 @@ impl sealed::Sealed for ConstUsize {} impl GenericConstUsize for ConstUsize { const VALUE: usize = VALUE; } + +impl Serialize for ConstUsize { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + VALUE.serialize(serializer) + } +} + +impl<'de, const VALUE: usize> Deserialize<'de> for ConstUsize { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let value = usize::deserialize(deserializer)?; + if value == VALUE { + Ok(ConstUsize) + } else { + Err(D::Error::invalid_value( + Unexpected::Unsigned(value as u64), + &&*VALUE.to_string(), + )) + } + } +} diff --git a/crates/fayalite/src/util/misc.rs b/crates/fayalite/src/util/misc.rs index f482eaa..cebbceb 100644 --- a/crates/fayalite/src/util/misc.rs +++ b/crates/fayalite/src/util/misc.rs @@ -5,6 +5,7 @@ use bitvec::{bits, order::Lsb0, slice::BitSlice, view::BitView}; use std::{ cell::Cell, fmt::{self, Debug, Write}, + ops::{Bound, Range, RangeBounds}, rc::Rc, sync::{Arc, OnceLock}, }; @@ -163,22 +164,6 @@ impl fmt::UpperHex for BitSliceWriteWithBase<'_> { } } -#[inline] -#[track_caller] -pub fn get_many_mut(slice: &mut [T], indexes: [usize; N]) -> [&mut T; N] { - for i in 0..N { - for j in 0..i { - assert!(indexes[i] != indexes[j], "duplicate index"); - } - assert!(indexes[i] < slice.len(), "index out of bounds"); - } - // Safety: checked that no indexes are duplicates and no indexes are out of bounds - unsafe { - let base = slice.as_mut_ptr(); // convert to a raw pointer before loop to avoid aliasing with &mut [T] - std::array::from_fn(|i| &mut *base.add(indexes[i])) - } -} - #[derive(Clone, Default)] pub struct RcWriter(Rc>>); @@ -225,3 +210,36 @@ impl std::io::Write for RcWriter { Ok(()) } } + +macro_rules! chain { + () => { + std::iter::empty() + }; + ($first:expr $(, $rest:expr)* $(,)?) => { + { + let retval = IntoIterator::into_iter($first); + $(let retval = Iterator::chain(retval, $rest);)* + retval + } + }; +} + +pub(crate) use chain; + +pub fn try_slice_range>(range: R, size: usize) -> Option> { + let start = match range.start_bound() { + Bound::Included(start) => *start, + Bound::Excluded(start) => start.checked_add(1)?, + Bound::Unbounded => 0, + }; + let end = match range.end_bound() { + Bound::Included(end) => end.checked_add(1)?, + Bound::Excluded(end) => *end, + Bound::Unbounded => size, + }; + (start <= end && end <= size).then_some(start..end) +} + +pub fn slice_range>(range: R, size: usize) -> Range { + try_slice_range(range, size).expect("range out of bounds") +} diff --git a/crates/fayalite/src/util/prefix_sum.rs b/crates/fayalite/src/util/prefix_sum.rs new file mode 100644 index 0000000..98e6d95 --- /dev/null +++ b/crates/fayalite/src/util/prefix_sum.rs @@ -0,0 +1,839 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// See Notices.txt for copyright information + +// code derived from: +// https://web.archive.org/web/20250303054010/https://git.libre-soc.org/?p=nmutil.git;a=blob;f=src/nmutil/prefix_sum.py;hb=effeb28e5848392adddcdad1f6e7a098f2a44c9c + +use crate::intern::{Intern, Interned, Memoize}; +use std::{borrow::Cow, num::NonZeroUsize}; + +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] +pub struct PrefixSumOp { + pub lhs_index: usize, + pub rhs_and_dest_index: NonZeroUsize, + pub row: u32, +} + +#[derive(Clone, PartialEq, Eq, Hash, Debug)] +#[non_exhaustive] +pub struct DiagramConfig { + pub space: Cow<'static, str>, + pub vertical_bar: Cow<'static, str>, + pub plus: Cow<'static, str>, + pub slant: Cow<'static, str>, + pub connect: Cow<'static, str>, + pub no_connect: Cow<'static, str>, + pub padding: usize, +} + +impl DiagramConfig { + pub const fn new() -> Self { + Self { + space: Cow::Borrowed(" "), + vertical_bar: Cow::Borrowed("|"), + plus: Cow::Borrowed("\u{2295}"), // ⊕ + slant: Cow::Borrowed(r"\"), + connect: Cow::Borrowed("\u{25CF}"), // ● + no_connect: Cow::Borrowed("X"), + padding: 1, + } + } + pub fn draw(self, ops: impl IntoIterator, item_count: usize) -> String { + #[derive(Copy, Clone, Debug)] + struct DiagramCell { + slant: bool, + plus: bool, + tee: bool, + } + let mut ops_by_row: Vec> = Vec::new(); + let mut last_row = 0; + ops.into_iter().for_each(|op| { + assert!( + op.lhs_index < op.rhs_and_dest_index.get(), + "invalid PrefixSumOp! lhs_index must be less \ + than rhs_and_dest_index: {op:?}", + ); + assert!( + op.row >= last_row, + "invalid PrefixSumOp! row must \ + not decrease (row last was: {last_row}): {op:?}", + ); + let ops = if op.row > last_row || ops_by_row.is_empty() { + ops_by_row.push(vec![]); + ops_by_row.last_mut().expect("just pushed") + } else { + ops_by_row + .last_mut() + .expect("just checked if ops_by_row is empty") + }; + if let Some(last) = ops.last() { + assert!( + op.rhs_and_dest_index < last.rhs_and_dest_index, + "invalid PrefixSumOp! rhs_and_dest_index must strictly \ + decrease in a row:\nthis op: {op:?}\nlast op: {last:?}", + ); + } + ops.push(op); + last_row = op.row; + }); + let blank_row = || { + vec![ + DiagramCell { + slant: false, + plus: false, + tee: false + }; + item_count + ] + }; + let mut cells = vec![blank_row()]; + for ops in ops_by_row { + let max_distance = ops + .iter() + .map( + |&PrefixSumOp { + lhs_index, + rhs_and_dest_index, + .. + }| { rhs_and_dest_index.get() - lhs_index }, + ) + .max() + .expect("ops is known to be non-empty"); + cells.extend((0..max_distance).map(|_| blank_row())); + for op in ops { + let mut y = cells.len() - 1; + assert!( + op.rhs_and_dest_index.get() < item_count, + "invalid PrefixSumOp! rhs_and_dest_index must be \ + less than item_count ({item_count}): {op:?}", + ); + let mut x = op.rhs_and_dest_index.get(); + cells[y][x].plus = true; + x -= 1; + y -= 1; + while op.lhs_index < x { + cells[y][x].slant = true; + x -= 1; + y -= 1; + } + cells[y][x].tee = true; + } + } + let mut retval = String::new(); + let mut row_text = vec![String::new(); 2 * self.padding + 1]; + for cells_row in cells { + for cell in cells_row { + // top padding + for y in 0..self.padding { + // top left padding + for x in 0..self.padding { + row_text[y] += if x == y && (cell.plus || cell.slant) { + &self.slant + } else { + &self.space + }; + } + // top vertical bar + row_text[y] += &self.vertical_bar; + // top right padding + for _ in 0..self.padding { + row_text[y] += &self.space; + } + } + // center left padding + for _ in 0..self.padding { + row_text[self.padding] += &self.space; + } + // center + row_text[self.padding] += if cell.plus { + &self.plus + } else if cell.tee { + &self.connect + } else if cell.slant { + &self.no_connect + } else { + &self.vertical_bar + }; + // center right padding + for _ in 0..self.padding { + row_text[self.padding] += &self.space; + } + let bottom_padding_start = self.padding + 1; + let bottom_padding_last = self.padding * 2; + // bottom padding + for y in bottom_padding_start..=bottom_padding_last { + // bottom left padding + for _ in 0..self.padding { + row_text[y] += &self.space; + } + // bottom vertical bar + row_text[y] += &self.vertical_bar; + // bottom right padding + for x in bottom_padding_start..=bottom_padding_last { + row_text[y] += if x == y && (cell.tee || cell.slant) { + &self.slant + } else { + &self.space + }; + } + } + } + for line in &mut row_text { + retval += line.trim_end(); + retval += "\n"; + line.clear(); + } + } + retval + } +} + +impl Default for DiagramConfig { + fn default() -> Self { + Self::new() + } +} + +impl PrefixSumOp { + pub fn diagram(ops: impl IntoIterator, item_count: usize) -> String { + Self::diagram_with_config(ops, item_count, DiagramConfig::new()) + } + pub fn diagram_with_config( + ops: impl IntoIterator, + item_count: usize, + config: DiagramConfig, + ) -> String { + config.draw(ops, item_count) + } +} + +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +pub enum PrefixSumAlgorithm { + /// Uses the algorithm from: + /// + LowLatency, + /// Uses the algorithm from: + /// + WorkEfficient, +} + +impl PrefixSumAlgorithm { + fn ops_impl(self, item_count: usize) -> Vec { + let mut retval = Vec::new(); + let mut distance = 1; + let mut row = 0; + while distance < item_count { + let double_distance = distance + .checked_mul(2) + .expect("prefix-sum item_count is too big"); + let (start, step) = match self { + Self::LowLatency => (distance, 1), + Self::WorkEfficient => (double_distance - 1, double_distance), + }; + for rhs_and_dest_index in (start..item_count).step_by(step).rev() { + let Some(rhs_and_dest_index) = NonZeroUsize::new(rhs_and_dest_index) else { + unreachable!(); + }; + let lhs_index = rhs_and_dest_index.get() - distance; + retval.push(PrefixSumOp { + lhs_index, + rhs_and_dest_index, + row, + }); + } + distance = double_distance; + row += 1; + } + match self { + Self::LowLatency => {} + Self::WorkEfficient => { + distance /= 2; + while distance >= 1 { + let start = distance + .checked_mul(3) + .expect("prefix-sum item_count is too big") + - 1; + for rhs_and_dest_index in (start..item_count).step_by(distance * 2).rev() { + let Some(rhs_and_dest_index) = NonZeroUsize::new(rhs_and_dest_index) else { + unreachable!(); + }; + let lhs_index = rhs_and_dest_index.get() - distance; + retval.push(PrefixSumOp { + lhs_index, + rhs_and_dest_index, + row, + }); + } + row += 1; + distance /= 2; + } + } + } + retval + } + pub fn ops(self, item_count: usize) -> Interned<[PrefixSumOp]> { + #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] + struct MyMemoize(PrefixSumAlgorithm); + impl Memoize for MyMemoize { + type Input = usize; + type InputOwned = usize; + type Output = Interned<[PrefixSumOp]>; + + fn inner(self, item_count: &Self::Input) -> Self::Output { + Intern::intern_owned(self.0.ops_impl(*item_count)) + } + } + MyMemoize(self).get_owned(item_count) + } + pub fn run(self, items: impl IntoIterator, f: impl FnMut(&T, &T) -> T) -> Vec { + let mut items = Vec::from_iter(items); + self.run_on_slice(&mut items, f); + items + } + pub fn run_on_slice(self, items: &mut [T], mut f: impl FnMut(&T, &T) -> T) -> &mut [T] { + self.ops(items.len()).into_iter().for_each( + |PrefixSumOp { + lhs_index, + rhs_and_dest_index, + row: _, + }| { + items[rhs_and_dest_index.get()] = + f(&items[lhs_index], &items[rhs_and_dest_index.get()]); + }, + ); + items + } + pub fn filtered_ops( + self, + item_live_out_flags: impl IntoIterator, + ) -> Vec { + let mut item_live_out_flags = Vec::from_iter(item_live_out_flags); + let prefix_sum_ops = self.ops(item_live_out_flags.len()); + let mut ops_live_flags = vec![false; prefix_sum_ops.len()]; + for ( + op_index, + &PrefixSumOp { + lhs_index, + rhs_and_dest_index, + row: _, + }, + ) in prefix_sum_ops.iter().enumerate().rev() + { + let live = item_live_out_flags[rhs_and_dest_index.get()]; + item_live_out_flags[lhs_index] |= live; + ops_live_flags[op_index] = live; + } + prefix_sum_ops + .into_iter() + .zip(ops_live_flags) + .filter_map(|(op, live)| live.then_some(op)) + .collect() + } + pub fn reduce_ops(self, item_count: usize) -> Interned<[PrefixSumOp]> { + #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] + struct MyMemoize(PrefixSumAlgorithm); + impl Memoize for MyMemoize { + type Input = usize; + type InputOwned = usize; + type Output = Interned<[PrefixSumOp]>; + + fn inner(self, item_count: &Self::Input) -> Self::Output { + let mut item_live_out_flags = vec![false; *item_count]; + let Some(last_item_live_out_flag) = item_live_out_flags.last_mut() else { + return Interned::default(); + }; + *last_item_live_out_flag = true; + Intern::intern_owned(self.0.filtered_ops(item_live_out_flags)) + } + } + MyMemoize(self).get_owned(item_count) + } +} + +pub fn reduce_ops(item_count: usize) -> Interned<[PrefixSumOp]> { + PrefixSumAlgorithm::LowLatency.reduce_ops(item_count) +} + +pub fn reduce(items: impl IntoIterator, mut f: impl FnMut(T, T) -> T) -> Option { + let mut items: Vec<_> = items.into_iter().map(Some).collect(); + for op in reduce_ops(items.len()) { + let (Some(lhs), Some(rhs)) = ( + items[op.lhs_index].take(), + items[op.rhs_and_dest_index.get()].take(), + ) else { + unreachable!(); + }; + items[op.rhs_and_dest_index.get()] = Some(f(lhs, rhs)); + } + items.last_mut().and_then(Option::take) +} + +#[cfg(test)] +mod tests { + use super::*; + + fn input_strings() -> [String; 9] { + std::array::from_fn(|i| String::from_utf8(vec![b'a' + i as u8]).unwrap()) + } + + #[test] + fn test_prefix_sum_strings() { + let input = input_strings(); + let expected: Vec = input + .iter() + .scan(String::new(), |l, r| { + *l += r; + Some(l.clone()) + }) + .collect(); + println!("expected: {expected:?}"); + assert_eq!( + *PrefixSumAlgorithm::WorkEfficient + .run_on_slice(&mut input.clone(), |l, r| l.to_string() + r), + *expected + ); + assert_eq!( + *PrefixSumAlgorithm::LowLatency + .run_on_slice(&mut input.clone(), |l, r| l.to_string() + r), + *expected + ); + } + + #[test] + fn test_reduce_string() { + let input = input_strings(); + let expected = input.clone().into_iter().reduce(|l, r| l + &r); + assert_eq!(reduce(input, |l, r| l + &r), expected); + } + + fn op(lhs_index: usize, rhs_and_dest_index: usize, row: u32) -> PrefixSumOp { + PrefixSumOp { + lhs_index, + rhs_and_dest_index: NonZeroUsize::new(rhs_and_dest_index).expect("should be non-zero"), + row, + } + } + + #[test] + fn test_reduce_ops_9() { + let expected = vec![ + op(7, 8, 0), + op(5, 6, 0), + op(3, 4, 0), + op(1, 2, 0), + op(6, 8, 1), + op(2, 4, 1), + op(4, 8, 2), + op(0, 8, 3), + ]; + println!("expected: {expected:#?}"); + let ops = reduce_ops(9); + println!("ops: {ops:#?}"); + assert_eq!(*ops, *expected); + } + + #[test] + fn test_reduce_ops_8() { + let expected = vec![ + op(6, 7, 0), + op(4, 5, 0), + op(2, 3, 0), + op(0, 1, 0), + op(5, 7, 1), + op(1, 3, 1), + op(3, 7, 2), + ]; + println!("expected: {expected:#?}"); + let ops = reduce_ops(8); + println!("ops: {ops:#?}"); + assert_eq!(*ops, *expected); + } + + #[test] + fn test_count_ones() { + for width in 0..=10u32 { + for v in 0..1u32 << width { + let expected = v.count_ones(); + assert_eq!( + reduce((0..width).map(|i| (v >> i) & 1), |l, r| l + r).unwrap_or(0), + expected, + "v={v:#X}" + ); + } + } + } + + #[track_caller] + fn test_diagram(ops: impl IntoIterator, item_count: usize, expected: &str) { + let text = PrefixSumOp::diagram_with_config( + ops, + item_count, + DiagramConfig { + plus: Cow::Borrowed("@"), + ..Default::default() + }, + ); + println!("text:\n{text}\n"); + assert_eq!(text, expected); + } + + #[test] + fn test_work_efficient_diagram_16() { + let item_count = 16; + test_diagram( + PrefixSumAlgorithm::WorkEfficient.ops(item_count), + item_count, + &r" + | | | | | | | | | | | | | | | | + ● | ● | ● | ● | ● | ● | ● | ● | + |\ | |\ | |\ | |\ | |\ | |\ | |\ | |\ | + | \| | \| | \| | \| | \| | \| | \| | \| + | @ | @ | @ | @ | @ | @ | @ | @ + | |\ | | | |\ | | | |\ | | | |\ | | + | | \| | | | \| | | | \| | | | \| | + | | X | | | X | | | X | | | X | + | | |\ | | | |\ | | | |\ | | | |\ | + | | | \| | | | \| | | | \| | | | \| + | | | @ | | | @ | | | @ | | | @ + | | | |\ | | | | | | | |\ | | | | + | | | | \| | | | | | | | \| | | | + | | | | X | | | | | | | X | | | + | | | | |\ | | | | | | | |\ | | | + | | | | | \| | | | | | | | \| | | + | | | | | X | | | | | | | X | | + | | | | | |\ | | | | | | | |\ | | + | | | | | | \| | | | | | | | \| | + | | | | | | X | | | | | | | X | + | | | | | | |\ | | | | | | | |\ | + | | | | | | | \| | | | | | | | \| + | | | | | | | @ | | | | | | | @ + | | | | | | | |\ | | | | | | | | + | | | | | | | | \| | | | | | | | + | | | | | | | | X | | | | | | | + | | | | | | | | |\ | | | | | | | + | | | | | | | | | \| | | | | | | + | | | | | | | | | X | | | | | | + | | | | | | | | | |\ | | | | | | + | | | | | | | | | | \| | | | | | + | | | | | | | | | | X | | | | | + | | | | | | | | | | |\ | | | | | + | | | | | | | | | | | \| | | | | + | | | | | | | | | | | X | | | | + | | | | | | | | | | | |\ | | | | + | | | | | | | | | | | | \| | | | + | | | | | | | | | | | | X | | | + | | | | | | | | | | | | |\ | | | + | | | | | | | | | | | | | \| | | + | | | | | | | | | | | | | X | | + | | | | | | | | | | | | | |\ | | + | | | | | | | | | | | | | | \| | + | | | | | | | | | | | | | | X | + | | | | | | | | | | | | | | |\ | + | | | | | | | | | | | | | | | \| + | | | | | | | ● | | | | | | | @ + | | | | | | | |\ | | | | | | | | + | | | | | | | | \| | | | | | | | + | | | | | | | | X | | | | | | | + | | | | | | | | |\ | | | | | | | + | | | | | | | | | \| | | | | | | + | | | | | | | | | X | | | | | | + | | | | | | | | | |\ | | | | | | + | | | | | | | | | | \| | | | | | + | | | | | | | | | | X | | | | | + | | | | | | | | | | |\ | | | | | + | | | | | | | | | | | \| | | | | + | | | ● | | | ● | | | @ | | | | + | | | |\ | | | |\ | | | |\ | | | | + | | | | \| | | | \| | | | \| | | | + | | | | X | | | X | | | X | | | + | | | | |\ | | | |\ | | | |\ | | | + | | | | | \| | | | \| | | | \| | | + | ● | ● | @ | ● | @ | ● | @ | | + | |\ | |\ | |\ | |\ | |\ | |\ | |\ | | + | | \| | \| | \| | \| | \| | \| | \| | + | | @ | @ | @ | @ | @ | @ | @ | + | | | | | | | | | | | | | | | | +"[1..], // trim newline at start + ); + } + + #[test] + fn test_low_latency_diagram_16() { + let item_count = 16; + test_diagram( + PrefixSumAlgorithm::LowLatency.ops(item_count), + item_count, + &r" + | | | | | | | | | | | | | | | | + ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● | + |\ |\ |\ |\ |\ |\ |\ |\ |\ |\ |\ |\ |\ |\ |\ | + | \| \| \| \| \| \| \| \| \| \| \| \| \| \| \| + ● @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ + |\ |\ |\ |\ |\ |\ |\ |\ |\ |\ |\ |\ |\ |\ | | + | \| \| \| \| \| \| \| \| \| \| \| \| \| \| | + | X X X X X X X X X X X X X X | + | |\ |\ |\ |\ |\ |\ |\ |\ |\ |\ |\ |\ |\ |\ | + | | \| \| \| \| \| \| \| \| \| \| \| \| \| \| + ● ● @ @ @ @ @ @ @ @ @ @ @ @ @ @ + |\ |\ |\ |\ |\ |\ |\ |\ |\ |\ |\ |\ | | | | + | \| \| \| \| \| \| \| \| \| \| \| \| | | | + | X X X X X X X X X X X X | | | + | |\ |\ |\ |\ |\ |\ |\ |\ |\ |\ |\ |\ | | | + | | \| \| \| \| \| \| \| \| \| \| \| \| | | + | | X X X X X X X X X X X X | | + | | |\ |\ |\ |\ |\ |\ |\ |\ |\ |\ |\ |\ | | + | | | \| \| \| \| \| \| \| \| \| \| \| \| | + | | | X X X X X X X X X X X X | + | | | |\ |\ |\ |\ |\ |\ |\ |\ |\ |\ |\ |\ | + | | | | \| \| \| \| \| \| \| \| \| \| \| \| + ● ● ● ● @ @ @ @ @ @ @ @ @ @ @ @ + |\ |\ |\ |\ |\ |\ |\ |\ | | | | | | | | + | \| \| \| \| \| \| \| \| | | | | | | | + | X X X X X X X X | | | | | | | + | |\ |\ |\ |\ |\ |\ |\ |\ | | | | | | | + | | \| \| \| \| \| \| \| \| | | | | | | + | | X X X X X X X X | | | | | | + | | |\ |\ |\ |\ |\ |\ |\ |\ | | | | | | + | | | \| \| \| \| \| \| \| \| | | | | | + | | | X X X X X X X X | | | | | + | | | |\ |\ |\ |\ |\ |\ |\ |\ | | | | | + | | | | \| \| \| \| \| \| \| \| | | | | + | | | | X X X X X X X X | | | | + | | | | |\ |\ |\ |\ |\ |\ |\ |\ | | | | + | | | | | \| \| \| \| \| \| \| \| | | | + | | | | | X X X X X X X X | | | + | | | | | |\ |\ |\ |\ |\ |\ |\ |\ | | | + | | | | | | \| \| \| \| \| \| \| \| | | + | | | | | | X X X X X X X X | | + | | | | | | |\ |\ |\ |\ |\ |\ |\ |\ | | + | | | | | | | \| \| \| \| \| \| \| \| | + | | | | | | | X X X X X X X X | + | | | | | | | |\ |\ |\ |\ |\ |\ |\ |\ | + | | | | | | | | \| \| \| \| \| \| \| \| + | | | | | | | | @ @ @ @ @ @ @ @ + | | | | | | | | | | | | | | | | +"[1..], // trim newline at start + ); + } + + #[test] + fn test_work_efficient_diagram_9() { + let item_count = 9; + test_diagram( + PrefixSumAlgorithm::WorkEfficient.ops(item_count), + item_count, + &r" + | | | | | | | | | + ● | ● | ● | ● | | + |\ | |\ | |\ | |\ | | + | \| | \| | \| | \| | + | @ | @ | @ | @ | + | |\ | | | |\ | | | + | | \| | | | \| | | + | | X | | | X | | + | | |\ | | | |\ | | + | | | \| | | | \| | + | | | @ | | | @ | + | | | |\ | | | | | + | | | | \| | | | | + | | | | X | | | | + | | | | |\ | | | | + | | | | | \| | | | + | | | | | X | | | + | | | | | |\ | | | + | | | | | | \| | | + | | | | | | X | | + | | | | | | |\ | | + | | | | | | | \| | + | | | ● | | | @ | + | | | |\ | | | | | + | | | | \| | | | | + | | | | X | | | | + | | | | |\ | | | | + | | | | | \| | | | + | ● | ● | @ | ● | + | |\ | |\ | |\ | |\ | + | | \| | \| | \| | \| + | | @ | @ | @ | @ + | | | | | | | | | +"[1..], // trim newline at start + ); + } + + #[test] + fn test_low_latency_diagram_9() { + let item_count = 9; + test_diagram( + PrefixSumAlgorithm::LowLatency.ops(item_count), + item_count, + &r" + | | | | | | | | | + ● ● ● ● ● ● ● ● | + |\ |\ |\ |\ |\ |\ |\ |\ | + | \| \| \| \| \| \| \| \| + ● @ @ @ @ @ @ @ @ + |\ |\ |\ |\ |\ |\ |\ | | + | \| \| \| \| \| \| \| | + | X X X X X X X | + | |\ |\ |\ |\ |\ |\ |\ | + | | \| \| \| \| \| \| \| + ● ● @ @ @ @ @ @ @ + |\ |\ |\ |\ |\ | | | | + | \| \| \| \| \| | | | + | X X X X X | | | + | |\ |\ |\ |\ |\ | | | + | | \| \| \| \| \| | | + | | X X X X X | | + | | |\ |\ |\ |\ |\ | | + | | | \| \| \| \| \| | + | | | X X X X X | + | | | |\ |\ |\ |\ |\ | + | | | | \| \| \| \| \| + ● | | | @ @ @ @ @ + |\ | | | | | | | | + | \| | | | | | | | + | X | | | | | | | + | |\ | | | | | | | + | | \| | | | | | | + | | X | | | | | | + | | |\ | | | | | | + | | | \| | | | | | + | | | X | | | | | + | | | |\ | | | | | + | | | | \| | | | | + | | | | X | | | | + | | | | |\ | | | | + | | | | | \| | | | + | | | | | X | | | + | | | | | |\ | | | + | | | | | | \| | | + | | | | | | X | | + | | | | | | |\ | | + | | | | | | | \| | + | | | | | | | X | + | | | | | | | |\ | + | | | | | | | | \| + | | | | | | | | @ + | | | | | | | | | +"[1..], // trim newline at start + ); + } + + #[test] + fn test_reduce_diagram_16() { + let item_count = 16; + test_diagram( + reduce_ops(item_count), + item_count, + &r" + | | | | | | | | | | | | | | | | + ● | ● | ● | ● | ● | ● | ● | ● | + |\ | |\ | |\ | |\ | |\ | |\ | |\ | |\ | + | \| | \| | \| | \| | \| | \| | \| | \| + | @ | @ | @ | @ | @ | @ | @ | @ + | |\ | | | |\ | | | |\ | | | |\ | | + | | \| | | | \| | | | \| | | | \| | + | | X | | | X | | | X | | | X | + | | |\ | | | |\ | | | |\ | | | |\ | + | | | \| | | | \| | | | \| | | | \| + | | | @ | | | @ | | | @ | | | @ + | | | |\ | | | | | | | |\ | | | | + | | | | \| | | | | | | | \| | | | + | | | | X | | | | | | | X | | | + | | | | |\ | | | | | | | |\ | | | + | | | | | \| | | | | | | | \| | | + | | | | | X | | | | | | | X | | + | | | | | |\ | | | | | | | |\ | | + | | | | | | \| | | | | | | | \| | + | | | | | | X | | | | | | | X | + | | | | | | |\ | | | | | | | |\ | + | | | | | | | \| | | | | | | | \| + | | | | | | | @ | | | | | | | @ + | | | | | | | |\ | | | | | | | | + | | | | | | | | \| | | | | | | | + | | | | | | | | X | | | | | | | + | | | | | | | | |\ | | | | | | | + | | | | | | | | | \| | | | | | | + | | | | | | | | | X | | | | | | + | | | | | | | | | |\ | | | | | | + | | | | | | | | | | \| | | | | | + | | | | | | | | | | X | | | | | + | | | | | | | | | | |\ | | | | | + | | | | | | | | | | | \| | | | | + | | | | | | | | | | | X | | | | + | | | | | | | | | | | |\ | | | | + | | | | | | | | | | | | \| | | | + | | | | | | | | | | | | X | | | + | | | | | | | | | | | | |\ | | | + | | | | | | | | | | | | | \| | | + | | | | | | | | | | | | | X | | + | | | | | | | | | | | | | |\ | | + | | | | | | | | | | | | | | \| | + | | | | | | | | | | | | | | X | + | | | | | | | | | | | | | | |\ | + | | | | | | | | | | | | | | | \| + | | | | | | | | | | | | | | | @ + | | | | | | | | | | | | | | | | +"[1..], // trim newline at start + ); + } + + #[test] + fn test_reduce_diagram_9() { + let item_count = 9; + test_diagram( + reduce_ops(item_count), + item_count, + &r" + | | | | | | | | | + | ● | ● | ● | ● | + | |\ | |\ | |\ | |\ | + | | \| | \| | \| | \| + | | @ | @ | @ | @ + | | |\ | | | |\ | | + | | | \| | | | \| | + | | | X | | | X | + | | | |\ | | | |\ | + | | | | \| | | | \| + | | | | @ | | | @ + | | | | |\ | | | | + | | | | | \| | | | + | | | | | X | | | + | | | | | |\ | | | + | | | | | | \| | | + | | | | | | X | | + | | | | | | |\ | | + | | | | | | | \| | + | | | | | | | X | + | | | | | | | |\ | + | | | | | | | | \| + ● | | | | | | | @ + |\ | | | | | | | | + | \| | | | | | | | + | X | | | | | | | + | |\ | | | | | | | + | | \| | | | | | | + | | X | | | | | | + | | |\ | | | | | | + | | | \| | | | | | + | | | X | | | | | + | | | |\ | | | | | + | | | | \| | | | | + | | | | X | | | | + | | | | |\ | | | | + | | | | | \| | | | + | | | | | X | | | + | | | | | |\ | | | + | | | | | | \| | | + | | | | | | X | | + | | | | | | |\ | | + | | | | | | | \| | + | | | | | | | X | + | | | | | | | |\ | + | | | | | | | | \| + | | | | | | | | @ + | | | | | | | | | +"[1..], // trim newline at start + ); + } +} diff --git a/crates/fayalite/src/util/test_hasher.rs b/crates/fayalite/src/util/test_hasher.rs new file mode 100644 index 0000000..20df5b7 --- /dev/null +++ b/crates/fayalite/src/util/test_hasher.rs @@ -0,0 +1,240 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// See Notices.txt for copyright information +#![cfg(feature = "unstable-test-hasher")] + +use std::{ + fmt::Write as _, + hash::{BuildHasher, Hash, Hasher}, + io::Write as _, + marker::PhantomData, + sync::LazyLock, +}; + +type BoxDynHasher = Box; +type BoxDynBuildHasher = Box; +type BoxDynMakeBuildHasher = Box BoxDynBuildHasher + Send + Sync>; + +trait TryGetDynBuildHasher: Copy { + type Type; + fn try_get_make_build_hasher(self) -> Option; +} + +impl TryGetDynBuildHasher for PhantomData { + type Type = T; + fn try_get_make_build_hasher(self) -> Option { + None + } +} + +impl + Send + Sync + 'static + Clone> + TryGetDynBuildHasher for &'_ PhantomData +{ + type Type = T; + fn try_get_make_build_hasher(self) -> Option { + Some(Box::new(|| Box::>::default())) + } +} + +#[derive(Default, Clone)] +struct DynBuildHasher(T); + +trait DynBuildHasherTrait: BuildHasher { + fn clone_dyn_build_hasher(&self) -> BoxDynBuildHasher; +} + +impl> BuildHasher for DynBuildHasher { + type Hasher = BoxDynHasher; + + fn build_hasher(&self) -> Self::Hasher { + Box::new(self.0.build_hasher()) + } + + fn hash_one(&self, x: T) -> u64 { + self.0.hash_one(x) + } +} + +impl DynBuildHasherTrait for DynBuildHasher +where + Self: Clone + BuildHasher + Send + Sync + 'static, +{ + fn clone_dyn_build_hasher(&self) -> BoxDynBuildHasher { + Box::new(self.clone()) + } +} + +pub struct DefaultBuildHasher(BoxDynBuildHasher); + +impl Clone for DefaultBuildHasher { + fn clone(&self) -> Self { + DefaultBuildHasher(self.0.clone_dyn_build_hasher()) + } +} + +const ENV_VAR_NAME: &'static str = "FAYALITE_TEST_HASHER"; + +struct EnvVarValue { + key: &'static str, + try_get_make_build_hasher: fn() -> Option, + description: &'static str, +} + +macro_rules! env_var_value { + ( + key: $key:literal, + build_hasher: $build_hasher:ty, + description: $description:literal, + ) => { + EnvVarValue { + key: $key, + try_get_make_build_hasher: || { + // use rust method resolution to detect if $build_hasher is usable + // (e.g. hashbrown's hasher won't be usable without the right feature enabled) + (&PhantomData::>).try_get_make_build_hasher() + }, + description: $description, + } + }; +} + +#[derive(Default)] +struct AlwaysZeroHasher; + +impl Hasher for AlwaysZeroHasher { + fn write(&mut self, _bytes: &[u8]) {} + fn finish(&self) -> u64 { + 0 + } +} + +const ENV_VAR_VALUES: &'static [EnvVarValue] = &[ + env_var_value! { + key: "std", + build_hasher: std::hash::RandomState, + description: "use std::hash::RandomState", + }, + env_var_value! { + key: "hashbrown", + build_hasher: hashbrown::DefaultHashBuilder, + description: "use hashbrown's DefaultHashBuilder", + }, + env_var_value! { + key: "always_zero", + build_hasher: std::hash::BuildHasherDefault, + description: "use a hasher that always returns 0 for all hashes,\n \ + this is useful for checking that PartialEq impls are correct", + }, +]; + +fn report_bad_env_var(msg: impl std::fmt::Display) -> ! { + let mut msg = format!("{ENV_VAR_NAME}: {msg}\n"); + for &EnvVarValue { + key, + try_get_make_build_hasher, + description, + } in ENV_VAR_VALUES + { + let availability = match try_get_make_build_hasher() { + Some(_) => "available", + None => "unavailable", + }; + writeln!(msg, "{key}: ({availability})\n {description}").expect("can't fail"); + } + std::io::stderr() + .write_all(msg.as_bytes()) + .expect("should be able to write to stderr"); + std::process::abort(); +} + +impl Default for DefaultBuildHasher { + fn default() -> Self { + static DEFAULT_FN: LazyLock = LazyLock::new(|| { + let var = std::env::var_os(ENV_VAR_NAME); + let var = var.as_deref().unwrap_or("std".as_ref()); + for &EnvVarValue { + key, + try_get_make_build_hasher, + description: _, + } in ENV_VAR_VALUES + { + if var.as_encoded_bytes().eq_ignore_ascii_case(key.as_bytes()) { + return try_get_make_build_hasher().unwrap_or_else(|| { + report_bad_env_var(format_args!( + "unavailable hasher: {key} (is the appropriate feature enabled?)" + )); + }); + } + } + report_bad_env_var(format_args!("unrecognized hasher: {var:?}")); + }); + Self(DEFAULT_FN()) + } +} + +pub struct DefaultHasher(BoxDynHasher); + +impl BuildHasher for DefaultBuildHasher { + type Hasher = DefaultHasher; + + fn build_hasher(&self) -> Self::Hasher { + DefaultHasher(self.0.build_hasher()) + } +} + +impl Hasher for DefaultHasher { + fn finish(&self) -> u64 { + self.0.finish() + } + + fn write(&mut self, bytes: &[u8]) { + self.0.write(bytes) + } + + fn write_u8(&mut self, i: u8) { + self.0.write_u8(i) + } + + fn write_u16(&mut self, i: u16) { + self.0.write_u16(i) + } + + fn write_u32(&mut self, i: u32) { + self.0.write_u32(i) + } + + fn write_u64(&mut self, i: u64) { + self.0.write_u64(i) + } + + fn write_u128(&mut self, i: u128) { + self.0.write_u128(i) + } + + fn write_usize(&mut self, i: usize) { + self.0.write_usize(i) + } + + fn write_i8(&mut self, i: i8) { + self.0.write_i8(i) + } + + fn write_i16(&mut self, i: i16) { + self.0.write_i16(i) + } + + fn write_i32(&mut self, i: i32) { + self.0.write_i32(i) + } + + fn write_i64(&mut self, i: i64) { + self.0.write_i64(i) + } + + fn write_i128(&mut self, i: i128) { + self.0.write_i128(i) + } + + fn write_isize(&mut self, i: isize) { + self.0.write_isize(i) + } +} diff --git a/crates/fayalite/tests/hdl_types.rs b/crates/fayalite/tests/hdl_types.rs index b191016..8802fd4 100644 --- a/crates/fayalite/tests/hdl_types.rs +++ b/crates/fayalite/tests/hdl_types.rs @@ -4,11 +4,17 @@ use fayalite::{ bundle::BundleType, enum_::EnumType, int::{BoolOrIntType, IntType}, + phantom_const::PhantomConst, prelude::*, ty::StaticType, }; use std::marker::PhantomData; +#[hdl(outline_generated)] +pub struct MyConstSize { + pub size: PhantomConst>, +} + #[hdl(outline_generated)] pub struct S { pub a: T, diff --git a/crates/fayalite/tests/module.rs b/crates/fayalite/tests/module.rs index 4e56df4..c2dc24e 100644 --- a/crates/fayalite/tests/module.rs +++ b/crates/fayalite/tests/module.rs @@ -1,8 +1,13 @@ // SPDX-License-Identifier: LGPL-3.0-or-later // See Notices.txt for copyright information use fayalite::{ - assert_export_firrtl, firrtl::ExportOptions, intern::Intern, - module::transform::simplify_enums::SimplifyEnumsKind, prelude::*, reset::ResetType, + assert_export_firrtl, + firrtl::ExportOptions, + int::{UIntInRange, UIntInRangeInclusive}, + intern::Intern, + module::transform::simplify_enums::SimplifyEnumsKind, + prelude::*, + reset::ResetType, ty::StaticType, }; use serde_json::json; @@ -191,10 +196,14 @@ circuit check_array_repeat: }; } +pub trait UnknownTrait {} + +impl UnknownTrait for T {} + #[hdl_module(outline_generated)] pub fn check_skipped_generics(v: U) where - T: StaticType, + T: StaticType + UnknownTrait, ConstUsize: KnownSize, U: std::fmt::Display, { @@ -376,18 +385,18 @@ circuit check_written_inside_both_if_else: }; } -#[hdl(outline_generated)] +#[hdl(outline_generated, cmp_eq)] pub struct TestStruct { pub a: T, pub b: UInt<8>, } -#[hdl(outline_generated)] +#[hdl(outline_generated, cmp_eq)] pub struct TestStruct2 { pub v: UInt<8>, } -#[hdl(outline_generated)] +#[hdl(outline_generated, cmp_eq)] pub struct TestStruct3 {} #[hdl_module(outline_generated)] @@ -4345,3 +4354,280 @@ circuit check_cfgs: ", }; } + +#[hdl_module(outline_generated)] +pub fn check_let_patterns() { + #[hdl] + let tuple_in: (UInt<1>, SInt<1>, Bool) = m.input(); + #[hdl] + let (tuple_0, tuple_1, tuple_2) = tuple_in; + #[hdl] + let tuple_0_out: UInt<1> = m.output(); + connect(tuple_0_out, tuple_0); + #[hdl] + let tuple_1_out: SInt<1> = m.output(); + connect(tuple_1_out, tuple_1); + #[hdl] + let tuple_2_out: Bool = m.output(); + connect(tuple_2_out, tuple_2); + + #[hdl] + let test_struct_in: TestStruct> = m.input(); + #[hdl] + let TestStruct::<_> { a, b } = test_struct_in; + #[hdl] + let test_struct_a_out: SInt<8> = m.output(); + connect(test_struct_a_out, a); + #[hdl] + let test_struct_b_out: UInt<8> = m.output(); + connect(test_struct_b_out, b); + + #[hdl] + let test_struct_2_in: TestStruct2 = m.input(); + #[hdl] + let TestStruct2 { v } = test_struct_2_in; + #[hdl] + let test_struct_2_v_out: UInt<8> = m.output(); + connect(test_struct_2_v_out, v); + + #[hdl] + let test_struct_3_in: TestStruct3 = m.input(); + #[hdl] + let TestStruct3 {} = test_struct_3_in; +} + +#[test] +fn test_let_patterns() { + let _n = SourceLocation::normalize_files_for_tests(); + let m = check_let_patterns(); + dbg!(m); + #[rustfmt::skip] // work around https://github.com/rust-lang/rustfmt/issues/6161 + assert_export_firrtl! { + m => + "/test/check_let_patterns.fir": r"FIRRTL version 3.2.0 +circuit check_let_patterns: + type Ty0 = {`0`: UInt<1>, `1`: SInt<1>, `2`: UInt<1>} + type Ty1 = {a: SInt<8>, b: UInt<8>} + type Ty2 = {v: UInt<8>} + type Ty3 = {} + module check_let_patterns: @[module-XXXXXXXXXX.rs 1:1] + input tuple_in: Ty0 @[module-XXXXXXXXXX.rs 2:1] + output tuple_0_out: UInt<1> @[module-XXXXXXXXXX.rs 4:1] + output tuple_1_out: SInt<1> @[module-XXXXXXXXXX.rs 6:1] + output tuple_2_out: UInt<1> @[module-XXXXXXXXXX.rs 8:1] + input test_struct_in: Ty1 @[module-XXXXXXXXXX.rs 10:1] + output test_struct_a_out: SInt<8> @[module-XXXXXXXXXX.rs 12:1] + output test_struct_b_out: UInt<8> @[module-XXXXXXXXXX.rs 14:1] + input test_struct_2_in: Ty2 @[module-XXXXXXXXXX.rs 16:1] + output test_struct_2_v_out: UInt<8> @[module-XXXXXXXXXX.rs 18:1] + input test_struct_3_in: Ty3 @[module-XXXXXXXXXX.rs 20:1] + connect tuple_0_out, tuple_in.`0` @[module-XXXXXXXXXX.rs 5:1] + connect tuple_1_out, tuple_in.`1` @[module-XXXXXXXXXX.rs 7:1] + connect tuple_2_out, tuple_in.`2` @[module-XXXXXXXXXX.rs 9:1] + connect test_struct_a_out, test_struct_in.a @[module-XXXXXXXXXX.rs 13:1] + connect test_struct_b_out, test_struct_in.b @[module-XXXXXXXXXX.rs 15:1] + connect test_struct_2_v_out, test_struct_2_in.v @[module-XXXXXXXXXX.rs 19:1] +", + }; +} + +#[hdl_module(outline_generated)] +pub fn check_struct_cmp_eq() { + #[hdl] + let tuple_lhs: (UInt<1>, SInt<1>, Bool) = m.input(); + #[hdl] + let tuple_rhs: (UInt<1>, SInt<1>, Bool) = m.input(); + #[hdl] + let tuple_cmp_eq: Bool = m.output(); + connect(tuple_cmp_eq, tuple_lhs.cmp_eq(tuple_rhs)); + #[hdl] + let tuple_cmp_ne: Bool = m.output(); + connect(tuple_cmp_ne, tuple_lhs.cmp_ne(tuple_rhs)); + + #[hdl] + let test_struct_lhs: TestStruct> = m.input(); + #[hdl] + let test_struct_rhs: TestStruct> = m.input(); + #[hdl] + let test_struct_cmp_eq: Bool = m.output(); + connect(test_struct_cmp_eq, test_struct_lhs.cmp_eq(test_struct_rhs)); + #[hdl] + let test_struct_cmp_ne: Bool = m.output(); + connect(test_struct_cmp_ne, test_struct_lhs.cmp_ne(test_struct_rhs)); + + #[hdl] + let test_struct_2_lhs: TestStruct2 = m.input(); + #[hdl] + let test_struct_2_rhs: TestStruct2 = m.input(); + #[hdl] + let test_struct_2_cmp_eq: Bool = m.output(); + connect( + test_struct_2_cmp_eq, + test_struct_2_lhs.cmp_eq(test_struct_2_rhs), + ); + #[hdl] + let test_struct_2_cmp_ne: Bool = m.output(); + connect( + test_struct_2_cmp_ne, + test_struct_2_lhs.cmp_ne(test_struct_2_rhs), + ); + + #[hdl] + let test_struct_3_lhs: TestStruct3 = m.input(); + #[hdl] + let test_struct_3_rhs: TestStruct3 = m.input(); + #[hdl] + let test_struct_3_cmp_eq: Bool = m.output(); + connect( + test_struct_3_cmp_eq, + test_struct_3_lhs.cmp_eq(test_struct_3_rhs), + ); + #[hdl] + let test_struct_3_cmp_ne: Bool = m.output(); + connect( + test_struct_3_cmp_ne, + test_struct_3_lhs.cmp_ne(test_struct_3_rhs), + ); +} + +#[test] +fn test_struct_cmp_eq() { + let _n = SourceLocation::normalize_files_for_tests(); + let m = check_struct_cmp_eq(); + dbg!(m); + #[rustfmt::skip] // work around https://github.com/rust-lang/rustfmt/issues/6161 + assert_export_firrtl! { + m => + "/test/check_struct_cmp_eq.fir": r"FIRRTL version 3.2.0 +circuit check_struct_cmp_eq: + type Ty0 = {`0`: UInt<1>, `1`: SInt<1>, `2`: UInt<1>} + type Ty1 = {a: SInt<8>, b: UInt<8>} + type Ty2 = {v: UInt<8>} + type Ty3 = {} + module check_struct_cmp_eq: @[module-XXXXXXXXXX.rs 1:1] + input tuple_lhs: Ty0 @[module-XXXXXXXXXX.rs 2:1] + input tuple_rhs: Ty0 @[module-XXXXXXXXXX.rs 3:1] + output tuple_cmp_eq: UInt<1> @[module-XXXXXXXXXX.rs 4:1] + output tuple_cmp_ne: UInt<1> @[module-XXXXXXXXXX.rs 6:1] + input test_struct_lhs: Ty1 @[module-XXXXXXXXXX.rs 8:1] + input test_struct_rhs: Ty1 @[module-XXXXXXXXXX.rs 9:1] + output test_struct_cmp_eq: UInt<1> @[module-XXXXXXXXXX.rs 10:1] + output test_struct_cmp_ne: UInt<1> @[module-XXXXXXXXXX.rs 12:1] + input test_struct_2_lhs: Ty2 @[module-XXXXXXXXXX.rs 14:1] + input test_struct_2_rhs: Ty2 @[module-XXXXXXXXXX.rs 15:1] + output test_struct_2_cmp_eq: UInt<1> @[module-XXXXXXXXXX.rs 16:1] + output test_struct_2_cmp_ne: UInt<1> @[module-XXXXXXXXXX.rs 18:1] + input test_struct_3_lhs: Ty3 @[module-XXXXXXXXXX.rs 20:1] + input test_struct_3_rhs: Ty3 @[module-XXXXXXXXXX.rs 21:1] + output test_struct_3_cmp_eq: UInt<1> @[module-XXXXXXXXXX.rs 22:1] + output test_struct_3_cmp_ne: UInt<1> @[module-XXXXXXXXXX.rs 24:1] + wire _array_literal_expr: UInt<1>[3] + connect _array_literal_expr[0], eq(tuple_lhs.`0`, tuple_rhs.`0`) + connect _array_literal_expr[1], eq(tuple_lhs.`1`, tuple_rhs.`1`) + connect _array_literal_expr[2], eq(tuple_lhs.`2`, tuple_rhs.`2`) + wire _cast_array_to_bits_expr: UInt<1>[3] + connect _cast_array_to_bits_expr[0], _array_literal_expr[0] + connect _cast_array_to_bits_expr[1], _array_literal_expr[1] + connect _cast_array_to_bits_expr[2], _array_literal_expr[2] + wire _cast_to_bits_expr: UInt<3> + connect _cast_to_bits_expr, cat(_cast_array_to_bits_expr[2], cat(_cast_array_to_bits_expr[1], _cast_array_to_bits_expr[0])) + connect tuple_cmp_eq, andr(_cast_to_bits_expr) @[module-XXXXXXXXXX.rs 5:1] + wire _array_literal_expr_1: UInt<1>[3] + connect _array_literal_expr_1[0], neq(tuple_lhs.`0`, tuple_rhs.`0`) + connect _array_literal_expr_1[1], neq(tuple_lhs.`1`, tuple_rhs.`1`) + connect _array_literal_expr_1[2], neq(tuple_lhs.`2`, tuple_rhs.`2`) + wire _cast_array_to_bits_expr_1: UInt<1>[3] + connect _cast_array_to_bits_expr_1[0], _array_literal_expr_1[0] + connect _cast_array_to_bits_expr_1[1], _array_literal_expr_1[1] + connect _cast_array_to_bits_expr_1[2], _array_literal_expr_1[2] + wire _cast_to_bits_expr_1: UInt<3> + connect _cast_to_bits_expr_1, cat(_cast_array_to_bits_expr_1[2], cat(_cast_array_to_bits_expr_1[1], _cast_array_to_bits_expr_1[0])) + connect tuple_cmp_ne, orr(_cast_to_bits_expr_1) @[module-XXXXXXXXXX.rs 7:1] + connect test_struct_cmp_eq, and(eq(test_struct_lhs.a, test_struct_rhs.a), eq(test_struct_lhs.b, test_struct_rhs.b)) @[module-XXXXXXXXXX.rs 11:1] + connect test_struct_cmp_ne, or(neq(test_struct_lhs.a, test_struct_rhs.a), neq(test_struct_lhs.b, test_struct_rhs.b)) @[module-XXXXXXXXXX.rs 13:1] + connect test_struct_2_cmp_eq, eq(test_struct_2_lhs.v, test_struct_2_rhs.v) @[module-XXXXXXXXXX.rs 17:1] + connect test_struct_2_cmp_ne, neq(test_struct_2_lhs.v, test_struct_2_rhs.v) @[module-XXXXXXXXXX.rs 19:1] + connect test_struct_3_cmp_eq, UInt<1>(0h1) @[module-XXXXXXXXXX.rs 23:1] + connect test_struct_3_cmp_ne, UInt<1>(0h0) @[module-XXXXXXXXXX.rs 25:1] +", + }; +} + +#[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] +", + }; +} diff --git a/crates/fayalite/tests/sim.rs b/crates/fayalite/tests/sim.rs index a249235..b9c6a80 100644 --- a/crates/fayalite/tests/sim.rs +++ b/crates/fayalite/tests/sim.rs @@ -2,14 +2,14 @@ // See Notices.txt for copyright information use fayalite::{ - int::UIntValue, + memory::{ReadStruct, ReadWriteStruct, WriteStruct}, + module::{instance_with_loc, reg_builder_with_loc}, prelude::*, reset::ResetType, - sim::{time::SimDuration, vcd::VcdWriterDecls, Simulation, ToSimValue}, - ty::StaticType, + sim::vcd::VcdWriterDecls, util::RcWriter, }; -use std::num::NonZeroUsize; +use std::{collections::BTreeMap, num::NonZeroUsize, rc::Rc}; #[hdl_module(outline_generated)] pub fn connect_const() { @@ -316,8 +316,13 @@ pub fn enums() { let which_out: UInt<2> = m.output(); #[hdl] let data_out: UInt<4> = m.output(); + let b_out_ty = HdlOption[(UInt[1], Bool)]; #[hdl] - let b_out: HdlOption<(UInt<1>, Bool)> = m.output(); + let b_out: HdlOption<(UInt, Bool)> = m.output(HdlOption[(UInt[1], Bool)]); + #[hdl] + let b2_out: HdlOption<(UInt<1>, Bool)> = m.output(); + + connect_any(b2_out, b_out); #[hdl] struct MyStruct { @@ -357,7 +362,7 @@ pub fn enums() { } } - connect(b_out, HdlNone()); + connect(b_out, b_out_ty.HdlNone()); #[hdl] match the_reg { @@ -368,7 +373,7 @@ pub fn enums() { MyEnum::B(v) => { connect(which_out, 1_hdl_u2); connect_any(data_out, v.0 | (v.1.cast_to_static::>() << 1)); - connect(b_out, HdlSome(v)); + connect_any(b_out, HdlSome(v)); } MyEnum::C(v) => { connect(which_out, 2_hdl_u2); @@ -384,132 +389,136 @@ fn test_enums() { let mut sim = Simulation::new(enums()); let mut writer = RcWriter::default(); sim.add_trace_writer(VcdWriterDecls::new(writer.clone())); - sim.write_clock(sim.io().cd.clk, false); - sim.write_reset(sim.io().cd.rst, true); - sim.write_bool(sim.io().en, false); - sim.write_bool_or_int(sim.io().which_in, 0_hdl_u2); - sim.write_bool_or_int(sim.io().data_in, 0_hdl_u4); + sim.write(sim.io().cd.clk, false); + sim.write(sim.io().cd.rst, true); + sim.write(sim.io().en, false); + sim.write(sim.io().which_in, 0_hdl_u2); + sim.write(sim.io().data_in, 0_hdl_u4); sim.advance_time(SimDuration::from_micros(1)); - sim.write_clock(sim.io().cd.clk, true); + sim.write(sim.io().cd.clk, true); sim.advance_time(SimDuration::from_nanos(100)); - sim.write_reset(sim.io().cd.rst, false); + sim.write(sim.io().cd.rst, false); sim.advance_time(SimDuration::from_nanos(900)); - type BOutTy = HdlOption<(UInt<1>, Bool)>; - #[derive(Debug)] - struct IO { - en: bool, - which_in: u8, - data_in: u8, - which_out: u8, - data_out: u8, - b_out: Expr, - } - impl PartialEq for IO { - fn eq(&self, other: &Self) -> bool { - let Self { - en, - which_in, - data_in, - which_out, - data_out, - b_out, - } = *self; - en == other.en - && which_in == other.which_in - && data_in == other.data_in - && which_out == other.which_out - && data_out == other.data_out - && b_out.to_sim_value(BOutTy::TYPE) == other.b_out.to_sim_value(BOutTy::TYPE) - } + #[hdl(cmp_eq)] + struct IO { + en: Bool, + which_in: UInt<2>, + data_in: UInt<4>, + which_out: UInt<2>, + data_out: UInt<4>, + b_out: HdlOption<(UIntType, Bool)>, + b2_out: HdlOption<(UInt<1>, Bool)>, } + let io_ty = IO[1]; let io_cycles = [ - IO { + #[hdl(sim)] + IO::<_> { en: false, - which_in: 0, - data_in: 0, - which_out: 0, - data_out: 0, - b_out: HdlNone(), + which_in: 0_hdl_u2, + data_in: 0_hdl_u4, + which_out: 0_hdl_u2, + data_out: 0_hdl_u4, + b_out: #[hdl(sim)] + (io_ty.b_out).HdlNone(), + b2_out: #[hdl(sim)] + HdlNone(), }, - IO { + #[hdl(sim)] + IO::<_> { en: true, - which_in: 1, - data_in: 0, - which_out: 0, - data_out: 0, - b_out: HdlNone(), + which_in: 1_hdl_u2, + data_in: 0_hdl_u4, + which_out: 0_hdl_u2, + data_out: 0_hdl_u4, + b_out: #[hdl(sim)] + (io_ty.b_out).HdlNone(), + b2_out: #[hdl(sim)] + HdlNone(), }, - IO { + #[hdl(sim)] + IO::<_> { en: false, - which_in: 0, - data_in: 0, - which_out: 1, - data_out: 0, - b_out: HdlSome((0_hdl_u1, false)), + which_in: 0_hdl_u2, + data_in: 0_hdl_u4, + which_out: 1_hdl_u2, + data_out: 0_hdl_u4, + b_out: #[hdl(sim)] + (io_ty.b_out).HdlSome((0u8.cast_to(UInt[1]), false)), + b2_out: #[hdl(sim)] + HdlSome((0_hdl_u1, false)), }, - IO { + #[hdl(sim)] + IO::<_> { en: true, - which_in: 1, - data_in: 0xF, - which_out: 1, - data_out: 0, - b_out: HdlSome((0_hdl_u1, false)), + which_in: 1_hdl_u2, + data_in: 0xF_hdl_u4, + which_out: 1_hdl_u2, + data_out: 0_hdl_u4, + b_out: #[hdl(sim)] + (io_ty.b_out).HdlSome((0u8.cast_to(UInt[1]), false)), + b2_out: #[hdl(sim)] + HdlSome((0_hdl_u1, false)), }, - IO { + #[hdl(sim)] + IO::<_> { en: true, - which_in: 1, - data_in: 0xF, - which_out: 1, - data_out: 0x3, - b_out: HdlSome((1_hdl_u1, true)), + which_in: 1_hdl_u2, + data_in: 0xF_hdl_u4, + which_out: 1_hdl_u2, + data_out: 0x3_hdl_u4, + b_out: #[hdl(sim)] + (io_ty.b_out).HdlSome((1u8.cast_to(UInt[1]), true)), + b2_out: #[hdl(sim)] + HdlSome((1_hdl_u1, true)), }, - IO { + #[hdl(sim)] + IO::<_> { en: true, - which_in: 2, - data_in: 0xF, - which_out: 1, - data_out: 0x3, - b_out: HdlSome((1_hdl_u1, true)), + which_in: 2_hdl_u2, + data_in: 0xF_hdl_u4, + which_out: 1_hdl_u2, + data_out: 0x3_hdl_u4, + b_out: #[hdl(sim)] + (io_ty.b_out).HdlSome((1u8.cast_to(UInt[1]), true)), + b2_out: #[hdl(sim)] + HdlSome((1_hdl_u1, true)), }, - IO { + #[hdl(sim)] + IO::<_> { en: true, - which_in: 2, - data_in: 0xF, - which_out: 2, - data_out: 0xF, - b_out: HdlNone(), + which_in: 2_hdl_u2, + data_in: 0xF_hdl_u4, + which_out: 2_hdl_u2, + data_out: 0xF_hdl_u4, + b_out: #[hdl(sim)] + (io_ty.b_out).HdlNone(), + b2_out: #[hdl(sim)] + HdlNone(), }, ]; - for ( - cycle, - expected @ IO { + for (cycle, expected) in io_cycles.into_iter().enumerate() { + #[hdl(sim)] + let IO::<_> { en, which_in, data_in, which_out: _, data_out: _, b_out: _, - }, - ) in io_cycles.into_iter().enumerate() - { - sim.write_bool(sim.io().en, en); - sim.write_bool_or_int(sim.io().which_in, which_in.cast_to_static()); - sim.write_bool_or_int(sim.io().data_in, data_in.cast_to_static()); - let io = IO { + b2_out: _, + } = expected; + sim.write(sim.io().en, &en); + sim.write(sim.io().which_in, &which_in); + sim.write(sim.io().data_in, &data_in); + let io = #[hdl(sim)] + IO::<_> { en, which_in, data_in, - which_out: sim - .read_bool_or_int(sim.io().which_out) - .to_bigint() - .try_into() - .expect("known to be in range"), - data_out: sim - .read_bool_or_int(sim.io().data_out) - .to_bigint() - .try_into() - .expect("known to be in range"), - b_out: sim.read(sim.io().b_out).to_expr(), + which_out: sim.read(sim.io().which_out), + data_out: sim.read(sim.io().data_out), + b_out: sim.read(sim.io().b_out), + b2_out: sim.read(sim.io().b2_out), }; assert_eq!( expected, @@ -517,6 +526,12 @@ fn test_enums() { "vcd:\n{}\ncycle: {cycle}", String::from_utf8(writer.take()).unwrap(), ); + // make sure matching on SimValue works + #[hdl(sim)] + match io.b_out { + HdlNone => println!("io.b_out is HdlNone"), + HdlSome(v) => println!("io.b_out is HdlSome(({:?}, {:?}))", *v.0, *v.1), + } sim.write_clock(sim.io().cd.clk, false); sim.advance_time(SimDuration::from_micros(1)); sim.write_clock(sim.io().cd.clk, true); @@ -538,9 +553,9 @@ fn test_enums() { #[hdl_module(outline_generated)] pub fn memories() { #[hdl] - let r: fayalite::memory::ReadStruct<(UInt<8>, SInt<8>), ConstUsize<4>> = m.input(); + let r: ReadStruct<(UInt<8>, SInt<8>), ConstUsize<4>> = m.input(); #[hdl] - let w: fayalite::memory::WriteStruct<(UInt<8>, SInt<8>), ConstUsize<4>> = m.input(); + let w: WriteStruct<(UInt<8>, SInt<8>), ConstUsize<4>> = m.input(); #[hdl] let mut mem = memory_with_init([(0x01u8, 0x23i8); 16]); mem.read_latency(0); @@ -559,120 +574,131 @@ fn test_memories() { sim.add_trace_writer(VcdWriterDecls::new(writer.clone())); sim.write_clock(sim.io().r.clk, false); sim.write_clock(sim.io().w.clk, false); - #[derive(Debug, PartialEq, Eq)] + #[hdl(cmp_eq)] struct IO { - r_addr: u8, - r_en: bool, - r_data: (u8, i8), - w_addr: u8, - w_en: bool, - w_data: (u8, i8), - w_mask: (bool, bool), + r_addr: UInt<4>, + r_en: Bool, + r_data: (UInt<8>, SInt<8>), + w_addr: UInt<4>, + w_en: Bool, + w_data: (UInt<8>, SInt<8>), + w_mask: (Bool, Bool), } let io_cycles = [ + #[hdl(sim)] IO { - r_addr: 0, + r_addr: 0_hdl_u4, r_en: false, - r_data: (0, 0), - w_addr: 0, + r_data: (0u8, 0i8), + w_addr: 0_hdl_u4, w_en: false, - w_data: (0, 0), + w_data: (0u8, 0i8), w_mask: (false, false), }, + #[hdl(sim)] IO { - r_addr: 0, + r_addr: 0_hdl_u4, r_en: true, - r_data: (0x1, 0x23), - w_addr: 0, + r_data: (0x1u8, 0x23i8), + w_addr: 0_hdl_u4, w_en: true, - w_data: (0x10, 0x20), + w_data: (0x10u8, 0x20i8), w_mask: (true, true), }, + #[hdl(sim)] IO { - r_addr: 0, + r_addr: 0_hdl_u4, r_en: true, - r_data: (0x10, 0x20), - w_addr: 0, + r_data: (0x10u8, 0x20i8), + w_addr: 0_hdl_u4, w_en: true, - w_data: (0x30, 0x40), + w_data: (0x30u8, 0x40i8), w_mask: (false, true), }, + #[hdl(sim)] IO { - r_addr: 0, + r_addr: 0_hdl_u4, r_en: true, - r_data: (0x10, 0x40), - w_addr: 0, + r_data: (0x10u8, 0x40i8), + w_addr: 0_hdl_u4, w_en: true, - w_data: (0x50, 0x60), + w_data: (0x50u8, 0x60i8), w_mask: (true, false), }, + #[hdl(sim)] IO { - r_addr: 0, + r_addr: 0_hdl_u4, r_en: true, - r_data: (0x50, 0x40), - w_addr: 0, + r_data: (0x50u8, 0x40i8), + w_addr: 0_hdl_u4, w_en: true, - w_data: (0x70, -0x80), + w_data: (0x70u8, -0x80i8), w_mask: (false, false), }, + #[hdl(sim)] IO { - r_addr: 0, + r_addr: 0_hdl_u4, r_en: true, - r_data: (0x50, 0x40), - w_addr: 0, + r_data: (0x50u8, 0x40i8), + w_addr: 0_hdl_u4, w_en: false, - w_data: (0x90, 0xA0u8 as i8), + w_data: (0x90u8, 0xA0u8 as i8), w_mask: (false, false), }, + #[hdl(sim)] IO { - r_addr: 0, + r_addr: 0_hdl_u4, r_en: true, - r_data: (0x50, 0x40), - w_addr: 1, + r_data: (0x50u8, 0x40i8), + w_addr: 1_hdl_u4, w_en: true, - w_data: (0x90, 0xA0u8 as i8), + w_data: (0x90u8, 0xA0u8 as i8), w_mask: (true, true), }, + #[hdl(sim)] IO { - r_addr: 0, + r_addr: 0_hdl_u4, r_en: true, - r_data: (0x50, 0x40), - w_addr: 2, + r_data: (0x50u8, 0x40i8), + w_addr: 2_hdl_u4, w_en: true, - w_data: (0xB0, 0xC0u8 as i8), + w_data: (0xB0u8, 0xC0u8 as i8), w_mask: (true, true), }, + #[hdl(sim)] IO { - r_addr: 0, + r_addr: 0_hdl_u4, r_en: true, - r_data: (0x50, 0x40), - w_addr: 2, + r_data: (0x50u8, 0x40i8), + w_addr: 2_hdl_u4, w_en: false, - w_data: (0xD0, 0xE0u8 as i8), + w_data: (0xD0u8, 0xE0u8 as i8), w_mask: (true, true), }, + #[hdl(sim)] IO { - r_addr: 1, + r_addr: 1_hdl_u4, r_en: true, - r_data: (0x90, 0xA0u8 as i8), - w_addr: 2, + r_data: (0x90u8, 0xA0u8 as i8), + w_addr: 2_hdl_u4, w_en: false, - w_data: (0xD0, 0xE0u8 as i8), + w_data: (0xD0u8, 0xE0u8 as i8), w_mask: (true, true), }, + #[hdl(sim)] IO { - r_addr: 2, + r_addr: 2_hdl_u4, r_en: true, - r_data: (0xB0, 0xC0u8 as i8), - w_addr: 2, + r_data: (0xB0u8, 0xC0u8 as i8), + w_addr: 2_hdl_u4, w_en: false, - w_data: (0xD0, 0xE0u8 as i8), + w_data: (0xD0u8, 0xE0u8 as i8), w_mask: (true, true), }, ]; - for ( - cycle, - expected @ IO { + for (cycle, expected) in io_cycles.into_iter().enumerate() { + #[hdl(sim)] + let IO { r_addr, r_en, r_data: _, @@ -680,30 +706,18 @@ fn test_memories() { w_en, w_data, w_mask, - }, - ) in io_cycles.into_iter().enumerate() - { - sim.write_bool_or_int(sim.io().r.addr, r_addr.cast_to_static()); - sim.write_bool(sim.io().r.en, r_en); - sim.write_bool_or_int(sim.io().w.addr, w_addr.cast_to_static()); - sim.write_bool(sim.io().w.en, w_en); - sim.write_bool_or_int(sim.io().w.data.0, w_data.0); - sim.write_bool_or_int(sim.io().w.data.1, w_data.1); - sim.write_bool(sim.io().w.mask.0, w_mask.0); - sim.write_bool(sim.io().w.mask.1, w_mask.1); - let io = IO { + } = expected; + sim.write(sim.io().r.addr, &r_addr); + sim.write(sim.io().r.en, &r_en); + sim.write(sim.io().w.addr, &w_addr); + sim.write(sim.io().w.en, &w_en); + sim.write(sim.io().w.data, &w_data); + sim.write(sim.io().w.mask, &w_mask); + let io = #[hdl(sim)] + IO { r_addr, r_en, - r_data: ( - sim.read_bool_or_int(sim.io().r.data.0) - .to_bigint() - .try_into() - .expect("known to be in range"), - sim.read_bool_or_int(sim.io().r.data.1) - .to_bigint() - .try_into() - .expect("known to be in range"), - ), + r_data: sim.read(sim.io().r.data), w_addr, w_en, w_data, @@ -716,11 +730,11 @@ fn test_memories() { String::from_utf8(writer.take()).unwrap(), ); sim.advance_time(SimDuration::from_micros(1)); - sim.write_clock(sim.io().r.clk, true); - sim.write_clock(sim.io().w.clk, true); + sim.write(sim.io().r.clk, true); + sim.write(sim.io().w.clk, true); sim.advance_time(SimDuration::from_micros(1)); - sim.write_clock(sim.io().r.clk, false); - sim.write_clock(sim.io().w.clk, false); + sim.write(sim.io().r.clk, false); + sim.write(sim.io().w.clk, false); } sim.flush_traces().unwrap(); let vcd = String::from_utf8(writer.take()).unwrap(); @@ -738,7 +752,7 @@ fn test_memories() { #[hdl_module(outline_generated)] pub fn memories2() { #[hdl] - let rw: fayalite::memory::ReadWriteStruct, ConstUsize<3>> = m.input(); + let rw: ReadWriteStruct, ConstUsize<3>> = m.input(); #[hdl] let mut mem = memory_with_init([HdlSome(true); 5]); mem.read_latency(1); @@ -1011,9 +1025,9 @@ fn test_memories2() { #[hdl_module(outline_generated)] pub fn memories3() { #[hdl] - let r: fayalite::memory::ReadStruct, 8>, ConstUsize<3>> = m.input(); + let r: ReadStruct, 8>, ConstUsize<3>> = m.input(); #[hdl] - let w: fayalite::memory::WriteStruct, 8>, ConstUsize<3>> = m.input(); + let w: WriteStruct, 8>, ConstUsize<3>> = m.input(); #[hdl] let mut mem: MemBuilder, 8>> = memory(); mem.depth(8); @@ -1246,3 +1260,465 @@ fn test_memories3() { panic!(); } } + +#[hdl_module(outline_generated)] +pub fn duplicate_names() { + #[hdl] + let w = wire(); + connect(w, 5u8); + #[hdl] + let w = wire(); + connect(w, 6u8); +} + +#[test] +fn test_duplicate_names() { + let _n = SourceLocation::normalize_files_for_tests(); + let mut sim = Simulation::new(duplicate_names()); + let mut writer = RcWriter::default(); + sim.add_trace_writer(VcdWriterDecls::new(writer.clone())); + sim.advance_time(SimDuration::from_micros(1)); + sim.flush_traces().unwrap(); + let vcd = String::from_utf8(writer.take()).unwrap(); + println!("####### VCD:\n{vcd}\n#######"); + if vcd != include_str!("sim/expected/duplicate_names.vcd") { + panic!(); + } + let sim_debug = format!("{sim:#?}"); + println!("#######\n{sim_debug}\n#######"); + if sim_debug != include_str!("sim/expected/duplicate_names.txt") { + panic!(); + } +} + +#[hdl_module(outline_generated)] +pub fn array_rw() { + #[hdl] + let array_in: Array, 16> = m.input(); + #[hdl] + let array_out: Array, 16> = m.output(); + #[hdl] + let read_index: UInt<8> = m.input(); + #[hdl] + let read_data: UInt<8> = m.output(); + #[hdl] + let write_index: UInt<8> = m.input(); + #[hdl] + let write_data: UInt<8> = m.input(); + #[hdl] + let write_en: Bool = m.input(); + #[hdl] + let array_wire = wire(); + connect(array_wire, array_in); + connect(array_out, array_wire); + #[hdl] + if write_en { + connect(array_wire[write_index], write_data); + } + connect(read_data, array_wire[read_index]); +} + +#[test] +fn test_array_rw() { + let _n = SourceLocation::normalize_files_for_tests(); + let mut sim = Simulation::new(array_rw()); + let mut writer = RcWriter::default(); + sim.add_trace_writer(VcdWriterDecls::new(writer.clone())); + #[derive(Debug, PartialEq)] + struct State { + array_in: [u8; 16], + array_out: [u8; 16], + read_index: u8, + read_data: u8, + write_index: u8, + write_data: u8, + write_en: bool, + } + let mut states = Vec::new(); + let array_in = [ + 0xFFu8, 0x7F, 0x3F, 0x1F, 0x0F, 0x07, 0x03, 0x01, // + 0x00u8, 0x80, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC, 0xFE, + ]; + for i in 0..=16 { + states.push(State { + array_in, + array_out: array_in, + read_index: i, + read_data: array_in.get(i as usize).copied().unwrap_or(0), + write_index: 0, + write_data: 0, + write_en: false, + }); + } + for i in 0..=16u8 { + let mut array_out = array_in; + let write_data = i.wrapping_mul(i); + if let Some(v) = array_out.get_mut(i as usize) { + *v = write_data; + } + states.push(State { + array_in, + array_out, + read_index: 0, + read_data: array_out[0], + write_index: i, + write_data, + write_en: true, + }); + } + for (cycle, expected) in states.into_iter().enumerate() { + let State { + array_in, + array_out: _, + read_index, + read_data: _, + write_index, + write_data, + write_en, + } = expected; + sim.write(sim.io().array_in, array_in); + sim.write(sim.io().read_index, read_index); + sim.write(sim.io().write_index, write_index); + sim.write(sim.io().write_data, write_data); + sim.write(sim.io().write_en, write_en); + sim.advance_time(SimDuration::from_micros(1)); + let array_out = std::array::from_fn(|index| { + sim.read_bool_or_int(sim.io().array_out[index]) + .to_bigint() + .try_into() + .expect("known to be in range") + }); + let read_data = sim + .read_bool_or_int(sim.io().read_data) + .to_bigint() + .try_into() + .expect("known to be in range"); + let state = State { + array_in, + array_out, + read_index, + read_data, + write_index, + write_data, + write_en, + }; + assert_eq!( + state, + expected, + "vcd:\n{}\ncycle: {cycle}", + String::from_utf8(writer.take()).unwrap(), + ); + } + sim.flush_traces().unwrap(); + let vcd = String::from_utf8(writer.take()).unwrap(); + println!("####### VCD:\n{vcd}\n#######"); + if vcd != include_str!("sim/expected/array_rw.vcd") { + panic!(); + } + let sim_debug = format!("{sim:#?}"); + println!("#######\n{sim_debug}\n#######"); + if sim_debug != include_str!("sim/expected/array_rw.txt") { + panic!(); + } +} + +#[hdl_module(outline_generated)] +pub fn conditional_assignment_last() { + #[hdl] + let i: Bool = m.input(); + #[hdl] + let w = wire(); + connect(w, true); + #[hdl] + if i { + connect(w, false); + } +} + +#[test] +fn test_conditional_assignment_last() { + let _n = SourceLocation::normalize_files_for_tests(); + let mut sim = Simulation::new(conditional_assignment_last()); + let mut writer = RcWriter::default(); + sim.add_trace_writer(VcdWriterDecls::new(writer.clone())); + sim.write(sim.io().i, false); + sim.advance_time(SimDuration::from_micros(1)); + sim.write(sim.io().i, true); + sim.advance_time(SimDuration::from_micros(1)); + sim.flush_traces().unwrap(); + let vcd = String::from_utf8(writer.take()).unwrap(); + println!("####### VCD:\n{vcd}\n#######"); + if vcd != include_str!("sim/expected/conditional_assignment_last.vcd") { + panic!(); + } + let sim_debug = format!("{sim:#?}"); + println!("#######\n{sim_debug}\n#######"); + if sim_debug != include_str!("sim/expected/conditional_assignment_last.txt") { + panic!(); + } +} + +#[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 = 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!(); + } +} + +/// use `Rc` to ensure you can use `!Send + !Sync` types +type SimOnlyTestMap = BTreeMap>; + +#[hdl_module(outline_generated, extern)] +fn sim_only_connects_helper() { + #[hdl] + let cd: ClockDomain = m.input(); + #[hdl] + let inp: SimOnly = m.input(); + #[hdl] + let out: SimOnly = m.output(); + m.extern_module_simulation_fn((cd, inp, out), |(cd, inp, out), mut sim| async move { + sim.write(out, SimOnlyValue::default()).await; + loop { + sim.wait_for_clock_edge(cd.clk).await; + let mut map = sim.read(inp).await; + let foo = map.get("foo").cloned().unwrap_or_default(); + map.insert(String::from("bar"), foo); + map.insert(String::from("foo"), Rc::from("baz")); + sim.write(out, map).await; + } + }); +} + +#[hdl_module(outline_generated)] +pub fn sim_only_connects() { + #[hdl] + let cd: ClockDomain = m.input(); + #[hdl] + let inp: SimOnly = m.input(); + #[hdl] + let out1: SimOnly = m.output(); + #[hdl] + let out2: SimOnly = m.output(); + #[hdl] + let out3: SimOnly = m.output(); + #[hdl] + let helper1 = instance(sim_only_connects_helper()); + #[hdl] + let delay1: SimOnly = reg_builder() + .clock_domain(cd) + .reset(SimOnly::::new().uninit()); + #[hdl] + let delay1_empty: Bool = reg_builder().clock_domain(cd).reset(true); + connect(helper1.cd, cd); + connect(helper1.inp, delay1); + connect(out1, delay1); + #[hdl] + if delay1_empty { + connect(helper1.inp, inp); + connect(out1, inp); + } + connect(delay1, inp); + connect(delay1_empty, false); + connect(out2, helper1.out); + #[hdl] + let helper2 = instance(sim_only_connects_helper()); + connect(helper2.cd, cd); + connect(helper2.inp, out2); + connect(out3, helper2.out); +} + +#[test] +fn test_sim_only_connects() { + let _n = SourceLocation::normalize_files_for_tests(); + let mut sim = Simulation::new(sim_only_connects()); + let mut writer = RcWriter::default(); + sim.add_trace_writer(VcdWriterDecls::new(writer.clone())); + sim.write(sim.io().cd.rst, true); + sim.write( + sim.io().inp, + SimOnlyValue::new(BTreeMap::from_iter([( + String::from("extra"), + Rc::from("value"), + )])), + ); + for _ in 0..8 { + sim.write(sim.io().cd.clk, false); + sim.advance_time(SimDuration::from_micros(1)); + sim.write(sim.io().cd.clk, true); + sim.advance_time(SimDuration::from_micros(1)); + sim.write(sim.io().cd.rst, false); + } + sim.flush_traces().unwrap(); + let vcd = String::from_utf8(writer.take()).unwrap(); + println!("####### VCD:\n{vcd}\n#######"); + if vcd != include_str!("sim/expected/sim_only_connects.vcd") { + panic!(); + } + let sim_debug = format!("{sim:#?}"); + println!("#######\n{sim_debug}\n#######"); + if sim_debug != include_str!("sim/expected/sim_only_connects.txt") { + panic!(); + } +} diff --git a/crates/fayalite/tests/sim/expected/array_rw.txt b/crates/fayalite/tests/sim/expected/array_rw.txt new file mode 100644 index 0000000..12e86f3 --- /dev/null +++ b/crates/fayalite/tests/sim/expected/array_rw.txt @@ -0,0 +1,1705 @@ +Simulation { + state: State { + insns: Insns { + state_layout: StateLayout { + ty: TypeLayout { + small_slots: StatePartLayout { + len: 2, + debug_data: [ + SlotDebugData { + name: "", + ty: UInt<8>, + }, + SlotDebugData { + name: "", + ty: UInt<8>, + }, + ], + .. + }, + big_slots: StatePartLayout { + len: 54, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(array_rw: array_rw).array_rw::array_in[0]", + ty: UInt<8>, + }, + SlotDebugData { + name: "InstantiatedModule(array_rw: array_rw).array_rw::array_in[1]", + ty: UInt<8>, + }, + SlotDebugData { + name: "InstantiatedModule(array_rw: array_rw).array_rw::array_in[2]", + ty: UInt<8>, + }, + SlotDebugData { + name: "InstantiatedModule(array_rw: array_rw).array_rw::array_in[3]", + ty: UInt<8>, + }, + SlotDebugData { + name: "InstantiatedModule(array_rw: array_rw).array_rw::array_in[4]", + ty: UInt<8>, + }, + SlotDebugData { + name: "InstantiatedModule(array_rw: array_rw).array_rw::array_in[5]", + ty: UInt<8>, + }, + SlotDebugData { + name: "InstantiatedModule(array_rw: array_rw).array_rw::array_in[6]", + ty: UInt<8>, + }, + SlotDebugData { + name: "InstantiatedModule(array_rw: array_rw).array_rw::array_in[7]", + ty: UInt<8>, + }, + SlotDebugData { + name: "InstantiatedModule(array_rw: array_rw).array_rw::array_in[8]", + ty: UInt<8>, + }, + SlotDebugData { + name: "InstantiatedModule(array_rw: array_rw).array_rw::array_in[9]", + ty: UInt<8>, + }, + SlotDebugData { + name: "InstantiatedModule(array_rw: array_rw).array_rw::array_in[10]", + ty: UInt<8>, + }, + SlotDebugData { + name: "InstantiatedModule(array_rw: array_rw).array_rw::array_in[11]", + ty: UInt<8>, + }, + SlotDebugData { + name: "InstantiatedModule(array_rw: array_rw).array_rw::array_in[12]", + ty: UInt<8>, + }, + SlotDebugData { + name: "InstantiatedModule(array_rw: array_rw).array_rw::array_in[13]", + ty: UInt<8>, + }, + SlotDebugData { + name: "InstantiatedModule(array_rw: array_rw).array_rw::array_in[14]", + ty: UInt<8>, + }, + SlotDebugData { + name: "InstantiatedModule(array_rw: array_rw).array_rw::array_in[15]", + ty: UInt<8>, + }, + SlotDebugData { + name: "InstantiatedModule(array_rw: array_rw).array_rw::array_out[0]", + ty: UInt<8>, + }, + SlotDebugData { + name: "InstantiatedModule(array_rw: array_rw).array_rw::array_out[1]", + ty: UInt<8>, + }, + SlotDebugData { + name: "InstantiatedModule(array_rw: array_rw).array_rw::array_out[2]", + ty: UInt<8>, + }, + SlotDebugData { + name: "InstantiatedModule(array_rw: array_rw).array_rw::array_out[3]", + ty: UInt<8>, + }, + SlotDebugData { + name: "InstantiatedModule(array_rw: array_rw).array_rw::array_out[4]", + ty: UInt<8>, + }, + SlotDebugData { + name: "InstantiatedModule(array_rw: array_rw).array_rw::array_out[5]", + ty: UInt<8>, + }, + SlotDebugData { + name: "InstantiatedModule(array_rw: array_rw).array_rw::array_out[6]", + ty: UInt<8>, + }, + SlotDebugData { + name: "InstantiatedModule(array_rw: array_rw).array_rw::array_out[7]", + ty: UInt<8>, + }, + SlotDebugData { + name: "InstantiatedModule(array_rw: array_rw).array_rw::array_out[8]", + ty: UInt<8>, + }, + SlotDebugData { + name: "InstantiatedModule(array_rw: array_rw).array_rw::array_out[9]", + ty: UInt<8>, + }, + SlotDebugData { + name: "InstantiatedModule(array_rw: array_rw).array_rw::array_out[10]", + ty: UInt<8>, + }, + SlotDebugData { + name: "InstantiatedModule(array_rw: array_rw).array_rw::array_out[11]", + ty: UInt<8>, + }, + SlotDebugData { + name: "InstantiatedModule(array_rw: array_rw).array_rw::array_out[12]", + ty: UInt<8>, + }, + SlotDebugData { + name: "InstantiatedModule(array_rw: array_rw).array_rw::array_out[13]", + ty: UInt<8>, + }, + SlotDebugData { + name: "InstantiatedModule(array_rw: array_rw).array_rw::array_out[14]", + ty: UInt<8>, + }, + SlotDebugData { + name: "InstantiatedModule(array_rw: array_rw).array_rw::array_out[15]", + ty: UInt<8>, + }, + SlotDebugData { + name: "InstantiatedModule(array_rw: array_rw).array_rw::read_index", + ty: UInt<8>, + }, + SlotDebugData { + name: "InstantiatedModule(array_rw: array_rw).array_rw::read_data", + ty: UInt<8>, + }, + SlotDebugData { + name: "InstantiatedModule(array_rw: array_rw).array_rw::write_index", + ty: UInt<8>, + }, + SlotDebugData { + name: "InstantiatedModule(array_rw: array_rw).array_rw::write_data", + ty: UInt<8>, + }, + SlotDebugData { + name: "InstantiatedModule(array_rw: array_rw).array_rw::write_en", + ty: Bool, + }, + SlotDebugData { + name: "InstantiatedModule(array_rw: array_rw).array_rw::array_wire[0]", + ty: UInt<8>, + }, + SlotDebugData { + name: "InstantiatedModule(array_rw: array_rw).array_rw::array_wire[1]", + ty: UInt<8>, + }, + SlotDebugData { + name: "InstantiatedModule(array_rw: array_rw).array_rw::array_wire[2]", + ty: UInt<8>, + }, + SlotDebugData { + name: "InstantiatedModule(array_rw: array_rw).array_rw::array_wire[3]", + ty: UInt<8>, + }, + SlotDebugData { + name: "InstantiatedModule(array_rw: array_rw).array_rw::array_wire[4]", + ty: UInt<8>, + }, + SlotDebugData { + name: "InstantiatedModule(array_rw: array_rw).array_rw::array_wire[5]", + ty: UInt<8>, + }, + SlotDebugData { + name: "InstantiatedModule(array_rw: array_rw).array_rw::array_wire[6]", + ty: UInt<8>, + }, + SlotDebugData { + name: "InstantiatedModule(array_rw: array_rw).array_rw::array_wire[7]", + ty: UInt<8>, + }, + SlotDebugData { + name: "InstantiatedModule(array_rw: array_rw).array_rw::array_wire[8]", + ty: UInt<8>, + }, + SlotDebugData { + name: "InstantiatedModule(array_rw: array_rw).array_rw::array_wire[9]", + ty: UInt<8>, + }, + SlotDebugData { + name: "InstantiatedModule(array_rw: array_rw).array_rw::array_wire[10]", + ty: UInt<8>, + }, + SlotDebugData { + name: "InstantiatedModule(array_rw: array_rw).array_rw::array_wire[11]", + ty: UInt<8>, + }, + SlotDebugData { + name: "InstantiatedModule(array_rw: array_rw).array_rw::array_wire[12]", + ty: UInt<8>, + }, + SlotDebugData { + name: "InstantiatedModule(array_rw: array_rw).array_rw::array_wire[13]", + ty: UInt<8>, + }, + SlotDebugData { + name: "InstantiatedModule(array_rw: array_rw).array_rw::array_wire[14]", + ty: UInt<8>, + }, + SlotDebugData { + name: "InstantiatedModule(array_rw: array_rw).array_rw::array_wire[15]", + ty: UInt<8>, + }, + SlotDebugData { + name: "", + ty: UInt<8>, + }, + ], + .. + }, + sim_only_slots: StatePartLayout { + len: 0, + debug_data: [], + layout_data: [], + .. + }, + }, + memories: StatePartLayout { + len: 0, + debug_data: [], + layout_data: [], + .. + }, + }, + insns: [ + // at: module-XXXXXXXXXX.rs:1:1 + 0: CastBigToArrayIndex { + dest: StatePartIndex(1), // (0x0 0) SlotDebugData { name: "", ty: UInt<8> }, + src: StatePartIndex(32), // (0x0) SlotDebugData { name: "InstantiatedModule(array_rw: array_rw).array_rw::read_index", ty: UInt<8> }, + }, + 1: CastBigToArrayIndex { + dest: StatePartIndex(0), // (0x10 16) SlotDebugData { name: "", ty: UInt<8> }, + src: StatePartIndex(34), // (0x10) SlotDebugData { name: "InstantiatedModule(array_rw: array_rw).array_rw::write_index", ty: UInt<8> }, + }, + // at: module-XXXXXXXXXX.rs:10:1 + 2: Copy { + dest: StatePartIndex(37), // (0xff) SlotDebugData { name: "InstantiatedModule(array_rw: array_rw).array_rw::array_wire[0]", ty: UInt<8> }, + src: StatePartIndex(0), // (0xff) SlotDebugData { name: "InstantiatedModule(array_rw: array_rw).array_rw::array_in[0]", ty: UInt<8> }, + }, + 3: Copy { + dest: StatePartIndex(38), // (0x7f) SlotDebugData { name: "InstantiatedModule(array_rw: array_rw).array_rw::array_wire[1]", ty: UInt<8> }, + src: StatePartIndex(1), // (0x7f) SlotDebugData { name: "InstantiatedModule(array_rw: array_rw).array_rw::array_in[1]", ty: UInt<8> }, + }, + 4: Copy { + dest: StatePartIndex(39), // (0x3f) SlotDebugData { name: "InstantiatedModule(array_rw: array_rw).array_rw::array_wire[2]", ty: UInt<8> }, + src: StatePartIndex(2), // (0x3f) SlotDebugData { name: "InstantiatedModule(array_rw: array_rw).array_rw::array_in[2]", ty: UInt<8> }, + }, + 5: Copy { + dest: StatePartIndex(40), // (0x1f) SlotDebugData { name: "InstantiatedModule(array_rw: array_rw).array_rw::array_wire[3]", ty: UInt<8> }, + src: StatePartIndex(3), // (0x1f) SlotDebugData { name: "InstantiatedModule(array_rw: array_rw).array_rw::array_in[3]", ty: UInt<8> }, + }, + 6: Copy { + dest: StatePartIndex(41), // (0xf) SlotDebugData { name: "InstantiatedModule(array_rw: array_rw).array_rw::array_wire[4]", ty: UInt<8> }, + src: StatePartIndex(4), // (0xf) SlotDebugData { name: "InstantiatedModule(array_rw: array_rw).array_rw::array_in[4]", ty: UInt<8> }, + }, + 7: Copy { + dest: StatePartIndex(42), // (0x7) SlotDebugData { name: "InstantiatedModule(array_rw: array_rw).array_rw::array_wire[5]", ty: UInt<8> }, + src: StatePartIndex(5), // (0x7) SlotDebugData { name: "InstantiatedModule(array_rw: array_rw).array_rw::array_in[5]", ty: UInt<8> }, + }, + 8: Copy { + dest: StatePartIndex(43), // (0x3) SlotDebugData { name: "InstantiatedModule(array_rw: array_rw).array_rw::array_wire[6]", ty: UInt<8> }, + src: StatePartIndex(6), // (0x3) SlotDebugData { name: "InstantiatedModule(array_rw: array_rw).array_rw::array_in[6]", ty: UInt<8> }, + }, + 9: Copy { + dest: StatePartIndex(44), // (0x1) SlotDebugData { name: "InstantiatedModule(array_rw: array_rw).array_rw::array_wire[7]", ty: UInt<8> }, + src: StatePartIndex(7), // (0x1) SlotDebugData { name: "InstantiatedModule(array_rw: array_rw).array_rw::array_in[7]", ty: UInt<8> }, + }, + 10: Copy { + dest: StatePartIndex(45), // (0x0) SlotDebugData { name: "InstantiatedModule(array_rw: array_rw).array_rw::array_wire[8]", ty: UInt<8> }, + src: StatePartIndex(8), // (0x0) SlotDebugData { name: "InstantiatedModule(array_rw: array_rw).array_rw::array_in[8]", ty: UInt<8> }, + }, + 11: Copy { + dest: StatePartIndex(46), // (0x80) SlotDebugData { name: "InstantiatedModule(array_rw: array_rw).array_rw::array_wire[9]", ty: UInt<8> }, + src: StatePartIndex(9), // (0x80) SlotDebugData { name: "InstantiatedModule(array_rw: array_rw).array_rw::array_in[9]", ty: UInt<8> }, + }, + 12: Copy { + dest: StatePartIndex(47), // (0xc0) SlotDebugData { name: "InstantiatedModule(array_rw: array_rw).array_rw::array_wire[10]", ty: UInt<8> }, + src: StatePartIndex(10), // (0xc0) SlotDebugData { name: "InstantiatedModule(array_rw: array_rw).array_rw::array_in[10]", ty: UInt<8> }, + }, + 13: Copy { + dest: StatePartIndex(48), // (0xe0) SlotDebugData { name: "InstantiatedModule(array_rw: array_rw).array_rw::array_wire[11]", ty: UInt<8> }, + src: StatePartIndex(11), // (0xe0) SlotDebugData { name: "InstantiatedModule(array_rw: array_rw).array_rw::array_in[11]", ty: UInt<8> }, + }, + 14: Copy { + dest: StatePartIndex(49), // (0xf0) SlotDebugData { name: "InstantiatedModule(array_rw: array_rw).array_rw::array_wire[12]", ty: UInt<8> }, + src: StatePartIndex(12), // (0xf0) SlotDebugData { name: "InstantiatedModule(array_rw: array_rw).array_rw::array_in[12]", ty: UInt<8> }, + }, + 15: Copy { + dest: StatePartIndex(50), // (0xf8) SlotDebugData { name: "InstantiatedModule(array_rw: array_rw).array_rw::array_wire[13]", ty: UInt<8> }, + src: StatePartIndex(13), // (0xf8) SlotDebugData { name: "InstantiatedModule(array_rw: array_rw).array_rw::array_in[13]", ty: UInt<8> }, + }, + 16: Copy { + dest: StatePartIndex(51), // (0xfc) SlotDebugData { name: "InstantiatedModule(array_rw: array_rw).array_rw::array_wire[14]", ty: UInt<8> }, + src: StatePartIndex(14), // (0xfc) SlotDebugData { name: "InstantiatedModule(array_rw: array_rw).array_rw::array_in[14]", ty: UInt<8> }, + }, + 17: Copy { + dest: StatePartIndex(52), // (0xfe) SlotDebugData { name: "InstantiatedModule(array_rw: array_rw).array_rw::array_wire[15]", ty: UInt<8> }, + src: StatePartIndex(15), // (0xfe) SlotDebugData { name: "InstantiatedModule(array_rw: array_rw).array_rw::array_in[15]", ty: UInt<8> }, + }, + // at: module-XXXXXXXXXX.rs:12:1 + 18: BranchIfZero { + target: 20, + value: StatePartIndex(36), // (0x1) SlotDebugData { name: "InstantiatedModule(array_rw: array_rw).array_rw::write_en", ty: Bool }, + }, + // at: module-XXXXXXXXXX.rs:13:1 + 19: WriteIndexed { + dest: StatePartIndex(37) /* (0xff) SlotDebugData { name: "InstantiatedModule(array_rw: array_rw).array_rw::array_wire[0]", ty: UInt<8> } */ [StatePartIndex(0) /* (0x10 16) SlotDebugData { name: "", ty: UInt<8> } */ , len=16, stride=1],, + src: StatePartIndex(35), // (0x0) SlotDebugData { name: "InstantiatedModule(array_rw: array_rw).array_rw::write_data", ty: UInt<8> }, + }, + // at: module-XXXXXXXXXX.rs:11:1 + 20: Copy { + dest: StatePartIndex(16), // (0xff) SlotDebugData { name: "InstantiatedModule(array_rw: array_rw).array_rw::array_out[0]", ty: UInt<8> }, + src: StatePartIndex(37), // (0xff) SlotDebugData { name: "InstantiatedModule(array_rw: array_rw).array_rw::array_wire[0]", ty: UInt<8> }, + }, + 21: Copy { + dest: StatePartIndex(17), // (0x7f) SlotDebugData { name: "InstantiatedModule(array_rw: array_rw).array_rw::array_out[1]", ty: UInt<8> }, + src: StatePartIndex(38), // (0x7f) SlotDebugData { name: "InstantiatedModule(array_rw: array_rw).array_rw::array_wire[1]", ty: UInt<8> }, + }, + 22: Copy { + dest: StatePartIndex(18), // (0x3f) SlotDebugData { name: "InstantiatedModule(array_rw: array_rw).array_rw::array_out[2]", ty: UInt<8> }, + src: StatePartIndex(39), // (0x3f) SlotDebugData { name: "InstantiatedModule(array_rw: array_rw).array_rw::array_wire[2]", ty: UInt<8> }, + }, + 23: Copy { + dest: StatePartIndex(19), // (0x1f) SlotDebugData { name: "InstantiatedModule(array_rw: array_rw).array_rw::array_out[3]", ty: UInt<8> }, + src: StatePartIndex(40), // (0x1f) SlotDebugData { name: "InstantiatedModule(array_rw: array_rw).array_rw::array_wire[3]", ty: UInt<8> }, + }, + 24: Copy { + dest: StatePartIndex(20), // (0xf) SlotDebugData { name: "InstantiatedModule(array_rw: array_rw).array_rw::array_out[4]", ty: UInt<8> }, + src: StatePartIndex(41), // (0xf) SlotDebugData { name: "InstantiatedModule(array_rw: array_rw).array_rw::array_wire[4]", ty: UInt<8> }, + }, + 25: Copy { + dest: StatePartIndex(21), // (0x7) SlotDebugData { name: "InstantiatedModule(array_rw: array_rw).array_rw::array_out[5]", ty: UInt<8> }, + src: StatePartIndex(42), // (0x7) SlotDebugData { name: "InstantiatedModule(array_rw: array_rw).array_rw::array_wire[5]", ty: UInt<8> }, + }, + 26: Copy { + dest: StatePartIndex(22), // (0x3) SlotDebugData { name: "InstantiatedModule(array_rw: array_rw).array_rw::array_out[6]", ty: UInt<8> }, + src: StatePartIndex(43), // (0x3) SlotDebugData { name: "InstantiatedModule(array_rw: array_rw).array_rw::array_wire[6]", ty: UInt<8> }, + }, + 27: Copy { + dest: StatePartIndex(23), // (0x1) SlotDebugData { name: "InstantiatedModule(array_rw: array_rw).array_rw::array_out[7]", ty: UInt<8> }, + src: StatePartIndex(44), // (0x1) SlotDebugData { name: "InstantiatedModule(array_rw: array_rw).array_rw::array_wire[7]", ty: UInt<8> }, + }, + 28: Copy { + dest: StatePartIndex(24), // (0x0) SlotDebugData { name: "InstantiatedModule(array_rw: array_rw).array_rw::array_out[8]", ty: UInt<8> }, + src: StatePartIndex(45), // (0x0) SlotDebugData { name: "InstantiatedModule(array_rw: array_rw).array_rw::array_wire[8]", ty: UInt<8> }, + }, + 29: Copy { + dest: StatePartIndex(25), // (0x80) SlotDebugData { name: "InstantiatedModule(array_rw: array_rw).array_rw::array_out[9]", ty: UInt<8> }, + src: StatePartIndex(46), // (0x80) SlotDebugData { name: "InstantiatedModule(array_rw: array_rw).array_rw::array_wire[9]", ty: UInt<8> }, + }, + 30: Copy { + dest: StatePartIndex(26), // (0xc0) SlotDebugData { name: "InstantiatedModule(array_rw: array_rw).array_rw::array_out[10]", ty: UInt<8> }, + src: StatePartIndex(47), // (0xc0) SlotDebugData { name: "InstantiatedModule(array_rw: array_rw).array_rw::array_wire[10]", ty: UInt<8> }, + }, + 31: Copy { + dest: StatePartIndex(27), // (0xe0) SlotDebugData { name: "InstantiatedModule(array_rw: array_rw).array_rw::array_out[11]", ty: UInt<8> }, + src: StatePartIndex(48), // (0xe0) SlotDebugData { name: "InstantiatedModule(array_rw: array_rw).array_rw::array_wire[11]", ty: UInt<8> }, + }, + 32: Copy { + dest: StatePartIndex(28), // (0xf0) SlotDebugData { name: "InstantiatedModule(array_rw: array_rw).array_rw::array_out[12]", ty: UInt<8> }, + src: StatePartIndex(49), // (0xf0) SlotDebugData { name: "InstantiatedModule(array_rw: array_rw).array_rw::array_wire[12]", ty: UInt<8> }, + }, + 33: Copy { + dest: StatePartIndex(29), // (0xf8) SlotDebugData { name: "InstantiatedModule(array_rw: array_rw).array_rw::array_out[13]", ty: UInt<8> }, + src: StatePartIndex(50), // (0xf8) SlotDebugData { name: "InstantiatedModule(array_rw: array_rw).array_rw::array_wire[13]", ty: UInt<8> }, + }, + 34: Copy { + dest: StatePartIndex(30), // (0xfc) SlotDebugData { name: "InstantiatedModule(array_rw: array_rw).array_rw::array_out[14]", ty: UInt<8> }, + src: StatePartIndex(51), // (0xfc) SlotDebugData { name: "InstantiatedModule(array_rw: array_rw).array_rw::array_wire[14]", ty: UInt<8> }, + }, + 35: Copy { + dest: StatePartIndex(31), // (0xfe) SlotDebugData { name: "InstantiatedModule(array_rw: array_rw).array_rw::array_out[15]", ty: UInt<8> }, + src: StatePartIndex(52), // (0xfe) SlotDebugData { name: "InstantiatedModule(array_rw: array_rw).array_rw::array_wire[15]", ty: UInt<8> }, + }, + // at: module-XXXXXXXXXX.rs:14:1 + 36: ReadIndexed { + dest: StatePartIndex(53), // (0xff) SlotDebugData { name: "", ty: UInt<8> }, + src: StatePartIndex(37) /* (0xff) SlotDebugData { name: "InstantiatedModule(array_rw: array_rw).array_rw::array_wire[0]", ty: UInt<8> } */ [StatePartIndex(1) /* (0x0 0) SlotDebugData { name: "", ty: UInt<8> } */ , len=16, stride=1],, + }, + 37: Copy { + dest: StatePartIndex(33), // (0xff) SlotDebugData { name: "InstantiatedModule(array_rw: array_rw).array_rw::read_data", ty: UInt<8> }, + src: StatePartIndex(53), // (0xff) SlotDebugData { name: "", ty: UInt<8> }, + }, + // at: module-XXXXXXXXXX.rs:1:1 + 38: Return, + ], + .. + }, + pc: 38, + memory_write_log: [], + memories: StatePart { + value: [], + }, + small_slots: StatePart { + value: [ + 16, + 0, + ], + }, + big_slots: StatePart { + value: [ + 255, + 127, + 63, + 31, + 15, + 7, + 3, + 1, + 0, + 128, + 192, + 224, + 240, + 248, + 252, + 254, + 255, + 127, + 63, + 31, + 15, + 7, + 3, + 1, + 0, + 128, + 192, + 224, + 240, + 248, + 252, + 254, + 0, + 255, + 16, + 0, + 1, + 255, + 127, + 63, + 31, + 15, + 7, + 3, + 1, + 0, + 128, + 192, + 224, + 240, + 248, + 252, + 254, + 255, + ], + }, + sim_only_slots: StatePart { + value: [], + }, + }, + io: Instance { + name: ::array_rw, + instantiated: Module { + name: array_rw, + .. + }, + }, + main_module: SimulationModuleState { + base_targets: [ + Instance { + name: ::array_rw, + instantiated: Module { + name: array_rw, + .. + }, + }.array_in, + Instance { + name: ::array_rw, + instantiated: Module { + name: array_rw, + .. + }, + }.array_out, + Instance { + name: ::array_rw, + instantiated: Module { + name: array_rw, + .. + }, + }.read_index, + Instance { + name: ::array_rw, + instantiated: Module { + name: array_rw, + .. + }, + }.read_data, + Instance { + name: ::array_rw, + instantiated: Module { + name: array_rw, + .. + }, + }.write_index, + Instance { + name: ::array_rw, + instantiated: Module { + name: array_rw, + .. + }, + }.write_data, + Instance { + name: ::array_rw, + instantiated: Module { + name: array_rw, + .. + }, + }.write_en, + ], + uninitialized_ios: {}, + io_targets: { + Instance { + name: ::array_rw, + instantiated: Module { + name: array_rw, + .. + }, + }.array_in, + Instance { + name: ::array_rw, + instantiated: Module { + name: array_rw, + .. + }, + }.array_in[0], + Instance { + name: ::array_rw, + instantiated: Module { + name: array_rw, + .. + }, + }.array_in[10], + Instance { + name: ::array_rw, + instantiated: Module { + name: array_rw, + .. + }, + }.array_in[11], + Instance { + name: ::array_rw, + instantiated: Module { + name: array_rw, + .. + }, + }.array_in[12], + Instance { + name: ::array_rw, + instantiated: Module { + name: array_rw, + .. + }, + }.array_in[13], + Instance { + name: ::array_rw, + instantiated: Module { + name: array_rw, + .. + }, + }.array_in[14], + Instance { + name: ::array_rw, + instantiated: Module { + name: array_rw, + .. + }, + }.array_in[15], + Instance { + name: ::array_rw, + instantiated: Module { + name: array_rw, + .. + }, + }.array_in[1], + Instance { + name: ::array_rw, + instantiated: Module { + name: array_rw, + .. + }, + }.array_in[2], + Instance { + name: ::array_rw, + instantiated: Module { + name: array_rw, + .. + }, + }.array_in[3], + Instance { + name: ::array_rw, + instantiated: Module { + name: array_rw, + .. + }, + }.array_in[4], + Instance { + name: ::array_rw, + instantiated: Module { + name: array_rw, + .. + }, + }.array_in[5], + Instance { + name: ::array_rw, + instantiated: Module { + name: array_rw, + .. + }, + }.array_in[6], + Instance { + name: ::array_rw, + instantiated: Module { + name: array_rw, + .. + }, + }.array_in[7], + Instance { + name: ::array_rw, + instantiated: Module { + name: array_rw, + .. + }, + }.array_in[8], + Instance { + name: ::array_rw, + instantiated: Module { + name: array_rw, + .. + }, + }.array_in[9], + Instance { + name: ::array_rw, + instantiated: Module { + name: array_rw, + .. + }, + }.array_out, + Instance { + name: ::array_rw, + instantiated: Module { + name: array_rw, + .. + }, + }.array_out[0], + Instance { + name: ::array_rw, + instantiated: Module { + name: array_rw, + .. + }, + }.array_out[10], + Instance { + name: ::array_rw, + instantiated: Module { + name: array_rw, + .. + }, + }.array_out[11], + Instance { + name: ::array_rw, + instantiated: Module { + name: array_rw, + .. + }, + }.array_out[12], + Instance { + name: ::array_rw, + instantiated: Module { + name: array_rw, + .. + }, + }.array_out[13], + Instance { + name: ::array_rw, + instantiated: Module { + name: array_rw, + .. + }, + }.array_out[14], + Instance { + name: ::array_rw, + instantiated: Module { + name: array_rw, + .. + }, + }.array_out[15], + Instance { + name: ::array_rw, + instantiated: Module { + name: array_rw, + .. + }, + }.array_out[1], + Instance { + name: ::array_rw, + instantiated: Module { + name: array_rw, + .. + }, + }.array_out[2], + Instance { + name: ::array_rw, + instantiated: Module { + name: array_rw, + .. + }, + }.array_out[3], + Instance { + name: ::array_rw, + instantiated: Module { + name: array_rw, + .. + }, + }.array_out[4], + Instance { + name: ::array_rw, + instantiated: Module { + name: array_rw, + .. + }, + }.array_out[5], + Instance { + name: ::array_rw, + instantiated: Module { + name: array_rw, + .. + }, + }.array_out[6], + Instance { + name: ::array_rw, + instantiated: Module { + name: array_rw, + .. + }, + }.array_out[7], + Instance { + name: ::array_rw, + instantiated: Module { + name: array_rw, + .. + }, + }.array_out[8], + Instance { + name: ::array_rw, + instantiated: Module { + name: array_rw, + .. + }, + }.array_out[9], + Instance { + name: ::array_rw, + instantiated: Module { + name: array_rw, + .. + }, + }.read_data, + Instance { + name: ::array_rw, + instantiated: Module { + name: array_rw, + .. + }, + }.read_index, + Instance { + name: ::array_rw, + instantiated: Module { + name: array_rw, + .. + }, + }.write_data, + Instance { + name: ::array_rw, + instantiated: Module { + name: array_rw, + .. + }, + }.write_en, + Instance { + name: ::array_rw, + instantiated: Module { + name: array_rw, + .. + }, + }.write_index, + }, + did_initial_settle: true, + }, + extern_modules: [], + state_ready_to_run: false, + trace_decls: TraceModule { + name: "array_rw", + children: [ + TraceModuleIO { + name: "array_in", + child: TraceArray { + name: "array_in", + elements: [ + TraceUInt { + location: TraceScalarId(0), + name: "[0]", + ty: UInt<8>, + flow: Source, + }, + TraceUInt { + location: TraceScalarId(1), + name: "[1]", + ty: UInt<8>, + flow: Source, + }, + TraceUInt { + location: TraceScalarId(2), + name: "[2]", + ty: UInt<8>, + flow: Source, + }, + TraceUInt { + location: TraceScalarId(3), + name: "[3]", + ty: UInt<8>, + flow: Source, + }, + TraceUInt { + location: TraceScalarId(4), + name: "[4]", + ty: UInt<8>, + flow: Source, + }, + TraceUInt { + location: TraceScalarId(5), + name: "[5]", + ty: UInt<8>, + flow: Source, + }, + TraceUInt { + location: TraceScalarId(6), + name: "[6]", + ty: UInt<8>, + flow: Source, + }, + TraceUInt { + location: TraceScalarId(7), + name: "[7]", + ty: UInt<8>, + flow: Source, + }, + TraceUInt { + location: TraceScalarId(8), + name: "[8]", + ty: UInt<8>, + flow: Source, + }, + TraceUInt { + location: TraceScalarId(9), + name: "[9]", + ty: UInt<8>, + flow: Source, + }, + TraceUInt { + location: TraceScalarId(10), + name: "[10]", + ty: UInt<8>, + flow: Source, + }, + TraceUInt { + location: TraceScalarId(11), + name: "[11]", + ty: UInt<8>, + flow: Source, + }, + TraceUInt { + location: TraceScalarId(12), + name: "[12]", + ty: UInt<8>, + flow: Source, + }, + TraceUInt { + location: TraceScalarId(13), + name: "[13]", + ty: UInt<8>, + flow: Source, + }, + TraceUInt { + location: TraceScalarId(14), + name: "[14]", + ty: UInt<8>, + flow: Source, + }, + TraceUInt { + location: TraceScalarId(15), + name: "[15]", + ty: UInt<8>, + flow: Source, + }, + ], + ty: Array, 16>, + flow: Source, + }, + ty: Array, 16>, + flow: Source, + }, + TraceModuleIO { + name: "array_out", + child: TraceArray { + name: "array_out", + elements: [ + TraceUInt { + location: TraceScalarId(16), + name: "[0]", + ty: UInt<8>, + flow: Sink, + }, + TraceUInt { + location: TraceScalarId(17), + name: "[1]", + ty: UInt<8>, + flow: Sink, + }, + TraceUInt { + location: TraceScalarId(18), + name: "[2]", + ty: UInt<8>, + flow: Sink, + }, + TraceUInt { + location: TraceScalarId(19), + name: "[3]", + ty: UInt<8>, + flow: Sink, + }, + TraceUInt { + location: TraceScalarId(20), + name: "[4]", + ty: UInt<8>, + flow: Sink, + }, + TraceUInt { + location: TraceScalarId(21), + name: "[5]", + ty: UInt<8>, + flow: Sink, + }, + TraceUInt { + location: TraceScalarId(22), + name: "[6]", + ty: UInt<8>, + flow: Sink, + }, + TraceUInt { + location: TraceScalarId(23), + name: "[7]", + ty: UInt<8>, + flow: Sink, + }, + TraceUInt { + location: TraceScalarId(24), + name: "[8]", + ty: UInt<8>, + flow: Sink, + }, + TraceUInt { + location: TraceScalarId(25), + name: "[9]", + ty: UInt<8>, + flow: Sink, + }, + TraceUInt { + location: TraceScalarId(26), + name: "[10]", + ty: UInt<8>, + flow: Sink, + }, + TraceUInt { + location: TraceScalarId(27), + name: "[11]", + ty: UInt<8>, + flow: Sink, + }, + TraceUInt { + location: TraceScalarId(28), + name: "[12]", + ty: UInt<8>, + flow: Sink, + }, + TraceUInt { + location: TraceScalarId(29), + name: "[13]", + ty: UInt<8>, + flow: Sink, + }, + TraceUInt { + location: TraceScalarId(30), + name: "[14]", + ty: UInt<8>, + flow: Sink, + }, + TraceUInt { + location: TraceScalarId(31), + name: "[15]", + ty: UInt<8>, + flow: Sink, + }, + ], + ty: Array, 16>, + flow: Sink, + }, + ty: Array, 16>, + flow: Sink, + }, + TraceModuleIO { + name: "read_index", + child: TraceUInt { + location: TraceScalarId(32), + name: "read_index", + ty: UInt<8>, + flow: Source, + }, + ty: UInt<8>, + flow: Source, + }, + TraceModuleIO { + name: "read_data", + child: TraceUInt { + location: TraceScalarId(33), + name: "read_data", + ty: UInt<8>, + flow: Sink, + }, + ty: UInt<8>, + flow: Sink, + }, + TraceModuleIO { + name: "write_index", + child: TraceUInt { + location: TraceScalarId(34), + name: "write_index", + ty: UInt<8>, + flow: Source, + }, + ty: UInt<8>, + flow: Source, + }, + TraceModuleIO { + name: "write_data", + child: TraceUInt { + location: TraceScalarId(35), + name: "write_data", + ty: UInt<8>, + flow: Source, + }, + ty: UInt<8>, + flow: Source, + }, + TraceModuleIO { + name: "write_en", + child: TraceBool { + location: TraceScalarId(36), + name: "write_en", + flow: Source, + }, + ty: Bool, + flow: Source, + }, + TraceWire { + name: "array_wire", + child: TraceArray { + name: "array_wire", + elements: [ + TraceUInt { + location: TraceScalarId(37), + name: "[0]", + ty: UInt<8>, + flow: Duplex, + }, + TraceUInt { + location: TraceScalarId(38), + name: "[1]", + ty: UInt<8>, + flow: Duplex, + }, + TraceUInt { + location: TraceScalarId(39), + name: "[2]", + ty: UInt<8>, + flow: Duplex, + }, + TraceUInt { + location: TraceScalarId(40), + name: "[3]", + ty: UInt<8>, + flow: Duplex, + }, + TraceUInt { + location: TraceScalarId(41), + name: "[4]", + ty: UInt<8>, + flow: Duplex, + }, + TraceUInt { + location: TraceScalarId(42), + name: "[5]", + ty: UInt<8>, + flow: Duplex, + }, + TraceUInt { + location: TraceScalarId(43), + name: "[6]", + ty: UInt<8>, + flow: Duplex, + }, + TraceUInt { + location: TraceScalarId(44), + name: "[7]", + ty: UInt<8>, + flow: Duplex, + }, + TraceUInt { + location: TraceScalarId(45), + name: "[8]", + ty: UInt<8>, + flow: Duplex, + }, + TraceUInt { + location: TraceScalarId(46), + name: "[9]", + ty: UInt<8>, + flow: Duplex, + }, + TraceUInt { + location: TraceScalarId(47), + name: "[10]", + ty: UInt<8>, + flow: Duplex, + }, + TraceUInt { + location: TraceScalarId(48), + name: "[11]", + ty: UInt<8>, + flow: Duplex, + }, + TraceUInt { + location: TraceScalarId(49), + name: "[12]", + ty: UInt<8>, + flow: Duplex, + }, + TraceUInt { + location: TraceScalarId(50), + name: "[13]", + ty: UInt<8>, + flow: Duplex, + }, + TraceUInt { + location: TraceScalarId(51), + name: "[14]", + ty: UInt<8>, + flow: Duplex, + }, + TraceUInt { + location: TraceScalarId(52), + name: "[15]", + ty: UInt<8>, + flow: Duplex, + }, + ], + ty: Array, 16>, + flow: Duplex, + }, + ty: Array, 16>, + }, + ], + }, + traces: [ + SimTrace { + id: TraceScalarId(0), + kind: BigUInt { + index: StatePartIndex(0), + ty: UInt<8>, + }, + state: 0xff, + last_state: 0xff, + }, + SimTrace { + id: TraceScalarId(1), + kind: BigUInt { + index: StatePartIndex(1), + ty: UInt<8>, + }, + state: 0x7f, + last_state: 0x7f, + }, + SimTrace { + id: TraceScalarId(2), + kind: BigUInt { + index: StatePartIndex(2), + ty: UInt<8>, + }, + state: 0x3f, + last_state: 0x3f, + }, + SimTrace { + id: TraceScalarId(3), + kind: BigUInt { + index: StatePartIndex(3), + ty: UInt<8>, + }, + state: 0x1f, + last_state: 0x1f, + }, + SimTrace { + id: TraceScalarId(4), + kind: BigUInt { + index: StatePartIndex(4), + ty: UInt<8>, + }, + state: 0x0f, + last_state: 0x0f, + }, + SimTrace { + id: TraceScalarId(5), + kind: BigUInt { + index: StatePartIndex(5), + ty: UInt<8>, + }, + state: 0x07, + last_state: 0x07, + }, + SimTrace { + id: TraceScalarId(6), + kind: BigUInt { + index: StatePartIndex(6), + ty: UInt<8>, + }, + state: 0x03, + last_state: 0x03, + }, + SimTrace { + id: TraceScalarId(7), + kind: BigUInt { + index: StatePartIndex(7), + ty: UInt<8>, + }, + state: 0x01, + last_state: 0x01, + }, + SimTrace { + id: TraceScalarId(8), + kind: BigUInt { + index: StatePartIndex(8), + ty: UInt<8>, + }, + state: 0x00, + last_state: 0x00, + }, + SimTrace { + id: TraceScalarId(9), + kind: BigUInt { + index: StatePartIndex(9), + ty: UInt<8>, + }, + state: 0x80, + last_state: 0x80, + }, + SimTrace { + id: TraceScalarId(10), + kind: BigUInt { + index: StatePartIndex(10), + ty: UInt<8>, + }, + state: 0xc0, + last_state: 0xc0, + }, + SimTrace { + id: TraceScalarId(11), + kind: BigUInt { + index: StatePartIndex(11), + ty: UInt<8>, + }, + state: 0xe0, + last_state: 0xe0, + }, + SimTrace { + id: TraceScalarId(12), + kind: BigUInt { + index: StatePartIndex(12), + ty: UInt<8>, + }, + state: 0xf0, + last_state: 0xf0, + }, + SimTrace { + id: TraceScalarId(13), + kind: BigUInt { + index: StatePartIndex(13), + ty: UInt<8>, + }, + state: 0xf8, + last_state: 0xf8, + }, + SimTrace { + id: TraceScalarId(14), + kind: BigUInt { + index: StatePartIndex(14), + ty: UInt<8>, + }, + state: 0xfc, + last_state: 0xfc, + }, + SimTrace { + id: TraceScalarId(15), + kind: BigUInt { + index: StatePartIndex(15), + ty: UInt<8>, + }, + state: 0xfe, + last_state: 0xfe, + }, + SimTrace { + id: TraceScalarId(16), + kind: BigUInt { + index: StatePartIndex(16), + ty: UInt<8>, + }, + state: 0xff, + last_state: 0xff, + }, + SimTrace { + id: TraceScalarId(17), + kind: BigUInt { + index: StatePartIndex(17), + ty: UInt<8>, + }, + state: 0x7f, + last_state: 0x7f, + }, + SimTrace { + id: TraceScalarId(18), + kind: BigUInt { + index: StatePartIndex(18), + ty: UInt<8>, + }, + state: 0x3f, + last_state: 0x3f, + }, + SimTrace { + id: TraceScalarId(19), + kind: BigUInt { + index: StatePartIndex(19), + ty: UInt<8>, + }, + state: 0x1f, + last_state: 0x1f, + }, + SimTrace { + id: TraceScalarId(20), + kind: BigUInt { + index: StatePartIndex(20), + ty: UInt<8>, + }, + state: 0x0f, + last_state: 0x0f, + }, + SimTrace { + id: TraceScalarId(21), + kind: BigUInt { + index: StatePartIndex(21), + ty: UInt<8>, + }, + state: 0x07, + last_state: 0x07, + }, + SimTrace { + id: TraceScalarId(22), + kind: BigUInt { + index: StatePartIndex(22), + ty: UInt<8>, + }, + state: 0x03, + last_state: 0x03, + }, + SimTrace { + id: TraceScalarId(23), + kind: BigUInt { + index: StatePartIndex(23), + ty: UInt<8>, + }, + state: 0x01, + last_state: 0x01, + }, + SimTrace { + id: TraceScalarId(24), + kind: BigUInt { + index: StatePartIndex(24), + ty: UInt<8>, + }, + state: 0x00, + last_state: 0x00, + }, + SimTrace { + id: TraceScalarId(25), + kind: BigUInt { + index: StatePartIndex(25), + ty: UInt<8>, + }, + state: 0x80, + last_state: 0x80, + }, + SimTrace { + id: TraceScalarId(26), + kind: BigUInt { + index: StatePartIndex(26), + ty: UInt<8>, + }, + state: 0xc0, + last_state: 0xc0, + }, + SimTrace { + id: TraceScalarId(27), + kind: BigUInt { + index: StatePartIndex(27), + ty: UInt<8>, + }, + state: 0xe0, + last_state: 0xe0, + }, + SimTrace { + id: TraceScalarId(28), + kind: BigUInt { + index: StatePartIndex(28), + ty: UInt<8>, + }, + state: 0xf0, + last_state: 0xf0, + }, + SimTrace { + id: TraceScalarId(29), + kind: BigUInt { + index: StatePartIndex(29), + ty: UInt<8>, + }, + state: 0xf8, + last_state: 0xf8, + }, + SimTrace { + id: TraceScalarId(30), + kind: BigUInt { + index: StatePartIndex(30), + ty: UInt<8>, + }, + state: 0xfc, + last_state: 0xfc, + }, + SimTrace { + id: TraceScalarId(31), + kind: BigUInt { + index: StatePartIndex(31), + ty: UInt<8>, + }, + state: 0xfe, + last_state: 0xe1, + }, + SimTrace { + id: TraceScalarId(32), + kind: BigUInt { + index: StatePartIndex(32), + ty: UInt<8>, + }, + state: 0x00, + last_state: 0x00, + }, + SimTrace { + id: TraceScalarId(33), + kind: BigUInt { + index: StatePartIndex(33), + ty: UInt<8>, + }, + state: 0xff, + last_state: 0xff, + }, + SimTrace { + id: TraceScalarId(34), + kind: BigUInt { + index: StatePartIndex(34), + ty: UInt<8>, + }, + state: 0x10, + last_state: 0x0f, + }, + SimTrace { + id: TraceScalarId(35), + kind: BigUInt { + index: StatePartIndex(35), + ty: UInt<8>, + }, + state: 0x00, + last_state: 0xe1, + }, + SimTrace { + id: TraceScalarId(36), + kind: BigBool { + index: StatePartIndex(36), + }, + state: 0x1, + last_state: 0x1, + }, + SimTrace { + id: TraceScalarId(37), + kind: BigUInt { + index: StatePartIndex(37), + ty: UInt<8>, + }, + state: 0xff, + last_state: 0xff, + }, + SimTrace { + id: TraceScalarId(38), + kind: BigUInt { + index: StatePartIndex(38), + ty: UInt<8>, + }, + state: 0x7f, + last_state: 0x7f, + }, + SimTrace { + id: TraceScalarId(39), + kind: BigUInt { + index: StatePartIndex(39), + ty: UInt<8>, + }, + state: 0x3f, + last_state: 0x3f, + }, + SimTrace { + id: TraceScalarId(40), + kind: BigUInt { + index: StatePartIndex(40), + ty: UInt<8>, + }, + state: 0x1f, + last_state: 0x1f, + }, + SimTrace { + id: TraceScalarId(41), + kind: BigUInt { + index: StatePartIndex(41), + ty: UInt<8>, + }, + state: 0x0f, + last_state: 0x0f, + }, + SimTrace { + id: TraceScalarId(42), + kind: BigUInt { + index: StatePartIndex(42), + ty: UInt<8>, + }, + state: 0x07, + last_state: 0x07, + }, + SimTrace { + id: TraceScalarId(43), + kind: BigUInt { + index: StatePartIndex(43), + ty: UInt<8>, + }, + state: 0x03, + last_state: 0x03, + }, + SimTrace { + id: TraceScalarId(44), + kind: BigUInt { + index: StatePartIndex(44), + ty: UInt<8>, + }, + state: 0x01, + last_state: 0x01, + }, + SimTrace { + id: TraceScalarId(45), + kind: BigUInt { + index: StatePartIndex(45), + ty: UInt<8>, + }, + state: 0x00, + last_state: 0x00, + }, + SimTrace { + id: TraceScalarId(46), + kind: BigUInt { + index: StatePartIndex(46), + ty: UInt<8>, + }, + state: 0x80, + last_state: 0x80, + }, + SimTrace { + id: TraceScalarId(47), + kind: BigUInt { + index: StatePartIndex(47), + ty: UInt<8>, + }, + state: 0xc0, + last_state: 0xc0, + }, + SimTrace { + id: TraceScalarId(48), + kind: BigUInt { + index: StatePartIndex(48), + ty: UInt<8>, + }, + state: 0xe0, + last_state: 0xe0, + }, + SimTrace { + id: TraceScalarId(49), + kind: BigUInt { + index: StatePartIndex(49), + ty: UInt<8>, + }, + state: 0xf0, + last_state: 0xf0, + }, + SimTrace { + id: TraceScalarId(50), + kind: BigUInt { + index: StatePartIndex(50), + ty: UInt<8>, + }, + state: 0xf8, + last_state: 0xf8, + }, + SimTrace { + id: TraceScalarId(51), + kind: BigUInt { + index: StatePartIndex(51), + ty: UInt<8>, + }, + state: 0xfc, + last_state: 0xfc, + }, + SimTrace { + id: TraceScalarId(52), + kind: BigUInt { + index: StatePartIndex(52), + ty: UInt<8>, + }, + state: 0xfe, + last_state: 0xe1, + }, + ], + trace_memories: {}, + trace_writers: [ + Running( + VcdWriter { + finished_init: true, + timescale: 1 ps, + .. + }, + ), + ], + instant: 34 μs, + clocks_triggered: [], + .. +} \ No newline at end of file diff --git a/crates/fayalite/tests/sim/expected/array_rw.vcd b/crates/fayalite/tests/sim/expected/array_rw.vcd new file mode 100644 index 0000000..8ede394 --- /dev/null +++ b/crates/fayalite/tests/sim/expected/array_rw.vcd @@ -0,0 +1,283 @@ +$timescale 1 ps $end +$scope module array_rw $end +$scope struct array_in $end +$var wire 8 ! \[0] $end +$var wire 8 " \[1] $end +$var wire 8 # \[2] $end +$var wire 8 $ \[3] $end +$var wire 8 % \[4] $end +$var wire 8 & \[5] $end +$var wire 8 ' \[6] $end +$var wire 8 ( \[7] $end +$var wire 8 ) \[8] $end +$var wire 8 * \[9] $end +$var wire 8 + \[10] $end +$var wire 8 , \[11] $end +$var wire 8 - \[12] $end +$var wire 8 . \[13] $end +$var wire 8 / \[14] $end +$var wire 8 0 \[15] $end +$upscope $end +$scope struct array_out $end +$var wire 8 1 \[0] $end +$var wire 8 2 \[1] $end +$var wire 8 3 \[2] $end +$var wire 8 4 \[3] $end +$var wire 8 5 \[4] $end +$var wire 8 6 \[5] $end +$var wire 8 7 \[6] $end +$var wire 8 8 \[7] $end +$var wire 8 9 \[8] $end +$var wire 8 : \[9] $end +$var wire 8 ; \[10] $end +$var wire 8 < \[11] $end +$var wire 8 = \[12] $end +$var wire 8 > \[13] $end +$var wire 8 ? \[14] $end +$var wire 8 @ \[15] $end +$upscope $end +$var wire 8 A read_index $end +$var wire 8 B read_data $end +$var wire 8 C write_index $end +$var wire 8 D write_data $end +$var wire 1 E write_en $end +$scope struct array_wire $end +$var wire 8 F \[0] $end +$var wire 8 G \[1] $end +$var wire 8 H \[2] $end +$var wire 8 I \[3] $end +$var wire 8 J \[4] $end +$var wire 8 K \[5] $end +$var wire 8 L \[6] $end +$var wire 8 M \[7] $end +$var wire 8 N \[8] $end +$var wire 8 O \[9] $end +$var wire 8 P \[10] $end +$var wire 8 Q \[11] $end +$var wire 8 R \[12] $end +$var wire 8 S \[13] $end +$var wire 8 T \[14] $end +$var wire 8 U \[15] $end +$upscope $end +$upscope $end +$enddefinitions $end +$dumpvars +b11111111 ! +b1111111 " +b111111 # +b11111 $ +b1111 % +b111 & +b11 ' +b1 ( +b0 ) +b10000000 * +b11000000 + +b11100000 , +b11110000 - +b11111000 . +b11111100 / +b11111110 0 +b11111111 1 +b1111111 2 +b111111 3 +b11111 4 +b1111 5 +b111 6 +b11 7 +b1 8 +b0 9 +b10000000 : +b11000000 ; +b11100000 < +b11110000 = +b11111000 > +b11111100 ? +b11111110 @ +b0 A +b11111111 B +b0 C +b0 D +0E +b11111111 F +b1111111 G +b111111 H +b11111 I +b1111 J +b111 K +b11 L +b1 M +b0 N +b10000000 O +b11000000 P +b11100000 Q +b11110000 R +b11111000 S +b11111100 T +b11111110 U +$end +#1000000 +b1 A +b1111111 B +#2000000 +b10 A +b111111 B +#3000000 +b11 A +b11111 B +#4000000 +b100 A +b1111 B +#5000000 +b101 A +b111 B +#6000000 +b110 A +b11 B +#7000000 +b111 A +b1 B +#8000000 +b1000 A +b0 B +#9000000 +b1001 A +b10000000 B +#10000000 +b1010 A +b11000000 B +#11000000 +b1011 A +b11100000 B +#12000000 +b1100 A +b11110000 B +#13000000 +b1101 A +b11111000 B +#14000000 +b1110 A +b11111100 B +#15000000 +b1111 A +b11111110 B +#16000000 +b10000 A +b0 B +#17000000 +b0 1 +b0 A +1E +b0 F +#18000000 +b11111111 1 +b1 2 +b11111111 B +b1 C +b1 D +b11111111 F +b1 G +#19000000 +b1111111 2 +b100 3 +b10 C +b100 D +b1111111 G +b100 H +#20000000 +b111111 3 +b1001 4 +b11 C +b1001 D +b111111 H +b1001 I +#21000000 +b11111 4 +b10000 5 +b100 C +b10000 D +b11111 I +b10000 J +#22000000 +b1111 5 +b11001 6 +b101 C +b11001 D +b1111 J +b11001 K +#23000000 +b111 6 +b100100 7 +b110 C +b100100 D +b111 K +b100100 L +#24000000 +b11 7 +b110001 8 +b111 C +b110001 D +b11 L +b110001 M +#25000000 +b1 8 +b1000000 9 +b1000 C +b1000000 D +b1 M +b1000000 N +#26000000 +b0 9 +b1010001 : +b1001 C +b1010001 D +b0 N +b1010001 O +#27000000 +b10000000 : +b1100100 ; +b1010 C +b1100100 D +b10000000 O +b1100100 P +#28000000 +b11000000 ; +b1111001 < +b1011 C +b1111001 D +b11000000 P +b1111001 Q +#29000000 +b11100000 < +b10010000 = +b1100 C +b10010000 D +b11100000 Q +b10010000 R +#30000000 +b11110000 = +b10101001 > +b1101 C +b10101001 D +b11110000 R +b10101001 S +#31000000 +b11111000 > +b11000100 ? +b1110 C +b11000100 D +b11111000 S +b11000100 T +#32000000 +b11111100 ? +b11100001 @ +b1111 C +b11100001 D +b11111100 T +b11100001 U +#33000000 +b11111110 @ +b10000 C +b0 D +b11111110 U +#34000000 diff --git a/crates/fayalite/tests/sim/expected/conditional_assignment_last.txt b/crates/fayalite/tests/sim/expected/conditional_assignment_last.txt new file mode 100644 index 0000000..58b2d20 --- /dev/null +++ b/crates/fayalite/tests/sim/expected/conditional_assignment_last.txt @@ -0,0 +1,183 @@ +Simulation { + state: State { + insns: Insns { + state_layout: StateLayout { + ty: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 4, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(conditional_assignment_last: conditional_assignment_last).conditional_assignment_last::i", + ty: Bool, + }, + SlotDebugData { + name: "InstantiatedModule(conditional_assignment_last: conditional_assignment_last).conditional_assignment_last::w", + ty: Bool, + }, + SlotDebugData { + name: "", + ty: Bool, + }, + SlotDebugData { + name: "", + ty: Bool, + }, + ], + .. + }, + sim_only_slots: StatePartLayout { + len: 0, + debug_data: [], + layout_data: [], + .. + }, + }, + memories: StatePartLayout { + len: 0, + debug_data: [], + layout_data: [], + .. + }, + }, + insns: [ + // at: module-XXXXXXXXXX.rs:1:1 + 0: Const { + dest: StatePartIndex(3), // (0x0) SlotDebugData { name: "", ty: Bool }, + value: 0x0, + }, + 1: Const { + dest: StatePartIndex(2), // (0x1) SlotDebugData { name: "", ty: Bool }, + value: 0x1, + }, + // at: module-XXXXXXXXXX.rs:4:1 + 2: Copy { + dest: StatePartIndex(1), // (0x0) SlotDebugData { name: "InstantiatedModule(conditional_assignment_last: conditional_assignment_last).conditional_assignment_last::w", ty: Bool }, + src: StatePartIndex(2), // (0x1) SlotDebugData { name: "", ty: Bool }, + }, + // at: module-XXXXXXXXXX.rs:5:1 + 3: BranchIfZero { + target: 5, + value: StatePartIndex(0), // (0x1) SlotDebugData { name: "InstantiatedModule(conditional_assignment_last: conditional_assignment_last).conditional_assignment_last::i", ty: Bool }, + }, + // at: module-XXXXXXXXXX.rs:6:1 + 4: Copy { + dest: StatePartIndex(1), // (0x0) SlotDebugData { name: "InstantiatedModule(conditional_assignment_last: conditional_assignment_last).conditional_assignment_last::w", ty: Bool }, + src: StatePartIndex(3), // (0x0) SlotDebugData { name: "", ty: Bool }, + }, + // at: module-XXXXXXXXXX.rs:1:1 + 5: Return, + ], + .. + }, + pc: 5, + memory_write_log: [], + memories: StatePart { + value: [], + }, + small_slots: StatePart { + value: [], + }, + big_slots: StatePart { + value: [ + 1, + 0, + 1, + 0, + ], + }, + sim_only_slots: StatePart { + value: [], + }, + }, + io: Instance { + name: ::conditional_assignment_last, + instantiated: Module { + name: conditional_assignment_last, + .. + }, + }, + main_module: SimulationModuleState { + base_targets: [ + Instance { + name: ::conditional_assignment_last, + instantiated: Module { + name: conditional_assignment_last, + .. + }, + }.i, + ], + uninitialized_ios: {}, + io_targets: { + Instance { + name: ::conditional_assignment_last, + instantiated: Module { + name: conditional_assignment_last, + .. + }, + }.i, + }, + did_initial_settle: true, + }, + extern_modules: [], + state_ready_to_run: false, + trace_decls: TraceModule { + name: "conditional_assignment_last", + children: [ + TraceModuleIO { + name: "i", + child: TraceBool { + location: TraceScalarId(0), + name: "i", + flow: Source, + }, + ty: Bool, + flow: Source, + }, + TraceWire { + name: "w", + child: TraceBool { + location: TraceScalarId(1), + name: "w", + flow: Duplex, + }, + ty: Bool, + }, + ], + }, + traces: [ + SimTrace { + id: TraceScalarId(0), + kind: BigBool { + index: StatePartIndex(0), + }, + state: 0x1, + last_state: 0x0, + }, + SimTrace { + id: TraceScalarId(1), + kind: BigBool { + index: StatePartIndex(1), + }, + state: 0x0, + last_state: 0x1, + }, + ], + trace_memories: {}, + trace_writers: [ + Running( + VcdWriter { + finished_init: true, + timescale: 1 ps, + .. + }, + ), + ], + instant: 2 μs, + clocks_triggered: [], + .. +} \ No newline at end of file diff --git a/crates/fayalite/tests/sim/expected/conditional_assignment_last.vcd b/crates/fayalite/tests/sim/expected/conditional_assignment_last.vcd new file mode 100644 index 0000000..dd9a85a --- /dev/null +++ b/crates/fayalite/tests/sim/expected/conditional_assignment_last.vcd @@ -0,0 +1,14 @@ +$timescale 1 ps $end +$scope module conditional_assignment_last $end +$var wire 1 ! i $end +$var wire 1 " w $end +$upscope $end +$enddefinitions $end +$dumpvars +0! +1" +$end +#1000000 +1! +0" +#2000000 diff --git a/crates/fayalite/tests/sim/expected/connect_const.txt b/crates/fayalite/tests/sim/expected/connect_const.txt index e44c50d..182ed84 100644 --- a/crates/fayalite/tests/sim/expected/connect_const.txt +++ b/crates/fayalite/tests/sim/expected/connect_const.txt @@ -22,6 +22,12 @@ Simulation { ], .. }, + sim_only_slots: StatePartLayout { + len: 0, + debug_data: [], + layout_data: [], + .. + }, }, memories: StatePartLayout { len: 0, @@ -60,6 +66,9 @@ Simulation { 5, ], }, + sim_only_slots: StatePart { + value: [], + }, }, io: Instance { name: ::connect_const, @@ -68,45 +77,30 @@ Simulation { .. }, }, - uninitialized_inputs: {}, - io_targets: { - Instance { - name: ::connect_const, - instantiated: Module { - name: connect_const, - .. - }, - }.o: CompiledValue { - layout: CompiledTypeLayout { - ty: UInt<8>, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "InstantiatedModule(connect_const: connect_const).connect_const::o", - ty: UInt<8>, - }, - ], - .. - }, + main_module: SimulationModuleState { + base_targets: [ + Instance { + name: ::connect_const, + instantiated: Module { + name: connect_const, + .. }, - body: Scalar, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 0, len: 1 }, - }, - write: None, + }.o, + ], + uninitialized_ios: {}, + io_targets: { + Instance { + name: ::connect_const, + instantiated: Module { + name: connect_const, + .. + }, + }.o, }, + did_initial_settle: true, }, - made_initial_step: true, - needs_settle: false, + extern_modules: [], + state_ready_to_run: false, trace_decls: TraceModule { name: "connect_const", children: [ diff --git a/crates/fayalite/tests/sim/expected/connect_const_reset.txt b/crates/fayalite/tests/sim/expected/connect_const_reset.txt index d1ab998..f56a6b4 100644 --- a/crates/fayalite/tests/sim/expected/connect_const_reset.txt +++ b/crates/fayalite/tests/sim/expected/connect_const_reset.txt @@ -34,6 +34,12 @@ Simulation { ], .. }, + sim_only_slots: StatePartLayout { + len: 0, + debug_data: [], + layout_data: [], + .. + }, }, memories: StatePartLayout { len: 0, @@ -89,6 +95,9 @@ Simulation { 1, ], }, + sim_only_slots: StatePart { + value: [], + }, }, io: Instance { name: ::connect_const_reset, @@ -97,79 +106,44 @@ Simulation { .. }, }, - uninitialized_inputs: {}, - io_targets: { - Instance { - name: ::connect_const_reset, - instantiated: Module { - name: connect_const_reset, - .. - }, - }.bit_out: CompiledValue { - layout: CompiledTypeLayout { - ty: Bool, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "InstantiatedModule(connect_const_reset: connect_const_reset).connect_const_reset::bit_out", - ty: Bool, - }, - ], - .. - }, + main_module: SimulationModuleState { + base_targets: [ + Instance { + name: ::connect_const_reset, + instantiated: Module { + name: connect_const_reset, + .. }, - body: Scalar, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 1, len: 1 }, - }, - write: None, - }, - Instance { - name: ::connect_const_reset, - instantiated: Module { - name: connect_const_reset, - .. - }, - }.reset_out: CompiledValue { - layout: CompiledTypeLayout { - ty: AsyncReset, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "InstantiatedModule(connect_const_reset: connect_const_reset).connect_const_reset::reset_out", - ty: AsyncReset, - }, - ], - .. - }, + }.reset_out, + Instance { + name: ::connect_const_reset, + instantiated: Module { + name: connect_const_reset, + .. }, - body: Scalar, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 0, len: 1 }, - }, - write: None, + }.bit_out, + ], + uninitialized_ios: {}, + io_targets: { + Instance { + name: ::connect_const_reset, + instantiated: Module { + name: connect_const_reset, + .. + }, + }.bit_out, + Instance { + name: ::connect_const_reset, + instantiated: Module { + name: connect_const_reset, + .. + }, + }.reset_out, }, + did_initial_settle: true, }, - made_initial_step: true, - needs_settle: false, + extern_modules: [], + state_ready_to_run: false, trace_decls: TraceModule { name: "connect_const_reset", children: [ diff --git a/crates/fayalite/tests/sim/expected/counter_async.txt b/crates/fayalite/tests/sim/expected/counter_async.txt index 2e005a0..8c8809a 100644 --- a/crates/fayalite/tests/sim/expected/counter_async.txt +++ b/crates/fayalite/tests/sim/expected/counter_async.txt @@ -71,6 +71,12 @@ Simulation { ], .. }, + sim_only_slots: StatePartLayout { + len: 0, + debug_data: [], + layout_data: [], + .. + }, }, memories: StatePartLayout { len: 0, @@ -195,6 +201,9 @@ Simulation { 4, ], }, + sim_only_slots: StatePart { + value: [], + }, }, io: Instance { name: ::counter, @@ -203,213 +212,58 @@ Simulation { .. }, }, - uninitialized_inputs: {}, - io_targets: { - Instance { - name: ::counter, - instantiated: Module { - name: counter, - .. - }, - }.cd: CompiledValue { - layout: CompiledTypeLayout { - ty: Bundle { - /* offset = 0 */ - clk: Clock, - /* offset = 1 */ - rst: AsyncReset, + main_module: SimulationModuleState { + base_targets: [ + Instance { + name: ::counter, + instantiated: Module { + name: counter, + .. }, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 2, - debug_data: [ - SlotDebugData { - name: "InstantiatedModule(counter: counter).counter::cd.clk", - ty: Clock, - }, - SlotDebugData { - name: "InstantiatedModule(counter: counter).counter::cd.rst", - ty: AsyncReset, - }, - ], - .. - }, + }.cd, + Instance { + name: ::counter, + instantiated: Module { + name: counter, + .. }, - body: Bundle { - fields: [ - CompiledBundleField { - offset: TypeIndex { - small_slots: StatePartIndex(0), - big_slots: StatePartIndex(0), - }, - ty: CompiledTypeLayout { - ty: Clock, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: Clock, - }, - ], - .. - }, - }, - body: Scalar, - }, - }, - CompiledBundleField { - offset: TypeIndex { - small_slots: StatePartIndex(0), - big_slots: StatePartIndex(1), - }, - ty: CompiledTypeLayout { - ty: AsyncReset, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: AsyncReset, - }, - ], - .. - }, - }, - body: Scalar, - }, - }, - ], + }.count, + ], + uninitialized_ios: {}, + io_targets: { + Instance { + name: ::counter, + instantiated: Module { + name: counter, + .. }, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 0, len: 2 }, - }, - write: None, - }, - Instance { - name: ::counter, - instantiated: Module { - name: counter, - .. - }, - }.cd.clk: CompiledValue { - layout: CompiledTypeLayout { - ty: Clock, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: Clock, - }, - ], - .. - }, - }, - body: Scalar, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 0, len: 1 }, - }, - write: None, - }, - Instance { - name: ::counter, - instantiated: Module { - name: counter, - .. - }, - }.cd.rst: CompiledValue { - layout: CompiledTypeLayout { - ty: AsyncReset, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: AsyncReset, - }, - ], - .. - }, - }, - body: Scalar, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 1, len: 1 }, - }, - write: None, - }, - Instance { - name: ::counter, - instantiated: Module { - name: counter, - .. - }, - }.count: CompiledValue { - layout: CompiledTypeLayout { - ty: UInt<4>, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "InstantiatedModule(counter: counter).counter::count", - ty: UInt<4>, - }, - ], - .. - }, - }, - body: Scalar, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 2, len: 1 }, - }, - write: None, + }.cd, + Instance { + name: ::counter, + instantiated: Module { + name: counter, + .. + }, + }.cd.clk, + Instance { + name: ::counter, + instantiated: Module { + name: counter, + .. + }, + }.cd.rst, + Instance { + name: ::counter, + instantiated: Module { + name: counter, + .. + }, + }.count, }, + did_initial_settle: true, }, - made_initial_step: true, - needs_settle: false, + extern_modules: [], + state_ready_to_run: false, trace_decls: TraceModule { name: "counter", children: [ diff --git a/crates/fayalite/tests/sim/expected/counter_sync.txt b/crates/fayalite/tests/sim/expected/counter_sync.txt index 78fc200..1d975b3 100644 --- a/crates/fayalite/tests/sim/expected/counter_sync.txt +++ b/crates/fayalite/tests/sim/expected/counter_sync.txt @@ -67,6 +67,12 @@ Simulation { ], .. }, + sim_only_slots: StatePartLayout { + len: 0, + debug_data: [], + layout_data: [], + .. + }, }, memories: StatePartLayout { len: 0, @@ -176,6 +182,9 @@ Simulation { 4, ], }, + sim_only_slots: StatePart { + value: [], + }, }, io: Instance { name: ::counter, @@ -184,213 +193,58 @@ Simulation { .. }, }, - uninitialized_inputs: {}, - io_targets: { - Instance { - name: ::counter, - instantiated: Module { - name: counter, - .. - }, - }.cd: CompiledValue { - layout: CompiledTypeLayout { - ty: Bundle { - /* offset = 0 */ - clk: Clock, - /* offset = 1 */ - rst: SyncReset, + main_module: SimulationModuleState { + base_targets: [ + Instance { + name: ::counter, + instantiated: Module { + name: counter, + .. }, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 2, - debug_data: [ - SlotDebugData { - name: "InstantiatedModule(counter: counter).counter::cd.clk", - ty: Clock, - }, - SlotDebugData { - name: "InstantiatedModule(counter: counter).counter::cd.rst", - ty: SyncReset, - }, - ], - .. - }, + }.cd, + Instance { + name: ::counter, + instantiated: Module { + name: counter, + .. }, - body: Bundle { - fields: [ - CompiledBundleField { - offset: TypeIndex { - small_slots: StatePartIndex(0), - big_slots: StatePartIndex(0), - }, - ty: CompiledTypeLayout { - ty: Clock, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: Clock, - }, - ], - .. - }, - }, - body: Scalar, - }, - }, - CompiledBundleField { - offset: TypeIndex { - small_slots: StatePartIndex(0), - big_slots: StatePartIndex(1), - }, - ty: CompiledTypeLayout { - ty: SyncReset, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: SyncReset, - }, - ], - .. - }, - }, - body: Scalar, - }, - }, - ], + }.count, + ], + uninitialized_ios: {}, + io_targets: { + Instance { + name: ::counter, + instantiated: Module { + name: counter, + .. }, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 0, len: 2 }, - }, - write: None, - }, - Instance { - name: ::counter, - instantiated: Module { - name: counter, - .. - }, - }.cd.clk: CompiledValue { - layout: CompiledTypeLayout { - ty: Clock, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: Clock, - }, - ], - .. - }, - }, - body: Scalar, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 0, len: 1 }, - }, - write: None, - }, - Instance { - name: ::counter, - instantiated: Module { - name: counter, - .. - }, - }.cd.rst: CompiledValue { - layout: CompiledTypeLayout { - ty: SyncReset, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: SyncReset, - }, - ], - .. - }, - }, - body: Scalar, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 1, len: 1 }, - }, - write: None, - }, - Instance { - name: ::counter, - instantiated: Module { - name: counter, - .. - }, - }.count: CompiledValue { - layout: CompiledTypeLayout { - ty: UInt<4>, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "InstantiatedModule(counter: counter).counter::count", - ty: UInt<4>, - }, - ], - .. - }, - }, - body: Scalar, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 2, len: 1 }, - }, - write: None, + }.cd, + Instance { + name: ::counter, + instantiated: Module { + name: counter, + .. + }, + }.cd.clk, + Instance { + name: ::counter, + instantiated: Module { + name: counter, + .. + }, + }.cd.rst, + Instance { + name: ::counter, + instantiated: Module { + name: counter, + .. + }, + }.count, }, + did_initial_settle: true, }, - made_initial_step: true, - needs_settle: false, + extern_modules: [], + state_ready_to_run: false, trace_decls: TraceModule { name: "counter", children: [ diff --git a/crates/fayalite/tests/sim/expected/duplicate_names.txt b/crates/fayalite/tests/sim/expected/duplicate_names.txt new file mode 100644 index 0000000..4c54aa8 --- /dev/null +++ b/crates/fayalite/tests/sim/expected/duplicate_names.txt @@ -0,0 +1,166 @@ +Simulation { + state: State { + insns: Insns { + state_layout: StateLayout { + ty: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 4, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(duplicate_names: duplicate_names).duplicate_names::w", + ty: UInt<8>, + }, + SlotDebugData { + name: "", + ty: UInt<8>, + }, + SlotDebugData { + name: "InstantiatedModule(duplicate_names: duplicate_names).duplicate_names::w", + ty: UInt<8>, + }, + SlotDebugData { + name: "", + ty: UInt<8>, + }, + ], + .. + }, + sim_only_slots: StatePartLayout { + len: 0, + debug_data: [], + layout_data: [], + .. + }, + }, + memories: StatePartLayout { + len: 0, + debug_data: [], + layout_data: [], + .. + }, + }, + insns: [ + // at: module-XXXXXXXXXX.rs:1:1 + 0: Const { + dest: StatePartIndex(3), // (0x6) SlotDebugData { name: "", ty: UInt<8> }, + value: 0x6, + }, + // at: module-XXXXXXXXXX.rs:5:1 + 1: Copy { + dest: StatePartIndex(2), // (0x6) SlotDebugData { name: "InstantiatedModule(duplicate_names: duplicate_names).duplicate_names::w", ty: UInt<8> }, + src: StatePartIndex(3), // (0x6) SlotDebugData { name: "", ty: UInt<8> }, + }, + // at: module-XXXXXXXXXX.rs:1:1 + 2: Const { + dest: StatePartIndex(1), // (0x5) SlotDebugData { name: "", ty: UInt<8> }, + value: 0x5, + }, + // at: module-XXXXXXXXXX.rs:3:1 + 3: Copy { + dest: StatePartIndex(0), // (0x5) SlotDebugData { name: "InstantiatedModule(duplicate_names: duplicate_names).duplicate_names::w", ty: UInt<8> }, + src: StatePartIndex(1), // (0x5) SlotDebugData { name: "", ty: UInt<8> }, + }, + // at: module-XXXXXXXXXX.rs:1:1 + 4: Return, + ], + .. + }, + pc: 4, + memory_write_log: [], + memories: StatePart { + value: [], + }, + small_slots: StatePart { + value: [], + }, + big_slots: StatePart { + value: [ + 5, + 5, + 6, + 6, + ], + }, + sim_only_slots: StatePart { + value: [], + }, + }, + io: Instance { + name: ::duplicate_names, + instantiated: Module { + name: duplicate_names, + .. + }, + }, + main_module: SimulationModuleState { + base_targets: [], + uninitialized_ios: {}, + io_targets: {}, + did_initial_settle: true, + }, + extern_modules: [], + state_ready_to_run: false, + trace_decls: TraceModule { + name: "duplicate_names", + children: [ + TraceWire { + name: "w", + child: TraceUInt { + location: TraceScalarId(0), + name: "w", + ty: UInt<8>, + flow: Duplex, + }, + ty: UInt<8>, + }, + TraceWire { + name: "w", + child: TraceUInt { + location: TraceScalarId(1), + name: "w", + ty: UInt<8>, + flow: Duplex, + }, + ty: UInt<8>, + }, + ], + }, + traces: [ + SimTrace { + id: TraceScalarId(0), + kind: BigUInt { + index: StatePartIndex(0), + ty: UInt<8>, + }, + state: 0x05, + last_state: 0x05, + }, + SimTrace { + id: TraceScalarId(1), + kind: BigUInt { + index: StatePartIndex(2), + ty: UInt<8>, + }, + state: 0x06, + last_state: 0x06, + }, + ], + trace_memories: {}, + trace_writers: [ + Running( + VcdWriter { + finished_init: true, + timescale: 1 ps, + .. + }, + ), + ], + instant: 1 μs, + clocks_triggered: [], + .. +} \ No newline at end of file diff --git a/crates/fayalite/tests/sim/expected/duplicate_names.vcd b/crates/fayalite/tests/sim/expected/duplicate_names.vcd new file mode 100644 index 0000000..1e9f6c6 --- /dev/null +++ b/crates/fayalite/tests/sim/expected/duplicate_names.vcd @@ -0,0 +1,11 @@ +$timescale 1 ps $end +$scope module duplicate_names $end +$var wire 8 ! w $end +$var wire 8 " w_2 $end +$upscope $end +$enddefinitions $end +$dumpvars +b101 ! +b110 " +$end +#1000000 diff --git a/crates/fayalite/tests/sim/expected/enums.txt b/crates/fayalite/tests/sim/expected/enums.txt index ebfae3e..4850a21 100644 --- a/crates/fayalite/tests/sim/expected/enums.txt +++ b/crates/fayalite/tests/sim/expected/enums.txt @@ -4,7 +4,7 @@ Simulation { state_layout: StateLayout { ty: TypeLayout { small_slots: StatePartLayout { - len: 6, + len: 7, debug_data: [ SlotDebugData { name: "", @@ -13,6 +13,13 @@ Simulation { HdlSome, }, }, + SlotDebugData { + name: "", + ty: Enum { + HdlNone, + HdlSome, + }, + }, SlotDebugData { name: "", ty: Bool, @@ -41,7 +48,7 @@ Simulation { .. }, big_slots: StatePartLayout { - len: 103, + len: 111, debug_data: [ SlotDebugData { name: "InstantiatedModule(enums: enums).enums::cd.clk", @@ -106,6 +113,41 @@ Simulation { name: "", ty: Bool, }, + SlotDebugData { + name: "InstantiatedModule(enums: enums).enums::b2_out", + ty: Enum { + HdlNone, + HdlSome(Bundle {0: UInt<1>, 1: Bool}), + }, + }, + SlotDebugData { + name: ".0", + ty: UInt<1>, + }, + SlotDebugData { + name: ".1", + ty: Bool, + }, + SlotDebugData { + name: "", + ty: UInt<3>, + }, + SlotDebugData { + name: "", + ty: UInt<2>, + }, + SlotDebugData { + name: "", + ty: UInt<1>, + }, + SlotDebugData { + name: "", + ty: UInt<1>, + }, + SlotDebugData { + name: "", + ty: Bool, + }, SlotDebugData { name: "InstantiatedModule(enums: enums).enums::the_reg", ty: Enum { @@ -487,6 +529,12 @@ Simulation { ], .. }, + sim_only_slots: StatePartLayout { + len: 0, + debug_data: [], + layout_data: [], + .. + }, }, memories: StatePartLayout { len: 0, @@ -498,594 +546,640 @@ Simulation { insns: [ // at: module-XXXXXXXXXX.rs:1:1 0: Const { - dest: StatePartIndex(90), // (0x1) SlotDebugData { name: "", ty: UInt<1> }, + dest: StatePartIndex(98), // (0x1) SlotDebugData { name: "", ty: UInt<1> }, value: 0x1, }, 1: Const { - dest: StatePartIndex(82), // (0x0) SlotDebugData { name: "", ty: UInt<4> }, + dest: StatePartIndex(90), // (0x0) SlotDebugData { name: "", ty: UInt<4> }, value: 0x0, }, 2: Const { - dest: StatePartIndex(80), // (0x0) SlotDebugData { name: "", ty: UInt<3> }, + dest: StatePartIndex(88), // (0x0) SlotDebugData { name: "", ty: UInt<3> }, value: 0x0, }, 3: Copy { - dest: StatePartIndex(81), // (0x0) SlotDebugData { name: "", ty: Enum {HdlNone, HdlSome(Bundle {0: UInt<1>, 1: Bool})} }, - src: StatePartIndex(80), // (0x0) SlotDebugData { name: "", ty: UInt<3> }, + dest: StatePartIndex(89), // (0x0) SlotDebugData { name: "", ty: Enum {HdlNone, HdlSome(Bundle {0: UInt<1>, 1: Bool})} }, + src: StatePartIndex(88), // (0x0) SlotDebugData { name: "", ty: UInt<3> }, }, - // at: module-XXXXXXXXXX.rs:16:1 + // at: module-XXXXXXXXXX.rs:18:1 4: Copy { dest: StatePartIndex(7), // (0x0) SlotDebugData { name: "InstantiatedModule(enums: enums).enums::b_out", ty: Enum {HdlNone, HdlSome(Bundle {0: UInt<1>, 1: Bool})} }, - src: StatePartIndex(81), // (0x0) SlotDebugData { name: "", ty: Enum {HdlNone, HdlSome(Bundle {0: UInt<1>, 1: Bool})} }, + src: StatePartIndex(89), // (0x0) SlotDebugData { name: "", ty: Enum {HdlNone, HdlSome(Bundle {0: UInt<1>, 1: Bool})} }, }, // at: module-XXXXXXXXXX.rs:1:1 5: SliceInt { - dest: StatePartIndex(69), // (0x3) SlotDebugData { name: "", ty: UInt<2> }, + dest: StatePartIndex(77), // (0x3) SlotDebugData { name: "", ty: UInt<2> }, src: StatePartIndex(4), // (0xf) SlotDebugData { name: "InstantiatedModule(enums: enums).enums::data_in", ty: UInt<4> }, start: 2, len: 2, }, 6: CastToSInt { - dest: StatePartIndex(70), // (-0x1) SlotDebugData { name: "", ty: SInt<2> }, - src: StatePartIndex(69), // (0x3) SlotDebugData { name: "", ty: UInt<2> }, + dest: StatePartIndex(78), // (-0x1) SlotDebugData { name: "", ty: SInt<2> }, + src: StatePartIndex(77), // (0x3) SlotDebugData { name: "", ty: UInt<2> }, dest_width: 2, }, 7: Const { - dest: StatePartIndex(62), // (0x2) SlotDebugData { name: "", ty: UInt<2> }, + dest: StatePartIndex(70), // (0x2) SlotDebugData { name: "", ty: UInt<2> }, value: 0x2, }, 8: SliceInt { - dest: StatePartIndex(49), // (0x1) SlotDebugData { name: "", ty: UInt<1> }, + dest: StatePartIndex(57), // (0x1) SlotDebugData { name: "", ty: UInt<1> }, src: StatePartIndex(4), // (0xf) SlotDebugData { name: "InstantiatedModule(enums: enums).enums::data_in", ty: UInt<4> }, start: 1, len: 1, }, 9: Copy { - dest: StatePartIndex(50), // (0x1) SlotDebugData { name: "", ty: Bool }, - src: StatePartIndex(49), // (0x1) SlotDebugData { name: "", ty: UInt<1> }, + dest: StatePartIndex(58), // (0x1) SlotDebugData { name: "", ty: Bool }, + src: StatePartIndex(57), // (0x1) SlotDebugData { name: "", ty: UInt<1> }, }, 10: Copy { - dest: StatePartIndex(68), // (0x1) SlotDebugData { name: "", ty: UInt<1> }, - src: StatePartIndex(50), // (0x1) SlotDebugData { name: "", ty: Bool }, + dest: StatePartIndex(76), // (0x1) SlotDebugData { name: "", ty: UInt<1> }, + src: StatePartIndex(58), // (0x1) SlotDebugData { name: "", ty: Bool }, }, 11: SliceInt { - dest: StatePartIndex(46), // (0x1) SlotDebugData { name: "", ty: UInt<1> }, + dest: StatePartIndex(54), // (0x1) SlotDebugData { name: "", ty: UInt<1> }, src: StatePartIndex(4), // (0xf) SlotDebugData { name: "InstantiatedModule(enums: enums).enums::data_in", ty: UInt<4> }, start: 0, len: 1, }, 12: Copy { - dest: StatePartIndex(47), // (0x1) SlotDebugData { name: "", ty: Bool }, - src: StatePartIndex(46), // (0x1) SlotDebugData { name: "", ty: UInt<1> }, + dest: StatePartIndex(55), // (0x1) SlotDebugData { name: "", ty: Bool }, + src: StatePartIndex(54), // (0x1) SlotDebugData { name: "", ty: UInt<1> }, }, 13: Copy { - dest: StatePartIndex(48), // (0x1) SlotDebugData { name: "", ty: UInt<1> }, - src: StatePartIndex(47), // (0x1) SlotDebugData { name: "", ty: Bool }, + dest: StatePartIndex(56), // (0x1) SlotDebugData { name: "", ty: UInt<1> }, + src: StatePartIndex(55), // (0x1) SlotDebugData { name: "", ty: Bool }, }, 14: Copy { - dest: StatePartIndex(44), // (0x1) SlotDebugData { name: ".0", ty: UInt<1> }, - src: StatePartIndex(48), // (0x1) SlotDebugData { name: "", ty: UInt<1> }, + dest: StatePartIndex(52), // (0x1) SlotDebugData { name: ".0", ty: UInt<1> }, + src: StatePartIndex(56), // (0x1) SlotDebugData { name: "", ty: UInt<1> }, }, 15: Copy { - dest: StatePartIndex(45), // (0x1) SlotDebugData { name: ".1", ty: Bool }, - src: StatePartIndex(50), // (0x1) SlotDebugData { name: "", ty: Bool }, + dest: StatePartIndex(53), // (0x1) SlotDebugData { name: ".1", ty: Bool }, + src: StatePartIndex(58), // (0x1) SlotDebugData { name: "", ty: Bool }, }, 16: Copy { - dest: StatePartIndex(66), // (0x1) SlotDebugData { name: "[0]", ty: UInt<1> }, - src: StatePartIndex(48), // (0x1) SlotDebugData { name: "", ty: UInt<1> }, + dest: StatePartIndex(74), // (0x1) SlotDebugData { name: "[0]", ty: UInt<1> }, + src: StatePartIndex(56), // (0x1) SlotDebugData { name: "", ty: UInt<1> }, }, 17: Copy { - dest: StatePartIndex(67), // (0x1) SlotDebugData { name: "[1]", ty: UInt<1> }, - src: StatePartIndex(68), // (0x1) SlotDebugData { name: "", ty: UInt<1> }, + dest: StatePartIndex(75), // (0x1) SlotDebugData { name: "[1]", ty: UInt<1> }, + src: StatePartIndex(76), // (0x1) SlotDebugData { name: "", ty: UInt<1> }, }, 18: Copy { - dest: StatePartIndex(63), // (0x1) SlotDebugData { name: ".a[0]", ty: UInt<1> }, - src: StatePartIndex(66), // (0x1) SlotDebugData { name: "[0]", ty: UInt<1> }, + dest: StatePartIndex(71), // (0x1) SlotDebugData { name: ".a[0]", ty: UInt<1> }, + src: StatePartIndex(74), // (0x1) SlotDebugData { name: "[0]", ty: UInt<1> }, }, 19: Copy { - dest: StatePartIndex(64), // (0x1) SlotDebugData { name: ".a[1]", ty: UInt<1> }, - src: StatePartIndex(67), // (0x1) SlotDebugData { name: "[1]", ty: UInt<1> }, + dest: StatePartIndex(72), // (0x1) SlotDebugData { name: ".a[1]", ty: UInt<1> }, + src: StatePartIndex(75), // (0x1) SlotDebugData { name: "[1]", ty: UInt<1> }, }, 20: Copy { - dest: StatePartIndex(65), // (-0x1) SlotDebugData { name: ".b", ty: SInt<2> }, - src: StatePartIndex(70), // (-0x1) SlotDebugData { name: "", ty: SInt<2> }, + dest: StatePartIndex(73), // (-0x1) SlotDebugData { name: ".b", ty: SInt<2> }, + src: StatePartIndex(78), // (-0x1) SlotDebugData { name: "", ty: SInt<2> }, }, 21: Copy { - dest: StatePartIndex(58), // (0x2) SlotDebugData { name: ".0", ty: UInt<2> }, - src: StatePartIndex(62), // (0x2) SlotDebugData { name: "", ty: UInt<2> }, + dest: StatePartIndex(66), // (0x2) SlotDebugData { name: ".0", ty: UInt<2> }, + src: StatePartIndex(70), // (0x2) SlotDebugData { name: "", ty: UInt<2> }, }, 22: Copy { - dest: StatePartIndex(59), // (0x1) SlotDebugData { name: ".1.a[0]", ty: UInt<1> }, - src: StatePartIndex(63), // (0x1) SlotDebugData { name: ".a[0]", ty: UInt<1> }, + dest: StatePartIndex(67), // (0x1) SlotDebugData { name: ".1.a[0]", ty: UInt<1> }, + src: StatePartIndex(71), // (0x1) SlotDebugData { name: ".a[0]", ty: UInt<1> }, }, 23: Copy { - dest: StatePartIndex(60), // (0x1) SlotDebugData { name: ".1.a[1]", ty: UInt<1> }, - src: StatePartIndex(64), // (0x1) SlotDebugData { name: ".a[1]", ty: UInt<1> }, + dest: StatePartIndex(68), // (0x1) SlotDebugData { name: ".1.a[1]", ty: UInt<1> }, + src: StatePartIndex(72), // (0x1) SlotDebugData { name: ".a[1]", ty: UInt<1> }, }, 24: Copy { - dest: StatePartIndex(61), // (-0x1) SlotDebugData { name: ".1.b", ty: SInt<2> }, - src: StatePartIndex(65), // (-0x1) SlotDebugData { name: ".b", ty: SInt<2> }, + dest: StatePartIndex(69), // (-0x1) SlotDebugData { name: ".1.b", ty: SInt<2> }, + src: StatePartIndex(73), // (-0x1) SlotDebugData { name: ".b", ty: SInt<2> }, }, 25: Shl { - dest: StatePartIndex(71), // (0x2) SlotDebugData { name: "", ty: UInt<2> }, - lhs: StatePartIndex(60), // (0x1) SlotDebugData { name: ".1.a[1]", ty: UInt<1> }, + dest: StatePartIndex(79), // (0x2) SlotDebugData { name: "", ty: UInt<2> }, + lhs: StatePartIndex(68), // (0x1) SlotDebugData { name: ".1.a[1]", ty: UInt<1> }, rhs: 1, }, 26: Or { - dest: StatePartIndex(72), // (0x3) SlotDebugData { name: "", ty: UInt<2> }, - lhs: StatePartIndex(59), // (0x1) SlotDebugData { name: ".1.a[0]", ty: UInt<1> }, - rhs: StatePartIndex(71), // (0x2) SlotDebugData { name: "", ty: UInt<2> }, + dest: StatePartIndex(80), // (0x3) SlotDebugData { name: "", ty: UInt<2> }, + lhs: StatePartIndex(67), // (0x1) SlotDebugData { name: ".1.a[0]", ty: UInt<1> }, + rhs: StatePartIndex(79), // (0x2) SlotDebugData { name: "", ty: UInt<2> }, }, 27: CastToUInt { - dest: StatePartIndex(73), // (0x3) SlotDebugData { name: "", ty: UInt<2> }, - src: StatePartIndex(61), // (-0x1) SlotDebugData { name: ".1.b", ty: SInt<2> }, + dest: StatePartIndex(81), // (0x3) SlotDebugData { name: "", ty: UInt<2> }, + src: StatePartIndex(69), // (-0x1) SlotDebugData { name: ".1.b", ty: SInt<2> }, dest_width: 2, }, 28: Shl { - dest: StatePartIndex(74), // (0xc) SlotDebugData { name: "", ty: UInt<4> }, - lhs: StatePartIndex(73), // (0x3) SlotDebugData { name: "", ty: UInt<2> }, + dest: StatePartIndex(82), // (0xc) SlotDebugData { name: "", ty: UInt<4> }, + lhs: StatePartIndex(81), // (0x3) SlotDebugData { name: "", ty: UInt<2> }, rhs: 2, }, 29: Or { - dest: StatePartIndex(75), // (0xf) SlotDebugData { name: "", ty: UInt<4> }, - lhs: StatePartIndex(72), // (0x3) SlotDebugData { name: "", ty: UInt<2> }, - rhs: StatePartIndex(74), // (0xc) SlotDebugData { name: "", ty: UInt<4> }, + dest: StatePartIndex(83), // (0xf) SlotDebugData { name: "", ty: UInt<4> }, + lhs: StatePartIndex(80), // (0x3) SlotDebugData { name: "", ty: UInt<2> }, + rhs: StatePartIndex(82), // (0xc) SlotDebugData { name: "", ty: UInt<4> }, }, 30: Shl { - dest: StatePartIndex(76), // (0x3c) SlotDebugData { name: "", ty: UInt<6> }, - lhs: StatePartIndex(75), // (0xf) SlotDebugData { name: "", ty: UInt<4> }, + dest: StatePartIndex(84), // (0x3c) SlotDebugData { name: "", ty: UInt<6> }, + lhs: StatePartIndex(83), // (0xf) SlotDebugData { name: "", ty: UInt<4> }, rhs: 2, }, 31: Or { - dest: StatePartIndex(77), // (0x3e) SlotDebugData { name: "", ty: UInt<6> }, - lhs: StatePartIndex(58), // (0x2) SlotDebugData { name: ".0", ty: UInt<2> }, - rhs: StatePartIndex(76), // (0x3c) SlotDebugData { name: "", ty: UInt<6> }, + dest: StatePartIndex(85), // (0x3e) SlotDebugData { name: "", ty: UInt<6> }, + lhs: StatePartIndex(66), // (0x2) SlotDebugData { name: ".0", ty: UInt<2> }, + rhs: StatePartIndex(84), // (0x3c) SlotDebugData { name: "", ty: UInt<6> }, }, 32: CastToUInt { - dest: StatePartIndex(78), // (0x3e) SlotDebugData { name: "", ty: UInt<6> }, - src: StatePartIndex(77), // (0x3e) SlotDebugData { name: "", ty: UInt<6> }, + dest: StatePartIndex(86), // (0x3e) SlotDebugData { name: "", ty: UInt<6> }, + src: StatePartIndex(85), // (0x3e) SlotDebugData { name: "", ty: UInt<6> }, dest_width: 6, }, 33: Copy { - dest: StatePartIndex(79), // (0x3e) SlotDebugData { name: "", ty: Enum {A, B(Bundle {0: UInt<1>, 1: Bool}), C(Bundle {a: Array, 2>, b: SInt<2>})} }, - src: StatePartIndex(78), // (0x3e) SlotDebugData { name: "", ty: UInt<6> }, + dest: StatePartIndex(87), // (0x3e) SlotDebugData { name: "", ty: Enum {A, B(Bundle {0: UInt<1>, 1: Bool}), C(Bundle {a: Array, 2>, b: SInt<2>})} }, + src: StatePartIndex(86), // (0x3e) SlotDebugData { name: "", ty: UInt<6> }, }, 34: Const { - dest: StatePartIndex(39), // (0x1) SlotDebugData { name: "", ty: UInt<2> }, + dest: StatePartIndex(47), // (0x1) SlotDebugData { name: "", ty: UInt<2> }, value: 0x1, }, 35: CmpEq { - dest: StatePartIndex(40), // (0x0) SlotDebugData { name: "", ty: Bool }, + dest: StatePartIndex(48), // (0x0) SlotDebugData { name: "", ty: Bool }, lhs: StatePartIndex(3), // (0x2) SlotDebugData { name: "InstantiatedModule(enums: enums).enums::which_in", ty: UInt<2> }, - rhs: StatePartIndex(39), // (0x1) SlotDebugData { name: "", ty: UInt<2> }, + rhs: StatePartIndex(47), // (0x1) SlotDebugData { name: "", ty: UInt<2> }, }, 36: Copy { - dest: StatePartIndex(41), // (0x1) SlotDebugData { name: ".0", ty: UInt<2> }, - src: StatePartIndex(39), // (0x1) SlotDebugData { name: "", ty: UInt<2> }, + dest: StatePartIndex(49), // (0x1) SlotDebugData { name: ".0", ty: UInt<2> }, + src: StatePartIndex(47), // (0x1) SlotDebugData { name: "", ty: UInt<2> }, }, 37: Copy { - dest: StatePartIndex(42), // (0x1) SlotDebugData { name: ".1.0", ty: UInt<1> }, - src: StatePartIndex(44), // (0x1) SlotDebugData { name: ".0", ty: UInt<1> }, + dest: StatePartIndex(50), // (0x1) SlotDebugData { name: ".1.0", ty: UInt<1> }, + src: StatePartIndex(52), // (0x1) SlotDebugData { name: ".0", ty: UInt<1> }, }, 38: Copy { - dest: StatePartIndex(43), // (0x1) SlotDebugData { name: ".1.1", ty: Bool }, - src: StatePartIndex(45), // (0x1) SlotDebugData { name: ".1", ty: Bool }, + dest: StatePartIndex(51), // (0x1) SlotDebugData { name: ".1.1", ty: Bool }, + src: StatePartIndex(53), // (0x1) SlotDebugData { name: ".1", ty: Bool }, }, 39: Copy { - dest: StatePartIndex(51), // (0x1) SlotDebugData { name: "", ty: UInt<1> }, - src: StatePartIndex(43), // (0x1) SlotDebugData { name: ".1.1", ty: Bool }, + dest: StatePartIndex(59), // (0x1) SlotDebugData { name: "", ty: UInt<1> }, + src: StatePartIndex(51), // (0x1) SlotDebugData { name: ".1.1", ty: Bool }, }, 40: Shl { - dest: StatePartIndex(52), // (0x2) SlotDebugData { name: "", ty: UInt<2> }, - lhs: StatePartIndex(51), // (0x1) SlotDebugData { name: "", ty: UInt<1> }, + dest: StatePartIndex(60), // (0x2) SlotDebugData { name: "", ty: UInt<2> }, + lhs: StatePartIndex(59), // (0x1) SlotDebugData { name: "", ty: UInt<1> }, rhs: 1, }, 41: Or { - dest: StatePartIndex(53), // (0x3) SlotDebugData { name: "", ty: UInt<2> }, - lhs: StatePartIndex(42), // (0x1) SlotDebugData { name: ".1.0", ty: UInt<1> }, - rhs: StatePartIndex(52), // (0x2) SlotDebugData { name: "", ty: UInt<2> }, + dest: StatePartIndex(61), // (0x3) SlotDebugData { name: "", ty: UInt<2> }, + lhs: StatePartIndex(50), // (0x1) SlotDebugData { name: ".1.0", ty: UInt<1> }, + rhs: StatePartIndex(60), // (0x2) SlotDebugData { name: "", ty: UInt<2> }, }, 42: Shl { - dest: StatePartIndex(54), // (0xc) SlotDebugData { name: "", ty: UInt<4> }, - lhs: StatePartIndex(53), // (0x3) SlotDebugData { name: "", ty: UInt<2> }, + dest: StatePartIndex(62), // (0xc) SlotDebugData { name: "", ty: UInt<4> }, + lhs: StatePartIndex(61), // (0x3) SlotDebugData { name: "", ty: UInt<2> }, rhs: 2, }, 43: Or { - dest: StatePartIndex(55), // (0xd) SlotDebugData { name: "", ty: UInt<4> }, - lhs: StatePartIndex(41), // (0x1) SlotDebugData { name: ".0", ty: UInt<2> }, - rhs: StatePartIndex(54), // (0xc) SlotDebugData { name: "", ty: UInt<4> }, + dest: StatePartIndex(63), // (0xd) SlotDebugData { name: "", ty: UInt<4> }, + lhs: StatePartIndex(49), // (0x1) SlotDebugData { name: ".0", ty: UInt<2> }, + rhs: StatePartIndex(62), // (0xc) SlotDebugData { name: "", ty: UInt<4> }, }, 44: CastToUInt { - dest: StatePartIndex(56), // (0xd) SlotDebugData { name: "", ty: UInt<6> }, - src: StatePartIndex(55), // (0xd) SlotDebugData { name: "", ty: UInt<4> }, + dest: StatePartIndex(64), // (0xd) SlotDebugData { name: "", ty: UInt<6> }, + src: StatePartIndex(63), // (0xd) SlotDebugData { name: "", ty: UInt<4> }, dest_width: 6, }, 45: Copy { - dest: StatePartIndex(57), // (0xd) SlotDebugData { name: "", ty: Enum {A, B(Bundle {0: UInt<1>, 1: Bool}), C(Bundle {a: Array, 2>, b: SInt<2>})} }, - src: StatePartIndex(56), // (0xd) SlotDebugData { name: "", ty: UInt<6> }, + dest: StatePartIndex(65), // (0xd) SlotDebugData { name: "", ty: Enum {A, B(Bundle {0: UInt<1>, 1: Bool}), C(Bundle {a: Array, 2>, b: SInt<2>})} }, + src: StatePartIndex(64), // (0xd) SlotDebugData { name: "", ty: UInt<6> }, }, 46: Const { - dest: StatePartIndex(37), // (0x0) SlotDebugData { name: "", ty: UInt<2> }, + dest: StatePartIndex(45), // (0x0) SlotDebugData { name: "", ty: UInt<2> }, value: 0x0, }, 47: CmpEq { - dest: StatePartIndex(38), // (0x0) SlotDebugData { name: "", ty: Bool }, + dest: StatePartIndex(46), // (0x0) SlotDebugData { name: "", ty: Bool }, lhs: StatePartIndex(3), // (0x2) SlotDebugData { name: "InstantiatedModule(enums: enums).enums::which_in", ty: UInt<2> }, - rhs: StatePartIndex(37), // (0x0) SlotDebugData { name: "", ty: UInt<2> }, + rhs: StatePartIndex(45), // (0x0) SlotDebugData { name: "", ty: UInt<2> }, }, 48: Copy { - dest: StatePartIndex(21), // (0x3e) SlotDebugData { name: "", ty: UInt<6> }, - src: StatePartIndex(15), // (0x3e) SlotDebugData { name: "InstantiatedModule(enums: enums).enums::the_reg", ty: Enum {A, B(Bundle {0: UInt<1>, 1: Bool}), C(Bundle {a: Array, 2>, b: SInt<2>})} }, + dest: StatePartIndex(29), // (0x3e) SlotDebugData { name: "", ty: UInt<6> }, + src: StatePartIndex(23), // (0x3e) SlotDebugData { name: "InstantiatedModule(enums: enums).enums::the_reg", ty: Enum {A, B(Bundle {0: UInt<1>, 1: Bool}), C(Bundle {a: Array, 2>, b: SInt<2>})} }, }, 49: SliceInt { - dest: StatePartIndex(22), // (0x3) SlotDebugData { name: "", ty: UInt<2> }, - src: StatePartIndex(21), // (0x3e) SlotDebugData { name: "", ty: UInt<6> }, + dest: StatePartIndex(30), // (0x3) SlotDebugData { name: "", ty: UInt<2> }, + src: StatePartIndex(29), // (0x3e) SlotDebugData { name: "", ty: UInt<6> }, start: 2, len: 2, }, 50: SliceInt { - dest: StatePartIndex(23), // (0x1) SlotDebugData { name: "", ty: UInt<1> }, - src: StatePartIndex(22), // (0x3) SlotDebugData { name: "", ty: UInt<2> }, + dest: StatePartIndex(31), // (0x1) SlotDebugData { name: "", ty: UInt<1> }, + src: StatePartIndex(30), // (0x3) SlotDebugData { name: "", ty: UInt<2> }, start: 0, len: 1, }, 51: SliceInt { - dest: StatePartIndex(24), // (0x1) SlotDebugData { name: "", ty: UInt<1> }, - src: StatePartIndex(22), // (0x3) SlotDebugData { name: "", ty: UInt<2> }, + dest: StatePartIndex(32), // (0x1) SlotDebugData { name: "", ty: UInt<1> }, + src: StatePartIndex(30), // (0x3) SlotDebugData { name: "", ty: UInt<2> }, start: 1, len: 1, }, 52: Copy { - dest: StatePartIndex(25), // (0x1) SlotDebugData { name: "", ty: Bool }, - src: StatePartIndex(24), // (0x1) SlotDebugData { name: "", ty: UInt<1> }, + dest: StatePartIndex(33), // (0x1) SlotDebugData { name: "", ty: Bool }, + src: StatePartIndex(32), // (0x1) SlotDebugData { name: "", ty: UInt<1> }, }, 53: Copy { - dest: StatePartIndex(19), // (0x1) SlotDebugData { name: ".0", ty: UInt<1> }, - src: StatePartIndex(23), // (0x1) SlotDebugData { name: "", ty: UInt<1> }, + dest: StatePartIndex(27), // (0x1) SlotDebugData { name: ".0", ty: UInt<1> }, + src: StatePartIndex(31), // (0x1) SlotDebugData { name: "", ty: UInt<1> }, }, 54: Copy { - dest: StatePartIndex(20), // (0x1) SlotDebugData { name: ".1", ty: Bool }, - src: StatePartIndex(25), // (0x1) SlotDebugData { name: "", ty: Bool }, + dest: StatePartIndex(28), // (0x1) SlotDebugData { name: ".1", ty: Bool }, + src: StatePartIndex(33), // (0x1) SlotDebugData { name: "", ty: Bool }, }, 55: Copy { - dest: StatePartIndex(83), // (0x1) SlotDebugData { name: "", ty: UInt<1> }, - src: StatePartIndex(20), // (0x1) SlotDebugData { name: ".1", ty: Bool }, + dest: StatePartIndex(91), // (0x1) SlotDebugData { name: "", ty: UInt<1> }, + src: StatePartIndex(28), // (0x1) SlotDebugData { name: ".1", ty: Bool }, }, 56: Shl { - dest: StatePartIndex(84), // (0x2) SlotDebugData { name: "", ty: UInt<2> }, - lhs: StatePartIndex(83), // (0x1) SlotDebugData { name: "", ty: UInt<1> }, - rhs: 1, - }, - 57: Or { - dest: StatePartIndex(85), // (0x3) SlotDebugData { name: "", ty: UInt<2> }, - lhs: StatePartIndex(19), // (0x1) SlotDebugData { name: ".0", ty: UInt<1> }, - rhs: StatePartIndex(84), // (0x2) SlotDebugData { name: "", ty: UInt<2> }, - }, - 58: CastToUInt { - dest: StatePartIndex(86), // (0x3) SlotDebugData { name: "", ty: UInt<4> }, - src: StatePartIndex(85), // (0x3) SlotDebugData { name: "", ty: UInt<2> }, - dest_width: 4, - }, - 59: Copy { - dest: StatePartIndex(87), // (0x1) SlotDebugData { name: ".0", ty: UInt<1> }, - src: StatePartIndex(90), // (0x1) SlotDebugData { name: "", ty: UInt<1> }, - }, - 60: Copy { - dest: StatePartIndex(88), // (0x1) SlotDebugData { name: ".1.0", ty: UInt<1> }, - src: StatePartIndex(19), // (0x1) SlotDebugData { name: ".0", ty: UInt<1> }, - }, - 61: Copy { - dest: StatePartIndex(89), // (0x1) SlotDebugData { name: ".1.1", ty: Bool }, - src: StatePartIndex(20), // (0x1) SlotDebugData { name: ".1", ty: Bool }, - }, - 62: Copy { - dest: StatePartIndex(91), // (0x1) SlotDebugData { name: "", ty: UInt<1> }, - src: StatePartIndex(89), // (0x1) SlotDebugData { name: ".1.1", ty: Bool }, - }, - 63: Shl { dest: StatePartIndex(92), // (0x2) SlotDebugData { name: "", ty: UInt<2> }, lhs: StatePartIndex(91), // (0x1) SlotDebugData { name: "", ty: UInt<1> }, rhs: 1, }, - 64: Or { + 57: Or { dest: StatePartIndex(93), // (0x3) SlotDebugData { name: "", ty: UInt<2> }, - lhs: StatePartIndex(88), // (0x1) SlotDebugData { name: ".1.0", ty: UInt<1> }, + lhs: StatePartIndex(27), // (0x1) SlotDebugData { name: ".0", ty: UInt<1> }, rhs: StatePartIndex(92), // (0x2) SlotDebugData { name: "", ty: UInt<2> }, }, + 58: CastToUInt { + dest: StatePartIndex(94), // (0x3) SlotDebugData { name: "", ty: UInt<4> }, + src: StatePartIndex(93), // (0x3) SlotDebugData { name: "", ty: UInt<2> }, + dest_width: 4, + }, + 59: Copy { + dest: StatePartIndex(95), // (0x1) SlotDebugData { name: ".0", ty: UInt<1> }, + src: StatePartIndex(98), // (0x1) SlotDebugData { name: "", ty: UInt<1> }, + }, + 60: Copy { + dest: StatePartIndex(96), // (0x1) SlotDebugData { name: ".1.0", ty: UInt<1> }, + src: StatePartIndex(27), // (0x1) SlotDebugData { name: ".0", ty: UInt<1> }, + }, + 61: Copy { + dest: StatePartIndex(97), // (0x1) SlotDebugData { name: ".1.1", ty: Bool }, + src: StatePartIndex(28), // (0x1) SlotDebugData { name: ".1", ty: Bool }, + }, + 62: Copy { + dest: StatePartIndex(99), // (0x1) SlotDebugData { name: "", ty: UInt<1> }, + src: StatePartIndex(97), // (0x1) SlotDebugData { name: ".1.1", ty: Bool }, + }, + 63: Shl { + dest: StatePartIndex(100), // (0x2) SlotDebugData { name: "", ty: UInt<2> }, + lhs: StatePartIndex(99), // (0x1) SlotDebugData { name: "", ty: UInt<1> }, + rhs: 1, + }, + 64: Or { + dest: StatePartIndex(101), // (0x3) SlotDebugData { name: "", ty: UInt<2> }, + lhs: StatePartIndex(96), // (0x1) SlotDebugData { name: ".1.0", ty: UInt<1> }, + rhs: StatePartIndex(100), // (0x2) SlotDebugData { name: "", ty: UInt<2> }, + }, 65: Shl { - dest: StatePartIndex(94), // (0x6) SlotDebugData { name: "", ty: UInt<3> }, - lhs: StatePartIndex(93), // (0x3) SlotDebugData { name: "", ty: UInt<2> }, + dest: StatePartIndex(102), // (0x6) SlotDebugData { name: "", ty: UInt<3> }, + lhs: StatePartIndex(101), // (0x3) SlotDebugData { name: "", ty: UInt<2> }, rhs: 1, }, 66: Or { - dest: StatePartIndex(95), // (0x7) SlotDebugData { name: "", ty: UInt<3> }, - lhs: StatePartIndex(87), // (0x1) SlotDebugData { name: ".0", ty: UInt<1> }, - rhs: StatePartIndex(94), // (0x6) SlotDebugData { name: "", ty: UInt<3> }, + dest: StatePartIndex(103), // (0x7) SlotDebugData { name: "", ty: UInt<3> }, + lhs: StatePartIndex(95), // (0x1) SlotDebugData { name: ".0", ty: UInt<1> }, + rhs: StatePartIndex(102), // (0x6) SlotDebugData { name: "", ty: UInt<3> }, }, 67: CastToUInt { - dest: StatePartIndex(96), // (0x7) SlotDebugData { name: "", ty: UInt<3> }, - src: StatePartIndex(95), // (0x7) SlotDebugData { name: "", ty: UInt<3> }, + dest: StatePartIndex(104), // (0x7) SlotDebugData { name: "", ty: UInt<3> }, + src: StatePartIndex(103), // (0x7) SlotDebugData { name: "", ty: UInt<3> }, dest_width: 3, }, 68: Copy { - dest: StatePartIndex(97), // (0x7) SlotDebugData { name: "", ty: Enum {HdlNone, HdlSome(Bundle {0: UInt<1>, 1: Bool})} }, - src: StatePartIndex(96), // (0x7) SlotDebugData { name: "", ty: UInt<3> }, + dest: StatePartIndex(105), // (0x7) SlotDebugData { name: "", ty: Enum {HdlNone, HdlSome(Bundle {0: UInt<1>, 1: Bool})} }, + src: StatePartIndex(104), // (0x7) SlotDebugData { name: "", ty: UInt<3> }, }, 69: SliceInt { - dest: StatePartIndex(31), // (0xf) SlotDebugData { name: "", ty: UInt<4> }, - src: StatePartIndex(21), // (0x3e) SlotDebugData { name: "", ty: UInt<6> }, + dest: StatePartIndex(39), // (0xf) SlotDebugData { name: "", ty: UInt<4> }, + src: StatePartIndex(29), // (0x3e) SlotDebugData { name: "", ty: UInt<6> }, start: 2, len: 4, }, 70: SliceInt { - dest: StatePartIndex(32), // (0x3) SlotDebugData { name: "", ty: UInt<2> }, - src: StatePartIndex(31), // (0xf) SlotDebugData { name: "", ty: UInt<4> }, + dest: StatePartIndex(40), // (0x3) SlotDebugData { name: "", ty: UInt<2> }, + src: StatePartIndex(39), // (0xf) SlotDebugData { name: "", ty: UInt<4> }, start: 0, len: 2, }, 71: SliceInt { - dest: StatePartIndex(33), // (0x1) SlotDebugData { name: "", ty: UInt<1> }, - src: StatePartIndex(32), // (0x3) SlotDebugData { name: "", ty: UInt<2> }, + dest: StatePartIndex(41), // (0x1) SlotDebugData { name: "", ty: UInt<1> }, + src: StatePartIndex(40), // (0x3) SlotDebugData { name: "", ty: UInt<2> }, start: 0, len: 1, }, 72: SliceInt { - dest: StatePartIndex(34), // (0x1) SlotDebugData { name: "", ty: UInt<1> }, - src: StatePartIndex(32), // (0x3) SlotDebugData { name: "", ty: UInt<2> }, + dest: StatePartIndex(42), // (0x1) SlotDebugData { name: "", ty: UInt<1> }, + src: StatePartIndex(40), // (0x3) SlotDebugData { name: "", ty: UInt<2> }, start: 1, len: 1, }, 73: Copy { - dest: StatePartIndex(29), // (0x1) SlotDebugData { name: "[0]", ty: UInt<1> }, - src: StatePartIndex(33), // (0x1) SlotDebugData { name: "", ty: UInt<1> }, + dest: StatePartIndex(37), // (0x1) SlotDebugData { name: "[0]", ty: UInt<1> }, + src: StatePartIndex(41), // (0x1) SlotDebugData { name: "", ty: UInt<1> }, }, 74: Copy { - dest: StatePartIndex(30), // (0x1) SlotDebugData { name: "[1]", ty: UInt<1> }, - src: StatePartIndex(34), // (0x1) SlotDebugData { name: "", ty: UInt<1> }, + dest: StatePartIndex(38), // (0x1) SlotDebugData { name: "[1]", ty: UInt<1> }, + src: StatePartIndex(42), // (0x1) SlotDebugData { name: "", ty: UInt<1> }, }, 75: SliceInt { - dest: StatePartIndex(35), // (0x3) SlotDebugData { name: "", ty: UInt<2> }, - src: StatePartIndex(31), // (0xf) SlotDebugData { name: "", ty: UInt<4> }, + dest: StatePartIndex(43), // (0x3) SlotDebugData { name: "", ty: UInt<2> }, + src: StatePartIndex(39), // (0xf) SlotDebugData { name: "", ty: UInt<4> }, start: 2, len: 2, }, 76: CastToSInt { - dest: StatePartIndex(36), // (-0x1) SlotDebugData { name: "", ty: SInt<2> }, - src: StatePartIndex(35), // (0x3) SlotDebugData { name: "", ty: UInt<2> }, + dest: StatePartIndex(44), // (-0x1) SlotDebugData { name: "", ty: SInt<2> }, + src: StatePartIndex(43), // (0x3) SlotDebugData { name: "", ty: UInt<2> }, dest_width: 2, }, 77: Copy { - dest: StatePartIndex(26), // (0x1) SlotDebugData { name: ".a[0]", ty: UInt<1> }, - src: StatePartIndex(29), // (0x1) SlotDebugData { name: "[0]", ty: UInt<1> }, + dest: StatePartIndex(34), // (0x1) SlotDebugData { name: ".a[0]", ty: UInt<1> }, + src: StatePartIndex(37), // (0x1) SlotDebugData { name: "[0]", ty: UInt<1> }, }, 78: Copy { - dest: StatePartIndex(27), // (0x1) SlotDebugData { name: ".a[1]", ty: UInt<1> }, - src: StatePartIndex(30), // (0x1) SlotDebugData { name: "[1]", ty: UInt<1> }, + dest: StatePartIndex(35), // (0x1) SlotDebugData { name: ".a[1]", ty: UInt<1> }, + src: StatePartIndex(38), // (0x1) SlotDebugData { name: "[1]", ty: UInt<1> }, }, 79: Copy { - dest: StatePartIndex(28), // (-0x1) SlotDebugData { name: ".b", ty: SInt<2> }, - src: StatePartIndex(36), // (-0x1) SlotDebugData { name: "", ty: SInt<2> }, + dest: StatePartIndex(36), // (-0x1) SlotDebugData { name: ".b", ty: SInt<2> }, + src: StatePartIndex(44), // (-0x1) SlotDebugData { name: "", ty: SInt<2> }, }, 80: Shl { - dest: StatePartIndex(98), // (0x2) SlotDebugData { name: "", ty: UInt<2> }, - lhs: StatePartIndex(27), // (0x1) SlotDebugData { name: ".a[1]", ty: UInt<1> }, + dest: StatePartIndex(106), // (0x2) SlotDebugData { name: "", ty: UInt<2> }, + lhs: StatePartIndex(35), // (0x1) SlotDebugData { name: ".a[1]", ty: UInt<1> }, rhs: 1, }, 81: Or { - dest: StatePartIndex(99), // (0x3) SlotDebugData { name: "", ty: UInt<2> }, - lhs: StatePartIndex(26), // (0x1) SlotDebugData { name: ".a[0]", ty: UInt<1> }, - rhs: StatePartIndex(98), // (0x2) SlotDebugData { name: "", ty: UInt<2> }, + dest: StatePartIndex(107), // (0x3) SlotDebugData { name: "", ty: UInt<2> }, + lhs: StatePartIndex(34), // (0x1) SlotDebugData { name: ".a[0]", ty: UInt<1> }, + rhs: StatePartIndex(106), // (0x2) SlotDebugData { name: "", ty: UInt<2> }, }, 82: CastToUInt { - dest: StatePartIndex(100), // (0x3) SlotDebugData { name: "", ty: UInt<2> }, - src: StatePartIndex(28), // (-0x1) SlotDebugData { name: ".b", ty: SInt<2> }, + dest: StatePartIndex(108), // (0x3) SlotDebugData { name: "", ty: UInt<2> }, + src: StatePartIndex(36), // (-0x1) SlotDebugData { name: ".b", ty: SInt<2> }, dest_width: 2, }, 83: Shl { - dest: StatePartIndex(101), // (0xc) SlotDebugData { name: "", ty: UInt<4> }, - lhs: StatePartIndex(100), // (0x3) SlotDebugData { name: "", ty: UInt<2> }, + dest: StatePartIndex(109), // (0xc) SlotDebugData { name: "", ty: UInt<4> }, + lhs: StatePartIndex(108), // (0x3) SlotDebugData { name: "", ty: UInt<2> }, rhs: 2, }, 84: Or { - dest: StatePartIndex(102), // (0xf) SlotDebugData { name: "", ty: UInt<4> }, - lhs: StatePartIndex(99), // (0x3) SlotDebugData { name: "", ty: UInt<2> }, - rhs: StatePartIndex(101), // (0xc) SlotDebugData { name: "", ty: UInt<4> }, + dest: StatePartIndex(110), // (0xf) SlotDebugData { name: "", ty: UInt<4> }, + lhs: StatePartIndex(107), // (0x3) SlotDebugData { name: "", ty: UInt<2> }, + rhs: StatePartIndex(109), // (0xc) SlotDebugData { name: "", ty: UInt<4> }, }, - // at: module-XXXXXXXXXX.rs:9:1 + // at: module-XXXXXXXXXX.rs:11:1 85: AndBigWithSmallImmediate { - dest: StatePartIndex(5), // (0x2 2) SlotDebugData { name: "", ty: Enum {A, B, C} }, - lhs: StatePartIndex(15), // (0x3e) SlotDebugData { name: "InstantiatedModule(enums: enums).enums::the_reg", ty: Enum {A, B(Bundle {0: UInt<1>, 1: Bool}), C(Bundle {a: Array, 2>, b: SInt<2>})} }, + dest: StatePartIndex(6), // (0x2 2) SlotDebugData { name: "", ty: Enum {A, B, C} }, + lhs: StatePartIndex(23), // (0x3e) SlotDebugData { name: "InstantiatedModule(enums: enums).enums::the_reg", ty: Enum {A, B(Bundle {0: UInt<1>, 1: Bool}), C(Bundle {a: Array, 2>, b: SInt<2>})} }, rhs: 0x3, }, - // at: module-XXXXXXXXXX.rs:17:1 + // at: module-XXXXXXXXXX.rs:19:1 86: BranchIfSmallNeImmediate { target: 89, - lhs: StatePartIndex(5), // (0x2 2) SlotDebugData { name: "", ty: Enum {A, B, C} }, + lhs: StatePartIndex(6), // (0x2 2) SlotDebugData { name: "", ty: Enum {A, B, C} }, rhs: 0x0, }, - // at: module-XXXXXXXXXX.rs:18:1 + // at: module-XXXXXXXXXX.rs:20:1 87: Copy { dest: StatePartIndex(5), // (0x2) SlotDebugData { name: "InstantiatedModule(enums: enums).enums::which_out", ty: UInt<2> }, - src: StatePartIndex(37), // (0x0) SlotDebugData { name: "", ty: UInt<2> }, - }, - // at: module-XXXXXXXXXX.rs:19:1 - 88: Copy { - dest: StatePartIndex(6), // (0xf) SlotDebugData { name: "InstantiatedModule(enums: enums).enums::data_out", ty: UInt<4> }, - src: StatePartIndex(82), // (0x0) SlotDebugData { name: "", ty: UInt<4> }, - }, - // at: module-XXXXXXXXXX.rs:17:1 - 89: BranchIfSmallNeImmediate { - target: 93, - lhs: StatePartIndex(5), // (0x2 2) SlotDebugData { name: "", ty: Enum {A, B, C} }, - rhs: 0x1, - }, - // at: module-XXXXXXXXXX.rs:20:1 - 90: Copy { - dest: StatePartIndex(5), // (0x2) SlotDebugData { name: "InstantiatedModule(enums: enums).enums::which_out", ty: UInt<2> }, - src: StatePartIndex(39), // (0x1) SlotDebugData { name: "", ty: UInt<2> }, + src: StatePartIndex(45), // (0x0) SlotDebugData { name: "", ty: UInt<2> }, }, // at: module-XXXXXXXXXX.rs:21:1 - 91: Copy { + 88: Copy { dest: StatePartIndex(6), // (0xf) SlotDebugData { name: "InstantiatedModule(enums: enums).enums::data_out", ty: UInt<4> }, - src: StatePartIndex(86), // (0x3) SlotDebugData { name: "", ty: UInt<4> }, + src: StatePartIndex(90), // (0x0) SlotDebugData { name: "", ty: UInt<4> }, + }, + // at: module-XXXXXXXXXX.rs:19:1 + 89: BranchIfSmallNeImmediate { + target: 93, + lhs: StatePartIndex(6), // (0x2 2) SlotDebugData { name: "", ty: Enum {A, B, C} }, + rhs: 0x1, }, // at: module-XXXXXXXXXX.rs:22:1 - 92: Copy { - dest: StatePartIndex(7), // (0x0) SlotDebugData { name: "InstantiatedModule(enums: enums).enums::b_out", ty: Enum {HdlNone, HdlSome(Bundle {0: UInt<1>, 1: Bool})} }, - src: StatePartIndex(97), // (0x7) SlotDebugData { name: "", ty: Enum {HdlNone, HdlSome(Bundle {0: UInt<1>, 1: Bool})} }, - }, - // at: module-XXXXXXXXXX.rs:17:1 - 93: BranchIfSmallNeImmediate { - target: 96, - lhs: StatePartIndex(5), // (0x2 2) SlotDebugData { name: "", ty: Enum {A, B, C} }, - rhs: 0x2, + 90: Copy { + dest: StatePartIndex(5), // (0x2) SlotDebugData { name: "InstantiatedModule(enums: enums).enums::which_out", ty: UInt<2> }, + src: StatePartIndex(47), // (0x1) SlotDebugData { name: "", ty: UInt<2> }, }, // at: module-XXXXXXXXXX.rs:23:1 - 94: Copy { - dest: StatePartIndex(5), // (0x2) SlotDebugData { name: "InstantiatedModule(enums: enums).enums::which_out", ty: UInt<2> }, - src: StatePartIndex(62), // (0x2) SlotDebugData { name: "", ty: UInt<2> }, + 91: Copy { + dest: StatePartIndex(6), // (0xf) SlotDebugData { name: "InstantiatedModule(enums: enums).enums::data_out", ty: UInt<4> }, + src: StatePartIndex(94), // (0x3) SlotDebugData { name: "", ty: UInt<4> }, }, // at: module-XXXXXXXXXX.rs:24:1 + 92: Copy { + dest: StatePartIndex(7), // (0x0) SlotDebugData { name: "InstantiatedModule(enums: enums).enums::b_out", ty: Enum {HdlNone, HdlSome(Bundle {0: UInt<1>, 1: Bool})} }, + src: StatePartIndex(105), // (0x7) SlotDebugData { name: "", ty: Enum {HdlNone, HdlSome(Bundle {0: UInt<1>, 1: Bool})} }, + }, + // at: module-XXXXXXXXXX.rs:19:1 + 93: BranchIfSmallNeImmediate { + target: 96, + lhs: StatePartIndex(6), // (0x2 2) SlotDebugData { name: "", ty: Enum {A, B, C} }, + rhs: 0x2, + }, + // at: module-XXXXXXXXXX.rs:25:1 + 94: Copy { + dest: StatePartIndex(5), // (0x2) SlotDebugData { name: "InstantiatedModule(enums: enums).enums::which_out", ty: UInt<2> }, + src: StatePartIndex(70), // (0x2) SlotDebugData { name: "", ty: UInt<2> }, + }, + // at: module-XXXXXXXXXX.rs:26:1 95: Copy { dest: StatePartIndex(6), // (0xf) SlotDebugData { name: "InstantiatedModule(enums: enums).enums::data_out", ty: UInt<4> }, - src: StatePartIndex(102), // (0xf) SlotDebugData { name: "", ty: UInt<4> }, + src: StatePartIndex(110), // (0xf) SlotDebugData { name: "", ty: UInt<4> }, }, - // at: module-XXXXXXXXXX.rs:9:1 + // at: module-XXXXXXXXXX.rs:11:1 96: IsNonZeroDestIsSmall { - dest: StatePartIndex(4), // (0x0 0) SlotDebugData { name: "", ty: Bool }, + dest: StatePartIndex(5), // (0x0 0) SlotDebugData { name: "", ty: Bool }, src: StatePartIndex(1), // (0x0) SlotDebugData { name: "InstantiatedModule(enums: enums).enums::cd.rst", ty: SyncReset }, }, // at: module-XXXXXXXXXX.rs:1:1 97: Const { - dest: StatePartIndex(17), // (0x0) SlotDebugData { name: "", ty: UInt<6> }, + dest: StatePartIndex(25), // (0x0) SlotDebugData { name: "", ty: UInt<6> }, value: 0x0, }, 98: Copy { - dest: StatePartIndex(18), // (0x0) SlotDebugData { name: "", ty: Enum {A, B(Bundle {0: UInt<1>, 1: Bool}), C(Bundle {a: Array, 2>, b: SInt<2>})} }, - src: StatePartIndex(17), // (0x0) SlotDebugData { name: "", ty: UInt<6> }, + dest: StatePartIndex(26), // (0x0) SlotDebugData { name: "", ty: Enum {A, B(Bundle {0: UInt<1>, 1: Bool}), C(Bundle {a: Array, 2>, b: SInt<2>})} }, + src: StatePartIndex(25), // (0x0) SlotDebugData { name: "", ty: UInt<6> }, }, - // at: module-XXXXXXXXXX.rs:10:1 + // at: module-XXXXXXXXXX.rs:12:1 99: BranchIfZero { target: 107, value: StatePartIndex(2), // (0x1) SlotDebugData { name: "InstantiatedModule(enums: enums).enums::en", ty: Bool }, }, - // at: module-XXXXXXXXXX.rs:11:1 + // at: module-XXXXXXXXXX.rs:13:1 100: BranchIfZero { target: 102, - value: StatePartIndex(38), // (0x0) SlotDebugData { name: "", ty: Bool }, - }, - // at: module-XXXXXXXXXX.rs:12:1 - 101: Copy { - dest: StatePartIndex(16), // (0x3e) SlotDebugData { name: "InstantiatedModule(enums: enums).enums::the_reg$next", ty: Enum {A, B(Bundle {0: UInt<1>, 1: Bool}), C(Bundle {a: Array, 2>, b: SInt<2>})} }, - src: StatePartIndex(18), // (0x0) SlotDebugData { name: "", ty: Enum {A, B(Bundle {0: UInt<1>, 1: Bool}), C(Bundle {a: Array, 2>, b: SInt<2>})} }, - }, - // at: module-XXXXXXXXXX.rs:11:1 - 102: BranchIfNonZero { - target: 107, - value: StatePartIndex(38), // (0x0) SlotDebugData { name: "", ty: Bool }, - }, - // at: module-XXXXXXXXXX.rs:13:1 - 103: BranchIfZero { - target: 105, - value: StatePartIndex(40), // (0x0) SlotDebugData { name: "", ty: Bool }, + value: StatePartIndex(46), // (0x0) SlotDebugData { name: "", ty: Bool }, }, // at: module-XXXXXXXXXX.rs:14:1 - 104: Copy { - dest: StatePartIndex(16), // (0x3e) SlotDebugData { name: "InstantiatedModule(enums: enums).enums::the_reg$next", ty: Enum {A, B(Bundle {0: UInt<1>, 1: Bool}), C(Bundle {a: Array, 2>, b: SInt<2>})} }, - src: StatePartIndex(57), // (0xd) SlotDebugData { name: "", ty: Enum {A, B(Bundle {0: UInt<1>, 1: Bool}), C(Bundle {a: Array, 2>, b: SInt<2>})} }, + 101: Copy { + dest: StatePartIndex(24), // (0x3e) SlotDebugData { name: "InstantiatedModule(enums: enums).enums::the_reg$next", ty: Enum {A, B(Bundle {0: UInt<1>, 1: Bool}), C(Bundle {a: Array, 2>, b: SInt<2>})} }, + src: StatePartIndex(26), // (0x0) SlotDebugData { name: "", ty: Enum {A, B(Bundle {0: UInt<1>, 1: Bool}), C(Bundle {a: Array, 2>, b: SInt<2>})} }, }, // at: module-XXXXXXXXXX.rs:13:1 - 105: BranchIfNonZero { + 102: BranchIfNonZero { target: 107, - value: StatePartIndex(40), // (0x0) SlotDebugData { name: "", ty: Bool }, + value: StatePartIndex(46), // (0x0) SlotDebugData { name: "", ty: Bool }, }, // at: module-XXXXXXXXXX.rs:15:1 - 106: Copy { - dest: StatePartIndex(16), // (0x3e) SlotDebugData { name: "InstantiatedModule(enums: enums).enums::the_reg$next", ty: Enum {A, B(Bundle {0: UInt<1>, 1: Bool}), C(Bundle {a: Array, 2>, b: SInt<2>})} }, - src: StatePartIndex(79), // (0x3e) SlotDebugData { name: "", ty: Enum {A, B(Bundle {0: UInt<1>, 1: Bool}), C(Bundle {a: Array, 2>, b: SInt<2>})} }, + 103: BranchIfZero { + target: 105, + value: StatePartIndex(48), // (0x0) SlotDebugData { name: "", ty: Bool }, }, - // at: module-XXXXXXXXXX.rs:9:1 + // at: module-XXXXXXXXXX.rs:16:1 + 104: Copy { + dest: StatePartIndex(24), // (0x3e) SlotDebugData { name: "InstantiatedModule(enums: enums).enums::the_reg$next", ty: Enum {A, B(Bundle {0: UInt<1>, 1: Bool}), C(Bundle {a: Array, 2>, b: SInt<2>})} }, + src: StatePartIndex(65), // (0xd) SlotDebugData { name: "", ty: Enum {A, B(Bundle {0: UInt<1>, 1: Bool}), C(Bundle {a: Array, 2>, b: SInt<2>})} }, + }, + // at: module-XXXXXXXXXX.rs:15:1 + 105: BranchIfNonZero { + target: 107, + value: StatePartIndex(48), // (0x0) SlotDebugData { name: "", ty: Bool }, + }, + // at: module-XXXXXXXXXX.rs:17:1 + 106: Copy { + dest: StatePartIndex(24), // (0x3e) SlotDebugData { name: "InstantiatedModule(enums: enums).enums::the_reg$next", ty: Enum {A, B(Bundle {0: UInt<1>, 1: Bool}), C(Bundle {a: Array, 2>, b: SInt<2>})} }, + src: StatePartIndex(87), // (0x3e) SlotDebugData { name: "", ty: Enum {A, B(Bundle {0: UInt<1>, 1: Bool}), C(Bundle {a: Array, 2>, b: SInt<2>})} }, + }, + // at: module-XXXXXXXXXX.rs:11:1 107: IsNonZeroDestIsSmall { - dest: StatePartIndex(3), // (0x1 1) SlotDebugData { name: "", ty: Bool }, + dest: StatePartIndex(4), // (0x1 1) SlotDebugData { name: "", ty: Bool }, src: StatePartIndex(0), // (0x1) SlotDebugData { name: "InstantiatedModule(enums: enums).enums::cd.clk", ty: Clock }, }, 108: AndSmall { - dest: StatePartIndex(2), // (0x0 0) SlotDebugData { name: "", ty: Bool }, - lhs: StatePartIndex(3), // (0x1 1) SlotDebugData { name: "", ty: Bool }, - rhs: StatePartIndex(1), // (0x0 0) SlotDebugData { name: "", ty: Bool }, + dest: StatePartIndex(3), // (0x0 0) SlotDebugData { name: "", ty: Bool }, + lhs: StatePartIndex(4), // (0x1 1) SlotDebugData { name: "", ty: Bool }, + rhs: StatePartIndex(2), // (0x0 0) SlotDebugData { name: "", ty: Bool }, + }, + // at: module-XXXXXXXXXX.rs:10:1 + 109: Copy { + dest: StatePartIndex(15), // (0x0) SlotDebugData { name: "InstantiatedModule(enums: enums).enums::b2_out", ty: Enum {HdlNone, HdlSome(Bundle {0: UInt<1>, 1: Bool})} }, + src: StatePartIndex(7), // (0x0) SlotDebugData { name: "InstantiatedModule(enums: enums).enums::b_out", ty: Enum {HdlNone, HdlSome(Bundle {0: UInt<1>, 1: Bool})} }, }, // at: module-XXXXXXXXXX.rs:1:1 - 109: Copy { + 110: Copy { + dest: StatePartIndex(18), // (0x0) SlotDebugData { name: "", ty: UInt<3> }, + src: StatePartIndex(15), // (0x0) SlotDebugData { name: "InstantiatedModule(enums: enums).enums::b2_out", ty: Enum {HdlNone, HdlSome(Bundle {0: UInt<1>, 1: Bool})} }, + }, + 111: SliceInt { + dest: StatePartIndex(19), // (0x0) SlotDebugData { name: "", ty: UInt<2> }, + src: StatePartIndex(18), // (0x0) SlotDebugData { name: "", ty: UInt<3> }, + start: 1, + len: 2, + }, + 112: SliceInt { + dest: StatePartIndex(20), // (0x0) SlotDebugData { name: "", ty: UInt<1> }, + src: StatePartIndex(19), // (0x0) SlotDebugData { name: "", ty: UInt<2> }, + start: 0, + len: 1, + }, + 113: SliceInt { + dest: StatePartIndex(21), // (0x0) SlotDebugData { name: "", ty: UInt<1> }, + src: StatePartIndex(19), // (0x0) SlotDebugData { name: "", ty: UInt<2> }, + start: 1, + len: 1, + }, + 114: Copy { + dest: StatePartIndex(22), // (0x0) SlotDebugData { name: "", ty: Bool }, + src: StatePartIndex(21), // (0x0) SlotDebugData { name: "", ty: UInt<1> }, + }, + 115: Copy { + dest: StatePartIndex(16), // (0x0) SlotDebugData { name: ".0", ty: UInt<1> }, + src: StatePartIndex(20), // (0x0) SlotDebugData { name: "", ty: UInt<1> }, + }, + 116: Copy { + dest: StatePartIndex(17), // (0x0) SlotDebugData { name: ".1", ty: Bool }, + src: StatePartIndex(22), // (0x0) SlotDebugData { name: "", ty: Bool }, + }, + // at: module-XXXXXXXXXX.rs:9:1 + 117: AndBigWithSmallImmediate { + dest: StatePartIndex(1), // (0x0 0) SlotDebugData { name: "", ty: Enum {HdlNone, HdlSome} }, + lhs: StatePartIndex(15), // (0x0) SlotDebugData { name: "InstantiatedModule(enums: enums).enums::b2_out", ty: Enum {HdlNone, HdlSome(Bundle {0: UInt<1>, 1: Bool})} }, + rhs: 0x1, + }, + // at: module-XXXXXXXXXX.rs:1:1 + 118: Copy { dest: StatePartIndex(10), // (0x0) SlotDebugData { name: "", ty: UInt<3> }, src: StatePartIndex(7), // (0x0) SlotDebugData { name: "InstantiatedModule(enums: enums).enums::b_out", ty: Enum {HdlNone, HdlSome(Bundle {0: UInt<1>, 1: Bool})} }, }, - 110: SliceInt { + 119: SliceInt { dest: StatePartIndex(11), // (0x0) SlotDebugData { name: "", ty: UInt<2> }, src: StatePartIndex(10), // (0x0) SlotDebugData { name: "", ty: UInt<3> }, start: 1, len: 2, }, - 111: SliceInt { + 120: SliceInt { dest: StatePartIndex(12), // (0x0) SlotDebugData { name: "", ty: UInt<1> }, src: StatePartIndex(11), // (0x0) SlotDebugData { name: "", ty: UInt<2> }, start: 0, len: 1, }, - 112: SliceInt { + 121: SliceInt { dest: StatePartIndex(13), // (0x0) SlotDebugData { name: "", ty: UInt<1> }, src: StatePartIndex(11), // (0x0) SlotDebugData { name: "", ty: UInt<2> }, start: 1, len: 1, }, - 113: Copy { + 122: Copy { dest: StatePartIndex(14), // (0x0) SlotDebugData { name: "", ty: Bool }, src: StatePartIndex(13), // (0x0) SlotDebugData { name: "", ty: UInt<1> }, }, - 114: Copy { + 123: Copy { dest: StatePartIndex(8), // (0x0) SlotDebugData { name: ".0", ty: UInt<1> }, src: StatePartIndex(12), // (0x0) SlotDebugData { name: "", ty: UInt<1> }, }, - 115: Copy { + 124: Copy { dest: StatePartIndex(9), // (0x0) SlotDebugData { name: ".1", ty: Bool }, src: StatePartIndex(14), // (0x0) SlotDebugData { name: "", ty: Bool }, }, // at: module-XXXXXXXXXX.rs:8:1 - 116: AndBigWithSmallImmediate { + 125: AndBigWithSmallImmediate { dest: StatePartIndex(0), // (0x0 0) SlotDebugData { name: "", ty: Enum {HdlNone, HdlSome} }, lhs: StatePartIndex(7), // (0x0) SlotDebugData { name: "InstantiatedModule(enums: enums).enums::b_out", ty: Enum {HdlNone, HdlSome(Bundle {0: UInt<1>, 1: Bool})} }, rhs: 0x1, }, - // at: module-XXXXXXXXXX.rs:9:1 - 117: BranchIfSmallZero { - target: 122, - value: StatePartIndex(2), // (0x0 0) SlotDebugData { name: "", ty: Bool }, + // at: module-XXXXXXXXXX.rs:11:1 + 126: BranchIfSmallZero { + target: 131, + value: StatePartIndex(3), // (0x0 0) SlotDebugData { name: "", ty: Bool }, }, - 118: BranchIfSmallNonZero { - target: 121, - value: StatePartIndex(4), // (0x0 0) SlotDebugData { name: "", ty: Bool }, + 127: BranchIfSmallNonZero { + target: 130, + value: StatePartIndex(5), // (0x0 0) SlotDebugData { name: "", ty: Bool }, }, - 119: Copy { - dest: StatePartIndex(15), // (0x3e) SlotDebugData { name: "InstantiatedModule(enums: enums).enums::the_reg", ty: Enum {A, B(Bundle {0: UInt<1>, 1: Bool}), C(Bundle {a: Array, 2>, b: SInt<2>})} }, - src: StatePartIndex(16), // (0x3e) SlotDebugData { name: "InstantiatedModule(enums: enums).enums::the_reg$next", ty: Enum {A, B(Bundle {0: UInt<1>, 1: Bool}), C(Bundle {a: Array, 2>, b: SInt<2>})} }, + 128: Copy { + dest: StatePartIndex(23), // (0x3e) SlotDebugData { name: "InstantiatedModule(enums: enums).enums::the_reg", ty: Enum {A, B(Bundle {0: UInt<1>, 1: Bool}), C(Bundle {a: Array, 2>, b: SInt<2>})} }, + src: StatePartIndex(24), // (0x3e) SlotDebugData { name: "InstantiatedModule(enums: enums).enums::the_reg$next", ty: Enum {A, B(Bundle {0: UInt<1>, 1: Bool}), C(Bundle {a: Array, 2>, b: SInt<2>})} }, }, - 120: Branch { - target: 122, + 129: Branch { + target: 131, }, - 121: Copy { - dest: StatePartIndex(15), // (0x3e) SlotDebugData { name: "InstantiatedModule(enums: enums).enums::the_reg", ty: Enum {A, B(Bundle {0: UInt<1>, 1: Bool}), C(Bundle {a: Array, 2>, b: SInt<2>})} }, - src: StatePartIndex(18), // (0x0) SlotDebugData { name: "", ty: Enum {A, B(Bundle {0: UInt<1>, 1: Bool}), C(Bundle {a: Array, 2>, b: SInt<2>})} }, + 130: Copy { + dest: StatePartIndex(23), // (0x3e) SlotDebugData { name: "InstantiatedModule(enums: enums).enums::the_reg", ty: Enum {A, B(Bundle {0: UInt<1>, 1: Bool}), C(Bundle {a: Array, 2>, b: SInt<2>})} }, + src: StatePartIndex(26), // (0x0) SlotDebugData { name: "", ty: Enum {A, B(Bundle {0: UInt<1>, 1: Bool}), C(Bundle {a: Array, 2>, b: SInt<2>})} }, }, - 122: XorSmallImmediate { - dest: StatePartIndex(1), // (0x0 0) SlotDebugData { name: "", ty: Bool }, - lhs: StatePartIndex(3), // (0x1 1) SlotDebugData { name: "", ty: Bool }, + 131: XorSmallImmediate { + dest: StatePartIndex(2), // (0x0 0) SlotDebugData { name: "", ty: Bool }, + lhs: StatePartIndex(4), // (0x1 1) SlotDebugData { name: "", ty: Bool }, rhs: 0x1, }, // at: module-XXXXXXXXXX.rs:1:1 - 123: Return, + 132: Return, ], .. }, - pc: 123, + pc: 132, memory_write_log: [], memories: StatePart { value: [], @@ -1095,6 +1189,7 @@ Simulation { 0, 0, 0, + 0, 1, 0, 2, @@ -1117,6 +1212,14 @@ Simulation { 0, 0, 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, 62, 62, 0, @@ -1207,6 +1310,9 @@ Simulation { 15, ], }, + sim_only_slots: StatePart { + value: [], + }, }, io: Instance { name: ::enums, @@ -1215,389 +1321,142 @@ Simulation { .. }, }, - uninitialized_inputs: {}, - io_targets: { - Instance { - name: ::enums, - instantiated: Module { - name: enums, - .. - }, - }.b_out: CompiledValue { - layout: CompiledTypeLayout { - ty: Enum { - HdlNone, - HdlSome(Bundle {0: UInt<1>, 1: Bool}), + main_module: SimulationModuleState { + base_targets: [ + Instance { + name: ::enums, + instantiated: Module { + name: enums, + .. }, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "InstantiatedModule(enums: enums).enums::b_out", - ty: Enum { - HdlNone, - HdlSome(Bundle {0: UInt<1>, 1: Bool}), - }, - }, - ], - .. - }, + }.cd, + Instance { + name: ::enums, + instantiated: Module { + name: enums, + .. }, - body: Scalar, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 7, len: 1 }, - }, - write: None, - }, - Instance { - name: ::enums, - instantiated: Module { - name: enums, - .. - }, - }.cd: CompiledValue { - layout: CompiledTypeLayout { - ty: Bundle { - /* offset = 0 */ - clk: Clock, - /* offset = 1 */ - rst: SyncReset, - }, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 2, - debug_data: [ - SlotDebugData { - name: "InstantiatedModule(enums: enums).enums::cd.clk", - ty: Clock, - }, - SlotDebugData { - name: "InstantiatedModule(enums: enums).enums::cd.rst", - ty: SyncReset, - }, - ], - .. - }, - }, - body: Bundle { - fields: [ - CompiledBundleField { - offset: TypeIndex { - small_slots: StatePartIndex(0), - big_slots: StatePartIndex(0), - }, - ty: CompiledTypeLayout { - ty: Clock, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: Clock, - }, - ], - .. - }, - }, - body: Scalar, - }, - }, - CompiledBundleField { - offset: TypeIndex { - small_slots: StatePartIndex(0), - big_slots: StatePartIndex(1), - }, - ty: CompiledTypeLayout { - ty: SyncReset, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: SyncReset, - }, - ], - .. - }, - }, - body: Scalar, - }, - }, - ], - }, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 0, len: 2 }, - }, - write: None, - }, - Instance { - name: ::enums, - instantiated: Module { - name: enums, - .. - }, - }.cd.clk: CompiledValue { - layout: CompiledTypeLayout { - ty: Clock, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: Clock, - }, - ], - .. - }, - }, - body: Scalar, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 0, len: 1 }, - }, - write: None, - }, - Instance { - name: ::enums, - instantiated: Module { - name: enums, - .. - }, - }.cd.rst: CompiledValue { - layout: CompiledTypeLayout { - ty: SyncReset, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: SyncReset, - }, - ], - .. - }, - }, - body: Scalar, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 1, len: 1 }, - }, - write: None, - }, - Instance { - name: ::enums, - instantiated: Module { - name: enums, - .. - }, - }.data_in: CompiledValue { - layout: CompiledTypeLayout { - ty: UInt<4>, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "InstantiatedModule(enums: enums).enums::data_in", - ty: UInt<4>, - }, - ], - .. - }, - }, - body: Scalar, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 4, len: 1 }, - }, - write: None, - }, - Instance { - name: ::enums, - instantiated: Module { - name: enums, - .. - }, - }.data_out: CompiledValue { - layout: CompiledTypeLayout { - ty: UInt<4>, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "InstantiatedModule(enums: enums).enums::data_out", - ty: UInt<4>, - }, - ], - .. - }, - }, - body: Scalar, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 6, len: 1 }, - }, - write: None, - }, - Instance { - name: ::enums, - instantiated: Module { - name: enums, - .. - }, - }.en: CompiledValue { - layout: CompiledTypeLayout { - ty: Bool, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "InstantiatedModule(enums: enums).enums::en", - ty: Bool, - }, - ], - .. - }, - }, - body: Scalar, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 2, len: 1 }, - }, - write: None, - }, - Instance { - name: ::enums, - instantiated: Module { - name: enums, - .. - }, - }.which_in: CompiledValue { - layout: CompiledTypeLayout { - ty: UInt<2>, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "InstantiatedModule(enums: enums).enums::which_in", - ty: UInt<2>, - }, - ], - .. - }, - }, - body: Scalar, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 3, len: 1 }, - }, - write: None, - }, - Instance { - name: ::enums, - instantiated: Module { - name: enums, - .. - }, - }.which_out: CompiledValue { - layout: CompiledTypeLayout { - ty: UInt<2>, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "InstantiatedModule(enums: enums).enums::which_out", - ty: UInt<2>, - }, - ], - .. - }, - }, - body: Scalar, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 5, len: 1 }, - }, - write: None, + }.en, + Instance { + name: ::enums, + instantiated: Module { + name: enums, + .. + }, + }.which_in, + Instance { + name: ::enums, + instantiated: Module { + name: enums, + .. + }, + }.data_in, + Instance { + name: ::enums, + instantiated: Module { + name: enums, + .. + }, + }.which_out, + Instance { + name: ::enums, + instantiated: Module { + name: enums, + .. + }, + }.data_out, + Instance { + name: ::enums, + instantiated: Module { + name: enums, + .. + }, + }.b_out, + Instance { + name: ::enums, + instantiated: Module { + name: enums, + .. + }, + }.b2_out, + ], + uninitialized_ios: {}, + io_targets: { + Instance { + name: ::enums, + instantiated: Module { + name: enums, + .. + }, + }.b2_out, + Instance { + name: ::enums, + instantiated: Module { + name: enums, + .. + }, + }.b_out, + Instance { + name: ::enums, + instantiated: Module { + name: enums, + .. + }, + }.cd, + Instance { + name: ::enums, + instantiated: Module { + name: enums, + .. + }, + }.cd.clk, + Instance { + name: ::enums, + instantiated: Module { + name: enums, + .. + }, + }.cd.rst, + Instance { + name: ::enums, + instantiated: Module { + name: enums, + .. + }, + }.data_in, + Instance { + name: ::enums, + instantiated: Module { + name: enums, + .. + }, + }.data_out, + Instance { + name: ::enums, + instantiated: Module { + name: enums, + .. + }, + }.en, + Instance { + name: ::enums, + instantiated: Module { + name: enums, + .. + }, + }.which_in, + Instance { + name: ::enums, + instantiated: Module { + name: enums, + .. + }, + }.which_out, }, + did_initial_settle: true, }, - made_initial_step: true, - needs_settle: false, + extern_modules: [], + state_ready_to_run: false, trace_decls: TraceModule { name: "enums", children: [ @@ -1737,23 +1596,22 @@ Simulation { }, flow: Sink, }, - TraceReg { - name: "the_reg", + TraceModuleIO { + name: "b2_out", child: TraceEnumWithFields { - name: "the_reg", + name: "b2_out", discriminant: TraceEnumDiscriminant { location: TraceScalarId(10), name: "$tag", ty: Enum { - A, - B(Bundle {0: UInt<1>, 1: Bool}), - C(Bundle {a: Array, 2>, b: SInt<2>}), + HdlNone, + HdlSome(Bundle {0: UInt<1>, 1: Bool}), }, - flow: Duplex, + flow: Sink, }, non_empty_fields: [ TraceBundle { - name: "B", + name: "HdlSome", fields: [ TraceUInt { location: TraceScalarId(11), @@ -1775,6 +1633,57 @@ Simulation { }, flow: Source, }, + ], + ty: Enum { + HdlNone, + HdlSome(Bundle {0: UInt<1>, 1: Bool}), + }, + flow: Sink, + }, + ty: Enum { + HdlNone, + HdlSome(Bundle {0: UInt<1>, 1: Bool}), + }, + flow: Sink, + }, + TraceReg { + name: "the_reg", + child: TraceEnumWithFields { + name: "the_reg", + discriminant: TraceEnumDiscriminant { + location: TraceScalarId(13), + name: "$tag", + ty: Enum { + A, + B(Bundle {0: UInt<1>, 1: Bool}), + C(Bundle {a: Array, 2>, b: SInt<2>}), + }, + flow: Duplex, + }, + non_empty_fields: [ + TraceBundle { + name: "B", + fields: [ + TraceUInt { + location: TraceScalarId(14), + name: "0", + ty: UInt<1>, + flow: Source, + }, + TraceBool { + location: TraceScalarId(15), + name: "1", + flow: Source, + }, + ], + ty: Bundle { + /* offset = 0 */ + 0: UInt<1>, + /* offset = 1 */ + 1: Bool, + }, + flow: Source, + }, TraceBundle { name: "C", fields: [ @@ -1782,13 +1691,13 @@ Simulation { name: "a", elements: [ TraceUInt { - location: TraceScalarId(13), + location: TraceScalarId(16), name: "[0]", ty: UInt<1>, flow: Source, }, TraceUInt { - location: TraceScalarId(14), + location: TraceScalarId(17), name: "[1]", ty: UInt<1>, flow: Source, @@ -1798,7 +1707,7 @@ Simulation { flow: Source, }, TraceSInt { - location: TraceScalarId(15), + location: TraceScalarId(18), name: "b", ty: SInt<2>, flow: Source, @@ -1921,7 +1830,36 @@ Simulation { SimTrace { id: TraceScalarId(10), kind: EnumDiscriminant { - index: StatePartIndex(5), + index: StatePartIndex(1), + ty: Enum { + HdlNone, + HdlSome(Bundle {0: UInt<1>, 1: Bool}), + }, + }, + state: 0x0, + last_state: 0x0, + }, + SimTrace { + id: TraceScalarId(11), + kind: BigUInt { + index: StatePartIndex(16), + ty: UInt<1>, + }, + state: 0x0, + last_state: 0x0, + }, + SimTrace { + id: TraceScalarId(12), + kind: BigBool { + index: StatePartIndex(17), + }, + state: 0x0, + last_state: 0x0, + }, + SimTrace { + id: TraceScalarId(13), + kind: EnumDiscriminant { + index: StatePartIndex(6), ty: Enum { A, B(Bundle {0: UInt<1>, 1: Bool}), @@ -1931,32 +1869,6 @@ Simulation { state: 0x2, last_state: 0x2, }, - SimTrace { - id: TraceScalarId(11), - kind: BigUInt { - index: StatePartIndex(19), - ty: UInt<1>, - }, - state: 0x1, - last_state: 0x1, - }, - SimTrace { - id: TraceScalarId(12), - kind: BigBool { - index: StatePartIndex(20), - }, - state: 0x1, - last_state: 0x1, - }, - SimTrace { - id: TraceScalarId(13), - kind: BigUInt { - index: StatePartIndex(26), - ty: UInt<1>, - }, - state: 0x1, - last_state: 0x1, - }, SimTrace { id: TraceScalarId(14), kind: BigUInt { @@ -1968,8 +1880,34 @@ Simulation { }, SimTrace { id: TraceScalarId(15), - kind: BigSInt { + kind: BigBool { index: StatePartIndex(28), + }, + state: 0x1, + last_state: 0x1, + }, + SimTrace { + id: TraceScalarId(16), + kind: BigUInt { + index: StatePartIndex(34), + ty: UInt<1>, + }, + state: 0x1, + last_state: 0x1, + }, + SimTrace { + id: TraceScalarId(17), + kind: BigUInt { + index: StatePartIndex(35), + ty: UInt<1>, + }, + state: 0x1, + last_state: 0x1, + }, + SimTrace { + id: TraceScalarId(18), + kind: BigSInt { + index: StatePartIndex(36), ty: SInt<2>, }, state: 0x3, @@ -1988,7 +1926,7 @@ Simulation { ], instant: 16 μs, clocks_triggered: [ - StatePartIndex(2), + StatePartIndex(3), ], .. } \ No newline at end of file diff --git a/crates/fayalite/tests/sim/expected/enums.vcd b/crates/fayalite/tests/sim/expected/enums.vcd index 07cbd32..aff867b 100644 --- a/crates/fayalite/tests/sim/expected/enums.vcd +++ b/crates/fayalite/tests/sim/expected/enums.vcd @@ -16,18 +16,25 @@ $var wire 1 ) \0 $end $var wire 1 * \1 $end $upscope $end $upscope $end -$scope struct the_reg $end +$scope struct b2_out $end $var string 1 + \$tag $end +$scope struct HdlSome $end +$var wire 1 , \0 $end +$var wire 1 - \1 $end +$upscope $end +$upscope $end +$scope struct the_reg $end +$var string 1 . \$tag $end $scope struct B $end -$var reg 1 , \0 $end -$var reg 1 - \1 $end +$var reg 1 / \0 $end +$var reg 1 0 \1 $end $upscope $end $scope struct C $end $scope struct a $end -$var reg 1 . \[0] $end -$var reg 1 / \[1] $end +$var reg 1 1 \[0] $end +$var reg 1 2 \[1] $end $upscope $end -$var reg 2 0 b $end +$var reg 2 3 b $end $upscope $end $upscope $end $upscope $end @@ -43,12 +50,15 @@ b0 ' sHdlNone\x20(0) ( 0) 0* -sA\x20(0) + +sHdlNone\x20(0) + 0, 0- -0. +sA\x20(0) . 0/ -b0 0 +00 +01 +02 +b0 3 $end #1000000 1! @@ -66,7 +76,8 @@ b1 $ 1! b1 & sHdlSome\x20(1) ( -sB\x20(1) + +sHdlSome\x20(1) + +sB\x20(1) . #6000000 0# b0 $ @@ -85,8 +96,10 @@ b11 ' 1* 1, 1- -1. 1/ +10 +11 +12 #10000000 0! #11000000 @@ -101,8 +114,11 @@ b1111 ' sHdlNone\x20(0) ( 0) 0* -sC\x20(2) + -b11 0 +sHdlNone\x20(0) + +0, +0- +sC\x20(2) . +b11 3 #14000000 0! #15000000 diff --git a/crates/fayalite/tests/sim/expected/extern_module.txt b/crates/fayalite/tests/sim/expected/extern_module.txt new file mode 100644 index 0000000..e09a767 --- /dev/null +++ b/crates/fayalite/tests/sim/expected/extern_module.txt @@ -0,0 +1,253 @@ +Simulation { + state: State { + insns: Insns { + state_layout: StateLayout { + ty: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 2, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(extern_module: extern_module).extern_module::i", + ty: Bool, + }, + SlotDebugData { + name: "InstantiatedModule(extern_module: extern_module).extern_module::o", + ty: Bool, + }, + ], + .. + }, + sim_only_slots: StatePartLayout { + len: 0, + debug_data: [], + layout_data: [], + .. + }, + }, + memories: StatePartLayout { + len: 0, + debug_data: [], + layout_data: [], + .. + }, + }, + insns: [ + // at: module-XXXXXXXXXX.rs:1:1 + 0: Return, + ], + .. + }, + pc: 0, + memory_write_log: [], + memories: StatePart { + value: [], + }, + small_slots: StatePart { + value: [], + }, + big_slots: StatePart { + value: [ + 1, + 1, + ], + }, + sim_only_slots: StatePart { + value: [], + }, + }, + io: Instance { + name: ::extern_module, + instantiated: Module { + name: extern_module, + .. + }, + }, + main_module: SimulationModuleState { + base_targets: [ + Instance { + name: ::extern_module, + instantiated: Module { + name: extern_module, + .. + }, + }.i, + Instance { + name: ::extern_module, + instantiated: Module { + name: extern_module, + .. + }, + }.o, + ], + uninitialized_ios: {}, + io_targets: { + Instance { + name: ::extern_module, + instantiated: Module { + name: extern_module, + .. + }, + }.i, + Instance { + name: ::extern_module, + instantiated: Module { + name: extern_module, + .. + }, + }.o, + }, + did_initial_settle: true, + }, + extern_modules: [ + SimulationExternModuleState { + module_state: SimulationModuleState { + base_targets: [ + ModuleIO { + name: extern_module::i, + is_input: true, + ty: Bool, + .. + }, + ModuleIO { + name: extern_module::o, + is_input: false, + ty: Bool, + .. + }, + ], + uninitialized_ios: {}, + io_targets: { + ModuleIO { + name: extern_module::i, + is_input: true, + ty: Bool, + .. + }, + ModuleIO { + name: extern_module::o, + is_input: false, + ty: Bool, + .. + }, + }, + did_initial_settle: true, + }, + 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: ..., + }, + sim_io_to_generator_map: { + ModuleIO { + name: extern_module::i, + is_input: true, + ty: Bool, + .. + }: ModuleIO { + name: extern_module::i, + is_input: true, + ty: Bool, + .. + }, + ModuleIO { + name: extern_module::o, + is_input: false, + ty: Bool, + .. + }: ModuleIO { + name: extern_module::o, + is_input: false, + ty: Bool, + .. + }, + }, + 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(0), + }, + state: 0x1, + last_state: 0x1, + }, + SimTrace { + id: TraceScalarId(1), + kind: BigBool { + index: StatePartIndex(1), + }, + state: 0x1, + last_state: 0x1, + }, + ], + trace_memories: {}, + trace_writers: [ + Running( + VcdWriter { + finished_init: true, + timescale: 1 ps, + .. + }, + ), + ], + instant: 20 μs, + clocks_triggered: [], + .. +} \ No newline at end of file diff --git a/crates/fayalite/tests/sim/expected/extern_module.vcd b/crates/fayalite/tests/sim/expected/extern_module.vcd new file mode 100644 index 0000000..e026a50 --- /dev/null +++ b/crates/fayalite/tests/sim/expected/extern_module.vcd @@ -0,0 +1,51 @@ +$timescale 1 ps $end +$scope module extern_module $end +$var wire 1 ! i $end +$var wire 1 " o $end +$upscope $end +$enddefinitions $end +$dumpvars +0! +1" +$end +#500000 +#1500000 +0" +#2500000 +1" +#3500000 +0" +#4500000 +1" +#5500000 +0" +#6500000 +1" +#7500000 +0" +#8500000 +1" +#9500000 +0" +#10000000 +1! +#10500000 +#11500000 +1" +#12500000 +0" +#13500000 +1" +#14500000 +0" +#15500000 +1" +#16500000 +0" +#17500000 +1" +#18500000 +0" +#19500000 +1" +#20000000 diff --git a/crates/fayalite/tests/sim/expected/extern_module2.txt b/crates/fayalite/tests/sim/expected/extern_module2.txt new file mode 100644 index 0000000..1023b2b --- /dev/null +++ b/crates/fayalite/tests/sim/expected/extern_module2.txt @@ -0,0 +1,362 @@ +Simulation { + state: State { + insns: Insns { + state_layout: StateLayout { + ty: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 3, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(extern_module2: extern_module2).extern_module2::en", + ty: Bool, + }, + SlotDebugData { + name: "InstantiatedModule(extern_module2: extern_module2).extern_module2::clk", + ty: Clock, + }, + SlotDebugData { + name: "InstantiatedModule(extern_module2: extern_module2).extern_module2::o", + ty: UInt<8>, + }, + ], + .. + }, + sim_only_slots: StatePartLayout { + len: 0, + debug_data: [], + layout_data: [], + .. + }, + }, + memories: StatePartLayout { + len: 0, + debug_data: [], + layout_data: [], + .. + }, + }, + insns: [ + // at: module-XXXXXXXXXX.rs:1:1 + 0: Return, + ], + .. + }, + pc: 0, + memory_write_log: [], + memories: StatePart { + value: [], + }, + small_slots: StatePart { + value: [], + }, + big_slots: StatePart { + value: [ + 0, + 1, + 101, + ], + }, + sim_only_slots: StatePart { + value: [], + }, + }, + io: Instance { + name: ::extern_module2, + instantiated: Module { + name: extern_module2, + .. + }, + }, + main_module: SimulationModuleState { + base_targets: [ + Instance { + name: ::extern_module2, + instantiated: Module { + name: extern_module2, + .. + }, + }.en, + Instance { + name: ::extern_module2, + instantiated: Module { + name: extern_module2, + .. + }, + }.clk, + Instance { + name: ::extern_module2, + instantiated: Module { + name: extern_module2, + .. + }, + }.o, + ], + uninitialized_ios: {}, + io_targets: { + Instance { + name: ::extern_module2, + instantiated: Module { + name: extern_module2, + .. + }, + }.clk, + Instance { + name: ::extern_module2, + instantiated: Module { + name: extern_module2, + .. + }, + }.en, + Instance { + name: ::extern_module2, + instantiated: Module { + name: extern_module2, + .. + }, + }.o, + }, + did_initial_settle: true, + }, + extern_modules: [ + SimulationExternModuleState { + module_state: SimulationModuleState { + base_targets: [ + ModuleIO { + name: extern_module2::en, + is_input: true, + ty: Bool, + .. + }, + ModuleIO { + name: extern_module2::clk, + is_input: true, + ty: Clock, + .. + }, + ModuleIO { + name: extern_module2::o, + is_input: false, + ty: UInt<8>, + .. + }, + ], + uninitialized_ios: {}, + io_targets: { + ModuleIO { + name: extern_module2::clk, + is_input: true, + ty: Clock, + .. + }, + ModuleIO { + name: extern_module2::en, + is_input: true, + ty: Bool, + .. + }, + ModuleIO { + name: extern_module2::o, + is_input: false, + ty: UInt<8>, + .. + }, + }, + did_initial_settle: true, + }, + sim: ExternModuleSimulation { + generator: SimGeneratorFn { + args: ( + ModuleIO { + name: extern_module2::en, + is_input: true, + ty: Bool, + .. + }, + ModuleIO { + name: extern_module2::clk, + is_input: true, + ty: Clock, + .. + }, + ModuleIO { + name: extern_module2::o, + is_input: false, + ty: UInt<8>, + .. + }, + ), + f: ..., + }, + sim_io_to_generator_map: { + ModuleIO { + name: extern_module2::clk, + is_input: true, + ty: Clock, + .. + }: ModuleIO { + name: extern_module2::clk, + is_input: true, + ty: Clock, + .. + }, + ModuleIO { + name: extern_module2::en, + is_input: true, + ty: Bool, + .. + }: ModuleIO { + name: extern_module2::en, + is_input: true, + ty: Bool, + .. + }, + ModuleIO { + name: extern_module2::o, + is_input: false, + ty: UInt<8>, + .. + }: ModuleIO { + name: extern_module2::o, + is_input: false, + ty: UInt<8>, + .. + }, + }, + source_location: SourceLocation( + module-XXXXXXXXXX.rs:5:1, + ), + }, + running_generator: Some( + ..., + ), + wait_targets: { + Change { + key: CompiledValue { + layout: CompiledTypeLayout { + ty: Clock, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(extern_module2: extern_module2).extern_module2::clk", + ty: Clock, + }, + ], + .. + }, + sim_only_slots: StatePartLayout { + len: 0, + debug_data: [], + layout_data: [], + .. + }, + }, + body: Scalar, + }, + range: TypeIndexRange { + small_slots: StatePartIndexRange { start: 0, len: 0 }, + big_slots: StatePartIndexRange { start: 1, len: 1 }, + sim_only_slots: StatePartIndexRange { start: 0, len: 0 }, + }, + write: None, + }, + value: SimValue { + ty: Clock, + value: OpaqueSimValue { + bits: 0x1_u1, + sim_only_values: [], + }, + }, + }, + }, + }, + ], + state_ready_to_run: false, + trace_decls: TraceModule { + name: "extern_module2", + children: [ + TraceModuleIO { + name: "en", + child: TraceBool { + location: TraceScalarId(0), + name: "en", + flow: Source, + }, + ty: Bool, + flow: Source, + }, + TraceModuleIO { + name: "clk", + child: TraceClock { + location: TraceScalarId(1), + name: "clk", + flow: Source, + }, + ty: Clock, + flow: Source, + }, + TraceModuleIO { + name: "o", + child: TraceUInt { + location: TraceScalarId(2), + name: "o", + ty: UInt<8>, + flow: Sink, + }, + ty: UInt<8>, + flow: Sink, + }, + ], + }, + traces: [ + SimTrace { + id: TraceScalarId(0), + kind: BigBool { + index: StatePartIndex(0), + }, + state: 0x0, + last_state: 0x0, + }, + SimTrace { + id: TraceScalarId(1), + kind: BigClock { + index: StatePartIndex(1), + }, + state: 0x1, + last_state: 0x1, + }, + SimTrace { + id: TraceScalarId(2), + kind: BigUInt { + index: StatePartIndex(2), + ty: UInt<8>, + }, + state: 0x65, + last_state: 0x65, + }, + ], + trace_memories: {}, + trace_writers: [ + Running( + VcdWriter { + finished_init: true, + timescale: 1 ps, + .. + }, + ), + ], + instant: 60 μs, + clocks_triggered: [], + .. +} \ No newline at end of file diff --git a/crates/fayalite/tests/sim/expected/extern_module2.vcd b/crates/fayalite/tests/sim/expected/extern_module2.vcd new file mode 100644 index 0000000..464f4bd --- /dev/null +++ b/crates/fayalite/tests/sim/expected/extern_module2.vcd @@ -0,0 +1,150 @@ +$timescale 1 ps $end +$scope module extern_module2 $end +$var wire 1 ! en $end +$var wire 1 " clk $end +$var wire 8 # o $end +$upscope $end +$enddefinitions $end +$dumpvars +1! +0" +b1001000 # +$end +#1000000 +1" +b1100101 # +#2000000 +0" +#3000000 +1" +b1101100 # +#4000000 +0" +#5000000 +1" +#6000000 +0" +#7000000 +1" +b1101111 # +#8000000 +0" +#9000000 +1" +b101100 # +#10000000 +0! +0" +#11000000 +1" +#12000000 +0" +#13000000 +1" +#14000000 +0" +#15000000 +1" +#16000000 +0" +#17000000 +1" +#18000000 +0" +#19000000 +1" +#20000000 +1! +0" +#21000000 +1" +b100000 # +#22000000 +0" +#23000000 +1" +b1010111 # +#24000000 +0" +#25000000 +1" +b1101111 # +#26000000 +0" +#27000000 +1" +b1110010 # +#28000000 +0" +#29000000 +1" +b1101100 # +#30000000 +0! +0" +#31000000 +1" +#32000000 +0" +#33000000 +1" +#34000000 +0" +#35000000 +1" +#36000000 +0" +#37000000 +1" +#38000000 +0" +#39000000 +1" +#40000000 +1! +0" +#41000000 +1" +b1100100 # +#42000000 +0" +#43000000 +1" +b100001 # +#44000000 +0" +#45000000 +1" +b1010 # +#46000000 +0" +#47000000 +1" +b1001000 # +#48000000 +0" +#49000000 +1" +b1100101 # +#50000000 +0! +0" +#51000000 +1" +#52000000 +0" +#53000000 +1" +#54000000 +0" +#55000000 +1" +#56000000 +0" +#57000000 +1" +#58000000 +0" +#59000000 +1" +#60000000 diff --git a/crates/fayalite/tests/sim/expected/memories.txt b/crates/fayalite/tests/sim/expected/memories.txt index afccd8a..f7f88e3 100644 --- a/crates/fayalite/tests/sim/expected/memories.txt +++ b/crates/fayalite/tests/sim/expected/memories.txt @@ -175,6 +175,12 @@ Simulation { ], .. }, + sim_only_slots: StatePartLayout { + len: 0, + debug_data: [], + layout_data: [], + .. + }, }, memories: StatePartLayout { len: 1, @@ -562,6 +568,9 @@ Simulation { 1, ], }, + sim_only_slots: StatePart { + value: [], + }, }, io: Instance { name: ::memories, @@ -570,1309 +579,149 @@ Simulation { .. }, }, - uninitialized_inputs: {}, - io_targets: { - Instance { - name: ::memories, - instantiated: Module { - name: memories, - .. - }, - }.r: CompiledValue { - layout: CompiledTypeLayout { - ty: Bundle { - /* offset = 0 */ - addr: UInt<4>, - /* offset = 4 */ - en: Bool, - /* offset = 5 */ - clk: Clock, - #[hdl(flip)] /* offset = 6 */ - data: Bundle { - /* offset = 0 */ - 0: UInt<8>, - /* offset = 8 */ - 1: SInt<8>, - }, + main_module: SimulationModuleState { + base_targets: [ + Instance { + name: ::memories, + instantiated: Module { + name: memories, + .. }, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 5, - debug_data: [ - SlotDebugData { - name: "InstantiatedModule(memories: memories).memories::r.addr", - ty: UInt<4>, - }, - SlotDebugData { - name: "InstantiatedModule(memories: memories).memories::r.en", - ty: Bool, - }, - SlotDebugData { - name: "InstantiatedModule(memories: memories).memories::r.clk", - ty: Clock, - }, - SlotDebugData { - name: "InstantiatedModule(memories: memories).memories::r.data.0", - ty: UInt<8>, - }, - SlotDebugData { - name: "InstantiatedModule(memories: memories).memories::r.data.1", - ty: SInt<8>, - }, - ], - .. - }, + }.r, + Instance { + name: ::memories, + instantiated: Module { + name: memories, + .. }, - body: Bundle { - fields: [ - CompiledBundleField { - offset: TypeIndex { - small_slots: StatePartIndex(0), - big_slots: StatePartIndex(0), - }, - ty: CompiledTypeLayout { - ty: UInt<4>, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: UInt<4>, - }, - ], - .. - }, - }, - body: Scalar, - }, - }, - CompiledBundleField { - offset: TypeIndex { - small_slots: StatePartIndex(0), - big_slots: StatePartIndex(1), - }, - ty: CompiledTypeLayout { - ty: Bool, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: Bool, - }, - ], - .. - }, - }, - body: Scalar, - }, - }, - CompiledBundleField { - offset: TypeIndex { - small_slots: StatePartIndex(0), - big_slots: StatePartIndex(2), - }, - ty: CompiledTypeLayout { - ty: Clock, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: Clock, - }, - ], - .. - }, - }, - body: Scalar, - }, - }, - CompiledBundleField { - offset: TypeIndex { - small_slots: StatePartIndex(0), - big_slots: StatePartIndex(3), - }, - ty: CompiledTypeLayout { - ty: Bundle { - /* offset = 0 */ - 0: UInt<8>, - /* offset = 8 */ - 1: SInt<8>, - }, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 2, - debug_data: [ - SlotDebugData { - name: ".0", - ty: UInt<8>, - }, - SlotDebugData { - name: ".1", - ty: SInt<8>, - }, - ], - .. - }, - }, - body: Bundle { - fields: [ - CompiledBundleField { - offset: TypeIndex { - small_slots: StatePartIndex(0), - big_slots: StatePartIndex(0), - }, - ty: CompiledTypeLayout { - ty: UInt<8>, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: UInt<8>, - }, - ], - .. - }, - }, - body: Scalar, - }, - }, - CompiledBundleField { - offset: TypeIndex { - small_slots: StatePartIndex(0), - big_slots: StatePartIndex(1), - }, - ty: CompiledTypeLayout { - ty: SInt<8>, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: SInt<8>, - }, - ], - .. - }, - }, - body: Scalar, - }, - }, - ], - }, - }, - }, - ], + }.w, + ], + uninitialized_ios: {}, + io_targets: { + Instance { + name: ::memories, + instantiated: Module { + name: memories, + .. }, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 0, len: 5 }, - }, - write: None, - }, - Instance { - name: ::memories, - instantiated: Module { - name: memories, - .. - }, - }.r.addr: CompiledValue { - layout: CompiledTypeLayout { - ty: UInt<4>, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: UInt<4>, - }, - ], - .. - }, - }, - body: Scalar, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 0, len: 1 }, - }, - write: None, - }, - Instance { - name: ::memories, - instantiated: Module { - name: memories, - .. - }, - }.r.clk: CompiledValue { - layout: CompiledTypeLayout { - ty: Clock, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: Clock, - }, - ], - .. - }, - }, - body: Scalar, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 2, len: 1 }, - }, - write: None, - }, - Instance { - name: ::memories, - instantiated: Module { - name: memories, - .. - }, - }.r.data: CompiledValue { - layout: CompiledTypeLayout { - ty: Bundle { - /* offset = 0 */ - 0: UInt<8>, - /* offset = 8 */ - 1: SInt<8>, - }, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 2, - debug_data: [ - SlotDebugData { - name: ".0", - ty: UInt<8>, - }, - SlotDebugData { - name: ".1", - ty: SInt<8>, - }, - ], - .. - }, - }, - body: Bundle { - fields: [ - CompiledBundleField { - offset: TypeIndex { - small_slots: StatePartIndex(0), - big_slots: StatePartIndex(0), - }, - ty: CompiledTypeLayout { - ty: UInt<8>, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: UInt<8>, - }, - ], - .. - }, - }, - body: Scalar, - }, - }, - CompiledBundleField { - offset: TypeIndex { - small_slots: StatePartIndex(0), - big_slots: StatePartIndex(1), - }, - ty: CompiledTypeLayout { - ty: SInt<8>, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: SInt<8>, - }, - ], - .. - }, - }, - body: Scalar, - }, - }, - ], - }, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 3, len: 2 }, - }, - write: None, - }, - Instance { - name: ::memories, - instantiated: Module { - name: memories, - .. - }, - }.r.data.0: CompiledValue { - layout: CompiledTypeLayout { - ty: UInt<8>, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: UInt<8>, - }, - ], - .. - }, - }, - body: Scalar, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 3, len: 1 }, - }, - write: None, - }, - Instance { - name: ::memories, - instantiated: Module { - name: memories, - .. - }, - }.r.data.1: CompiledValue { - layout: CompiledTypeLayout { - ty: SInt<8>, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: SInt<8>, - }, - ], - .. - }, - }, - body: Scalar, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 4, len: 1 }, - }, - write: None, - }, - Instance { - name: ::memories, - instantiated: Module { - name: memories, - .. - }, - }.r.en: CompiledValue { - layout: CompiledTypeLayout { - ty: Bool, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: Bool, - }, - ], - .. - }, - }, - body: Scalar, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 1, len: 1 }, - }, - write: None, - }, - Instance { - name: ::memories, - instantiated: Module { - name: memories, - .. - }, - }.w: CompiledValue { - layout: CompiledTypeLayout { - ty: Bundle { - /* offset = 0 */ - addr: UInt<4>, - /* offset = 4 */ - en: Bool, - /* offset = 5 */ - clk: Clock, - /* offset = 6 */ - data: Bundle { - /* offset = 0 */ - 0: UInt<8>, - /* offset = 8 */ - 1: SInt<8>, - }, - /* offset = 22 */ - mask: Bundle { - /* offset = 0 */ - 0: Bool, - /* offset = 1 */ - 1: Bool, - }, - }, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 7, - debug_data: [ - SlotDebugData { - name: "InstantiatedModule(memories: memories).memories::w.addr", - ty: UInt<4>, - }, - SlotDebugData { - name: "InstantiatedModule(memories: memories).memories::w.en", - ty: Bool, - }, - SlotDebugData { - name: "InstantiatedModule(memories: memories).memories::w.clk", - ty: Clock, - }, - SlotDebugData { - name: "InstantiatedModule(memories: memories).memories::w.data.0", - ty: UInt<8>, - }, - SlotDebugData { - name: "InstantiatedModule(memories: memories).memories::w.data.1", - ty: SInt<8>, - }, - SlotDebugData { - name: "InstantiatedModule(memories: memories).memories::w.mask.0", - ty: Bool, - }, - SlotDebugData { - name: "InstantiatedModule(memories: memories).memories::w.mask.1", - ty: Bool, - }, - ], - .. - }, - }, - body: Bundle { - fields: [ - CompiledBundleField { - offset: TypeIndex { - small_slots: StatePartIndex(0), - big_slots: StatePartIndex(0), - }, - ty: CompiledTypeLayout { - ty: UInt<4>, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: UInt<4>, - }, - ], - .. - }, - }, - body: Scalar, - }, - }, - CompiledBundleField { - offset: TypeIndex { - small_slots: StatePartIndex(0), - big_slots: StatePartIndex(1), - }, - ty: CompiledTypeLayout { - ty: Bool, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: Bool, - }, - ], - .. - }, - }, - body: Scalar, - }, - }, - CompiledBundleField { - offset: TypeIndex { - small_slots: StatePartIndex(0), - big_slots: StatePartIndex(2), - }, - ty: CompiledTypeLayout { - ty: Clock, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: Clock, - }, - ], - .. - }, - }, - body: Scalar, - }, - }, - CompiledBundleField { - offset: TypeIndex { - small_slots: StatePartIndex(0), - big_slots: StatePartIndex(3), - }, - ty: CompiledTypeLayout { - ty: Bundle { - /* offset = 0 */ - 0: UInt<8>, - /* offset = 8 */ - 1: SInt<8>, - }, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 2, - debug_data: [ - SlotDebugData { - name: ".0", - ty: UInt<8>, - }, - SlotDebugData { - name: ".1", - ty: SInt<8>, - }, - ], - .. - }, - }, - body: Bundle { - fields: [ - CompiledBundleField { - offset: TypeIndex { - small_slots: StatePartIndex(0), - big_slots: StatePartIndex(0), - }, - ty: CompiledTypeLayout { - ty: UInt<8>, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: UInt<8>, - }, - ], - .. - }, - }, - body: Scalar, - }, - }, - CompiledBundleField { - offset: TypeIndex { - small_slots: StatePartIndex(0), - big_slots: StatePartIndex(1), - }, - ty: CompiledTypeLayout { - ty: SInt<8>, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: SInt<8>, - }, - ], - .. - }, - }, - body: Scalar, - }, - }, - ], - }, - }, - }, - CompiledBundleField { - offset: TypeIndex { - small_slots: StatePartIndex(0), - big_slots: StatePartIndex(5), - }, - ty: CompiledTypeLayout { - ty: Bundle { - /* offset = 0 */ - 0: Bool, - /* offset = 1 */ - 1: Bool, - }, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 2, - debug_data: [ - SlotDebugData { - name: ".0", - ty: Bool, - }, - SlotDebugData { - name: ".1", - ty: Bool, - }, - ], - .. - }, - }, - body: Bundle { - fields: [ - CompiledBundleField { - offset: TypeIndex { - small_slots: StatePartIndex(0), - big_slots: StatePartIndex(0), - }, - ty: CompiledTypeLayout { - ty: Bool, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: Bool, - }, - ], - .. - }, - }, - body: Scalar, - }, - }, - CompiledBundleField { - offset: TypeIndex { - small_slots: StatePartIndex(0), - big_slots: StatePartIndex(1), - }, - ty: CompiledTypeLayout { - ty: Bool, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: Bool, - }, - ], - .. - }, - }, - body: Scalar, - }, - }, - ], - }, - }, - }, - ], - }, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 5, len: 7 }, - }, - write: None, - }, - Instance { - name: ::memories, - instantiated: Module { - name: memories, - .. - }, - }.w.addr: CompiledValue { - layout: CompiledTypeLayout { - ty: UInt<4>, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: UInt<4>, - }, - ], - .. - }, - }, - body: Scalar, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 5, len: 1 }, - }, - write: None, - }, - Instance { - name: ::memories, - instantiated: Module { - name: memories, - .. - }, - }.w.clk: CompiledValue { - layout: CompiledTypeLayout { - ty: Clock, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: Clock, - }, - ], - .. - }, - }, - body: Scalar, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 7, len: 1 }, - }, - write: None, - }, - Instance { - name: ::memories, - instantiated: Module { - name: memories, - .. - }, - }.w.data: CompiledValue { - layout: CompiledTypeLayout { - ty: Bundle { - /* offset = 0 */ - 0: UInt<8>, - /* offset = 8 */ - 1: SInt<8>, - }, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 2, - debug_data: [ - SlotDebugData { - name: ".0", - ty: UInt<8>, - }, - SlotDebugData { - name: ".1", - ty: SInt<8>, - }, - ], - .. - }, - }, - body: Bundle { - fields: [ - CompiledBundleField { - offset: TypeIndex { - small_slots: StatePartIndex(0), - big_slots: StatePartIndex(0), - }, - ty: CompiledTypeLayout { - ty: UInt<8>, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: UInt<8>, - }, - ], - .. - }, - }, - body: Scalar, - }, - }, - CompiledBundleField { - offset: TypeIndex { - small_slots: StatePartIndex(0), - big_slots: StatePartIndex(1), - }, - ty: CompiledTypeLayout { - ty: SInt<8>, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: SInt<8>, - }, - ], - .. - }, - }, - body: Scalar, - }, - }, - ], - }, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 8, len: 2 }, - }, - write: None, - }, - Instance { - name: ::memories, - instantiated: Module { - name: memories, - .. - }, - }.w.data.0: CompiledValue { - layout: CompiledTypeLayout { - ty: UInt<8>, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: UInt<8>, - }, - ], - .. - }, - }, - body: Scalar, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 8, len: 1 }, - }, - write: None, - }, - Instance { - name: ::memories, - instantiated: Module { - name: memories, - .. - }, - }.w.data.1: CompiledValue { - layout: CompiledTypeLayout { - ty: SInt<8>, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: SInt<8>, - }, - ], - .. - }, - }, - body: Scalar, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 9, len: 1 }, - }, - write: None, - }, - Instance { - name: ::memories, - instantiated: Module { - name: memories, - .. - }, - }.w.en: CompiledValue { - layout: CompiledTypeLayout { - ty: Bool, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: Bool, - }, - ], - .. - }, - }, - body: Scalar, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 6, len: 1 }, - }, - write: None, - }, - Instance { - name: ::memories, - instantiated: Module { - name: memories, - .. - }, - }.w.mask: CompiledValue { - layout: CompiledTypeLayout { - ty: Bundle { - /* offset = 0 */ - 0: Bool, - /* offset = 1 */ - 1: Bool, - }, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 2, - debug_data: [ - SlotDebugData { - name: ".0", - ty: Bool, - }, - SlotDebugData { - name: ".1", - ty: Bool, - }, - ], - .. - }, - }, - body: Bundle { - fields: [ - CompiledBundleField { - offset: TypeIndex { - small_slots: StatePartIndex(0), - big_slots: StatePartIndex(0), - }, - ty: CompiledTypeLayout { - ty: Bool, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: Bool, - }, - ], - .. - }, - }, - body: Scalar, - }, - }, - CompiledBundleField { - offset: TypeIndex { - small_slots: StatePartIndex(0), - big_slots: StatePartIndex(1), - }, - ty: CompiledTypeLayout { - ty: Bool, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: Bool, - }, - ], - .. - }, - }, - body: Scalar, - }, - }, - ], - }, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 10, len: 2 }, - }, - write: None, - }, - Instance { - name: ::memories, - instantiated: Module { - name: memories, - .. - }, - }.w.mask.0: CompiledValue { - layout: CompiledTypeLayout { - ty: Bool, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: Bool, - }, - ], - .. - }, - }, - body: Scalar, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 10, len: 1 }, - }, - write: None, - }, - Instance { - name: ::memories, - instantiated: Module { - name: memories, - .. - }, - }.w.mask.1: CompiledValue { - layout: CompiledTypeLayout { - ty: Bool, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: Bool, - }, - ], - .. - }, - }, - body: Scalar, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 11, len: 1 }, - }, - write: None, + }.r, + Instance { + name: ::memories, + instantiated: Module { + name: memories, + .. + }, + }.r.addr, + Instance { + name: ::memories, + instantiated: Module { + name: memories, + .. + }, + }.r.clk, + Instance { + name: ::memories, + instantiated: Module { + name: memories, + .. + }, + }.r.data, + Instance { + name: ::memories, + instantiated: Module { + name: memories, + .. + }, + }.r.data.0, + Instance { + name: ::memories, + instantiated: Module { + name: memories, + .. + }, + }.r.data.1, + Instance { + name: ::memories, + instantiated: Module { + name: memories, + .. + }, + }.r.en, + Instance { + name: ::memories, + instantiated: Module { + name: memories, + .. + }, + }.w, + Instance { + name: ::memories, + instantiated: Module { + name: memories, + .. + }, + }.w.addr, + Instance { + name: ::memories, + instantiated: Module { + name: memories, + .. + }, + }.w.clk, + Instance { + name: ::memories, + instantiated: Module { + name: memories, + .. + }, + }.w.data, + Instance { + name: ::memories, + instantiated: Module { + name: memories, + .. + }, + }.w.data.0, + Instance { + name: ::memories, + instantiated: Module { + name: memories, + .. + }, + }.w.data.1, + Instance { + name: ::memories, + instantiated: Module { + name: memories, + .. + }, + }.w.en, + Instance { + name: ::memories, + instantiated: Module { + name: memories, + .. + }, + }.w.mask, + Instance { + name: ::memories, + instantiated: Module { + name: memories, + .. + }, + }.w.mask.0, + Instance { + name: ::memories, + instantiated: Module { + name: memories, + .. + }, + }.w.mask.1, }, + did_initial_settle: true, }, - made_initial_step: true, - needs_settle: false, + extern_modules: [], + state_ready_to_run: false, trace_decls: TraceModule { name: "memories", children: [ diff --git a/crates/fayalite/tests/sim/expected/memories.vcd b/crates/fayalite/tests/sim/expected/memories.vcd index 72af410..bedc354 100644 --- a/crates/fayalite/tests/sim/expected/memories.vcd +++ b/crates/fayalite/tests/sim/expected/memories.vcd @@ -24,97 +24,97 @@ $upscope $end $upscope $end $scope struct mem $end $scope struct contents $end -$scope struct [0] $end +$scope struct \[0] $end $scope struct mem $end $var reg 8 9 \0 $end $var reg 8 I \1 $end $upscope $end $upscope $end -$scope struct [1] $end +$scope struct \[1] $end $scope struct mem $end $var reg 8 : \0 $end $var reg 8 J \1 $end $upscope $end $upscope $end -$scope struct [2] $end +$scope struct \[2] $end $scope struct mem $end $var reg 8 ; \0 $end $var reg 8 K \1 $end $upscope $end $upscope $end -$scope struct [3] $end +$scope struct \[3] $end $scope struct mem $end $var reg 8 < \0 $end $var reg 8 L \1 $end $upscope $end $upscope $end -$scope struct [4] $end +$scope struct \[4] $end $scope struct mem $end $var reg 8 = \0 $end $var reg 8 M \1 $end $upscope $end $upscope $end -$scope struct [5] $end +$scope struct \[5] $end $scope struct mem $end $var reg 8 > \0 $end $var reg 8 N \1 $end $upscope $end $upscope $end -$scope struct [6] $end +$scope struct \[6] $end $scope struct mem $end $var reg 8 ? \0 $end $var reg 8 O \1 $end $upscope $end $upscope $end -$scope struct [7] $end +$scope struct \[7] $end $scope struct mem $end $var reg 8 @ \0 $end $var reg 8 P \1 $end $upscope $end $upscope $end -$scope struct [8] $end +$scope struct \[8] $end $scope struct mem $end $var reg 8 A \0 $end $var reg 8 Q \1 $end $upscope $end $upscope $end -$scope struct [9] $end +$scope struct \[9] $end $scope struct mem $end $var reg 8 B \0 $end $var reg 8 R \1 $end $upscope $end $upscope $end -$scope struct [10] $end +$scope struct \[10] $end $scope struct mem $end $var reg 8 C \0 $end $var reg 8 S \1 $end $upscope $end $upscope $end -$scope struct [11] $end +$scope struct \[11] $end $scope struct mem $end $var reg 8 D \0 $end $var reg 8 T \1 $end $upscope $end $upscope $end -$scope struct [12] $end +$scope struct \[12] $end $scope struct mem $end $var reg 8 E \0 $end $var reg 8 U \1 $end $upscope $end $upscope $end -$scope struct [13] $end +$scope struct \[13] $end $scope struct mem $end $var reg 8 F \0 $end $var reg 8 V \1 $end $upscope $end $upscope $end -$scope struct [14] $end +$scope struct \[14] $end $scope struct mem $end $var reg 8 G \0 $end $var reg 8 W \1 $end $upscope $end $upscope $end -$scope struct [15] $end +$scope struct \[15] $end $scope struct mem $end $var reg 8 H \0 $end $var reg 8 X \1 $end diff --git a/crates/fayalite/tests/sim/expected/memories2.txt b/crates/fayalite/tests/sim/expected/memories2.txt index 5d90815..c216104 100644 --- a/crates/fayalite/tests/sim/expected/memories2.txt +++ b/crates/fayalite/tests/sim/expected/memories2.txt @@ -224,6 +224,12 @@ Simulation { ], .. }, + sim_only_slots: StatePartLayout { + len: 0, + debug_data: [], + layout_data: [], + .. + }, }, memories: StatePartLayout { len: 1, @@ -590,6 +596,9 @@ Simulation { 1, ], }, + sim_only_slots: StatePart { + value: [], + }, }, io: Instance { name: ::memories2, @@ -598,514 +607,79 @@ Simulation { .. }, }, - uninitialized_inputs: {}, - io_targets: { - Instance { - name: ::memories2, - instantiated: Module { - name: memories2, - .. - }, - }.rw: CompiledValue { - layout: CompiledTypeLayout { - ty: Bundle { - /* offset = 0 */ - addr: UInt<3>, - /* offset = 3 */ - en: Bool, - /* offset = 4 */ - clk: Clock, - #[hdl(flip)] /* offset = 5 */ - rdata: UInt<2>, - /* offset = 7 */ - wmode: Bool, - /* offset = 8 */ - wdata: UInt<2>, - /* offset = 10 */ - wmask: Bool, + main_module: SimulationModuleState { + base_targets: [ + Instance { + name: ::memories2, + instantiated: Module { + name: memories2, + .. }, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 7, - debug_data: [ - SlotDebugData { - name: "InstantiatedModule(memories2: memories2).memories2::rw.addr", - ty: UInt<3>, - }, - SlotDebugData { - name: "InstantiatedModule(memories2: memories2).memories2::rw.en", - ty: Bool, - }, - SlotDebugData { - name: "InstantiatedModule(memories2: memories2).memories2::rw.clk", - ty: Clock, - }, - SlotDebugData { - name: "InstantiatedModule(memories2: memories2).memories2::rw.rdata", - ty: UInt<2>, - }, - SlotDebugData { - name: "InstantiatedModule(memories2: memories2).memories2::rw.wmode", - ty: Bool, - }, - SlotDebugData { - name: "InstantiatedModule(memories2: memories2).memories2::rw.wdata", - ty: UInt<2>, - }, - SlotDebugData { - name: "InstantiatedModule(memories2: memories2).memories2::rw.wmask", - ty: Bool, - }, - ], - .. - }, + }.rw, + ], + uninitialized_ios: {}, + io_targets: { + Instance { + name: ::memories2, + instantiated: Module { + name: memories2, + .. }, - body: Bundle { - fields: [ - CompiledBundleField { - offset: TypeIndex { - small_slots: StatePartIndex(0), - big_slots: StatePartIndex(0), - }, - ty: CompiledTypeLayout { - ty: UInt<3>, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: UInt<3>, - }, - ], - .. - }, - }, - body: Scalar, - }, - }, - CompiledBundleField { - offset: TypeIndex { - small_slots: StatePartIndex(0), - big_slots: StatePartIndex(1), - }, - ty: CompiledTypeLayout { - ty: Bool, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: Bool, - }, - ], - .. - }, - }, - body: Scalar, - }, - }, - CompiledBundleField { - offset: TypeIndex { - small_slots: StatePartIndex(0), - big_slots: StatePartIndex(2), - }, - ty: CompiledTypeLayout { - ty: Clock, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: Clock, - }, - ], - .. - }, - }, - body: Scalar, - }, - }, - CompiledBundleField { - offset: TypeIndex { - small_slots: StatePartIndex(0), - big_slots: StatePartIndex(3), - }, - ty: CompiledTypeLayout { - ty: UInt<2>, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: UInt<2>, - }, - ], - .. - }, - }, - body: Scalar, - }, - }, - CompiledBundleField { - offset: TypeIndex { - small_slots: StatePartIndex(0), - big_slots: StatePartIndex(4), - }, - ty: CompiledTypeLayout { - ty: Bool, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: Bool, - }, - ], - .. - }, - }, - body: Scalar, - }, - }, - CompiledBundleField { - offset: TypeIndex { - small_slots: StatePartIndex(0), - big_slots: StatePartIndex(5), - }, - ty: CompiledTypeLayout { - ty: UInt<2>, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: UInt<2>, - }, - ], - .. - }, - }, - body: Scalar, - }, - }, - CompiledBundleField { - offset: TypeIndex { - small_slots: StatePartIndex(0), - big_slots: StatePartIndex(6), - }, - ty: CompiledTypeLayout { - ty: Bool, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: Bool, - }, - ], - .. - }, - }, - body: Scalar, - }, - }, - ], + }.rw, + Instance { + name: ::memories2, + instantiated: Module { + name: memories2, + .. }, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 0, len: 7 }, - }, - write: None, - }, - Instance { - name: ::memories2, - instantiated: Module { - name: memories2, - .. - }, - }.rw.addr: CompiledValue { - layout: CompiledTypeLayout { - ty: UInt<3>, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: UInt<3>, - }, - ], - .. - }, - }, - body: Scalar, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 0, len: 1 }, - }, - write: None, - }, - Instance { - name: ::memories2, - instantiated: Module { - name: memories2, - .. - }, - }.rw.clk: CompiledValue { - layout: CompiledTypeLayout { - ty: Clock, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: Clock, - }, - ], - .. - }, - }, - body: Scalar, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 2, len: 1 }, - }, - write: None, - }, - Instance { - name: ::memories2, - instantiated: Module { - name: memories2, - .. - }, - }.rw.en: CompiledValue { - layout: CompiledTypeLayout { - ty: Bool, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: Bool, - }, - ], - .. - }, - }, - body: Scalar, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 1, len: 1 }, - }, - write: None, - }, - Instance { - name: ::memories2, - instantiated: Module { - name: memories2, - .. - }, - }.rw.rdata: CompiledValue { - layout: CompiledTypeLayout { - ty: UInt<2>, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: UInt<2>, - }, - ], - .. - }, - }, - body: Scalar, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 3, len: 1 }, - }, - write: None, - }, - Instance { - name: ::memories2, - instantiated: Module { - name: memories2, - .. - }, - }.rw.wdata: CompiledValue { - layout: CompiledTypeLayout { - ty: UInt<2>, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: UInt<2>, - }, - ], - .. - }, - }, - body: Scalar, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 5, len: 1 }, - }, - write: None, - }, - Instance { - name: ::memories2, - instantiated: Module { - name: memories2, - .. - }, - }.rw.wmask: CompiledValue { - layout: CompiledTypeLayout { - ty: Bool, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: Bool, - }, - ], - .. - }, - }, - body: Scalar, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 6, len: 1 }, - }, - write: None, - }, - Instance { - name: ::memories2, - instantiated: Module { - name: memories2, - .. - }, - }.rw.wmode: CompiledValue { - layout: CompiledTypeLayout { - ty: Bool, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: Bool, - }, - ], - .. - }, - }, - body: Scalar, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 4, len: 1 }, - }, - write: None, + }.rw.addr, + Instance { + name: ::memories2, + instantiated: Module { + name: memories2, + .. + }, + }.rw.clk, + Instance { + name: ::memories2, + instantiated: Module { + name: memories2, + .. + }, + }.rw.en, + Instance { + name: ::memories2, + instantiated: Module { + name: memories2, + .. + }, + }.rw.rdata, + Instance { + name: ::memories2, + instantiated: Module { + name: memories2, + .. + }, + }.rw.wdata, + Instance { + name: ::memories2, + instantiated: Module { + name: memories2, + .. + }, + }.rw.wmask, + Instance { + name: ::memories2, + instantiated: Module { + name: memories2, + .. + }, + }.rw.wmode, }, + did_initial_settle: true, }, - made_initial_step: true, - needs_settle: false, + extern_modules: [], + state_ready_to_run: false, trace_decls: TraceModule { name: "memories2", children: [ diff --git a/crates/fayalite/tests/sim/expected/memories2.vcd b/crates/fayalite/tests/sim/expected/memories2.vcd index bd48f24..4039754 100644 --- a/crates/fayalite/tests/sim/expected/memories2.vcd +++ b/crates/fayalite/tests/sim/expected/memories2.vcd @@ -11,31 +11,31 @@ $var wire 1 ' wmask $end $upscope $end $scope struct mem $end $scope struct contents $end -$scope struct [0] $end +$scope struct \[0] $end $scope struct mem $end $var string 1 1 \$tag $end $var reg 1 6 HdlSome $end $upscope $end $upscope $end -$scope struct [1] $end +$scope struct \[1] $end $scope struct mem $end $var string 1 2 \$tag $end $var reg 1 7 HdlSome $end $upscope $end $upscope $end -$scope struct [2] $end +$scope struct \[2] $end $scope struct mem $end $var string 1 3 \$tag $end $var reg 1 8 HdlSome $end $upscope $end $upscope $end -$scope struct [3] $end +$scope struct \[3] $end $scope struct mem $end $var string 1 4 \$tag $end $var reg 1 9 HdlSome $end $upscope $end $upscope $end -$scope struct [4] $end +$scope struct \[4] $end $scope struct mem $end $var string 1 5 \$tag $end $var reg 1 : HdlSome $end diff --git a/crates/fayalite/tests/sim/expected/memories3.txt b/crates/fayalite/tests/sim/expected/memories3.txt index 7860bc5..8114c7e 100644 --- a/crates/fayalite/tests/sim/expected/memories3.txt +++ b/crates/fayalite/tests/sim/expected/memories3.txt @@ -503,6 +503,12 @@ Simulation { ], .. }, + sim_only_slots: StatePartLayout { + len: 0, + debug_data: [], + layout_data: [], + .. + }, }, memories: StatePartLayout { len: 1, @@ -1478,6 +1484,9 @@ Simulation { 0, ], }, + sim_only_slots: StatePart { + value: [], + }, }, io: Instance { name: ::memories3, @@ -1486,1882 +1495,275 @@ Simulation { .. }, }, - uninitialized_inputs: {}, - io_targets: { - Instance { - name: ::memories3, - instantiated: Module { - name: memories3, - .. - }, - }.r: CompiledValue { - layout: CompiledTypeLayout { - ty: Bundle { - /* offset = 0 */ - addr: UInt<3>, - /* offset = 3 */ - en: Bool, - /* offset = 4 */ - clk: Clock, - #[hdl(flip)] /* offset = 5 */ - data: Array, 8>, + main_module: SimulationModuleState { + base_targets: [ + Instance { + name: ::memories3, + instantiated: Module { + name: memories3, + .. }, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 11, - debug_data: [ - SlotDebugData { - name: "InstantiatedModule(memories3: memories3).memories3::r.addr", - ty: UInt<3>, - }, - SlotDebugData { - name: "InstantiatedModule(memories3: memories3).memories3::r.en", - ty: Bool, - }, - SlotDebugData { - name: "InstantiatedModule(memories3: memories3).memories3::r.clk", - ty: Clock, - }, - SlotDebugData { - name: "InstantiatedModule(memories3: memories3).memories3::r.data[0]", - ty: UInt<8>, - }, - SlotDebugData { - name: "InstantiatedModule(memories3: memories3).memories3::r.data[1]", - ty: UInt<8>, - }, - SlotDebugData { - name: "InstantiatedModule(memories3: memories3).memories3::r.data[2]", - ty: UInt<8>, - }, - SlotDebugData { - name: "InstantiatedModule(memories3: memories3).memories3::r.data[3]", - ty: UInt<8>, - }, - SlotDebugData { - name: "InstantiatedModule(memories3: memories3).memories3::r.data[4]", - ty: UInt<8>, - }, - SlotDebugData { - name: "InstantiatedModule(memories3: memories3).memories3::r.data[5]", - ty: UInt<8>, - }, - SlotDebugData { - name: "InstantiatedModule(memories3: memories3).memories3::r.data[6]", - ty: UInt<8>, - }, - SlotDebugData { - name: "InstantiatedModule(memories3: memories3).memories3::r.data[7]", - ty: UInt<8>, - }, - ], - .. - }, + }.r, + Instance { + name: ::memories3, + instantiated: Module { + name: memories3, + .. }, - body: Bundle { - fields: [ - CompiledBundleField { - offset: TypeIndex { - small_slots: StatePartIndex(0), - big_slots: StatePartIndex(0), - }, - ty: CompiledTypeLayout { - ty: UInt<3>, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: UInt<3>, - }, - ], - .. - }, - }, - body: Scalar, - }, - }, - CompiledBundleField { - offset: TypeIndex { - small_slots: StatePartIndex(0), - big_slots: StatePartIndex(1), - }, - ty: CompiledTypeLayout { - ty: Bool, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: Bool, - }, - ], - .. - }, - }, - body: Scalar, - }, - }, - CompiledBundleField { - offset: TypeIndex { - small_slots: StatePartIndex(0), - big_slots: StatePartIndex(2), - }, - ty: CompiledTypeLayout { - ty: Clock, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: Clock, - }, - ], - .. - }, - }, - body: Scalar, - }, - }, - CompiledBundleField { - offset: TypeIndex { - small_slots: StatePartIndex(0), - big_slots: StatePartIndex(3), - }, - ty: CompiledTypeLayout { - ty: Array, 8>, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 8, - debug_data: [ - SlotDebugData { - name: "[0]", - ty: UInt<8>, - }, - SlotDebugData { - name: "[1]", - ty: UInt<8>, - }, - SlotDebugData { - name: "[2]", - ty: UInt<8>, - }, - SlotDebugData { - name: "[3]", - ty: UInt<8>, - }, - SlotDebugData { - name: "[4]", - ty: UInt<8>, - }, - SlotDebugData { - name: "[5]", - ty: UInt<8>, - }, - SlotDebugData { - name: "[6]", - ty: UInt<8>, - }, - SlotDebugData { - name: "[7]", - ty: UInt<8>, - }, - ], - .. - }, - }, - body: Array { - element: CompiledTypeLayout { - ty: UInt<8>, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: UInt<8>, - }, - ], - .. - }, - }, - body: Scalar, - }, - }, - }, - }, - ], + }.w, + ], + uninitialized_ios: {}, + io_targets: { + Instance { + name: ::memories3, + instantiated: Module { + name: memories3, + .. }, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 0, len: 11 }, - }, - write: None, - }, - Instance { - name: ::memories3, - instantiated: Module { - name: memories3, - .. - }, - }.r.addr: CompiledValue { - layout: CompiledTypeLayout { - ty: UInt<3>, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: UInt<3>, - }, - ], - .. - }, - }, - body: Scalar, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 0, len: 1 }, - }, - write: None, - }, - Instance { - name: ::memories3, - instantiated: Module { - name: memories3, - .. - }, - }.r.clk: CompiledValue { - layout: CompiledTypeLayout { - ty: Clock, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: Clock, - }, - ], - .. - }, - }, - body: Scalar, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 2, len: 1 }, - }, - write: None, - }, - Instance { - name: ::memories3, - instantiated: Module { - name: memories3, - .. - }, - }.r.data: CompiledValue { - layout: CompiledTypeLayout { - ty: Array, 8>, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 8, - debug_data: [ - SlotDebugData { - name: "[0]", - ty: UInt<8>, - }, - SlotDebugData { - name: "[1]", - ty: UInt<8>, - }, - SlotDebugData { - name: "[2]", - ty: UInt<8>, - }, - SlotDebugData { - name: "[3]", - ty: UInt<8>, - }, - SlotDebugData { - name: "[4]", - ty: UInt<8>, - }, - SlotDebugData { - name: "[5]", - ty: UInt<8>, - }, - SlotDebugData { - name: "[6]", - ty: UInt<8>, - }, - SlotDebugData { - name: "[7]", - ty: UInt<8>, - }, - ], - .. - }, - }, - body: Array { - element: CompiledTypeLayout { - ty: UInt<8>, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: UInt<8>, - }, - ], - .. - }, - }, - body: Scalar, - }, - }, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 3, len: 8 }, - }, - write: None, - }, - Instance { - name: ::memories3, - instantiated: Module { - name: memories3, - .. - }, - }.r.data[0]: CompiledValue { - layout: CompiledTypeLayout { - ty: UInt<8>, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: UInt<8>, - }, - ], - .. - }, - }, - body: Scalar, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 3, len: 1 }, - }, - write: None, - }, - Instance { - name: ::memories3, - instantiated: Module { - name: memories3, - .. - }, - }.r.data[1]: CompiledValue { - layout: CompiledTypeLayout { - ty: UInt<8>, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: UInt<8>, - }, - ], - .. - }, - }, - body: Scalar, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 4, len: 1 }, - }, - write: None, - }, - Instance { - name: ::memories3, - instantiated: Module { - name: memories3, - .. - }, - }.r.data[2]: CompiledValue { - layout: CompiledTypeLayout { - ty: UInt<8>, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: UInt<8>, - }, - ], - .. - }, - }, - body: Scalar, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 5, len: 1 }, - }, - write: None, - }, - Instance { - name: ::memories3, - instantiated: Module { - name: memories3, - .. - }, - }.r.data[3]: CompiledValue { - layout: CompiledTypeLayout { - ty: UInt<8>, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: UInt<8>, - }, - ], - .. - }, - }, - body: Scalar, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 6, len: 1 }, - }, - write: None, - }, - Instance { - name: ::memories3, - instantiated: Module { - name: memories3, - .. - }, - }.r.data[4]: CompiledValue { - layout: CompiledTypeLayout { - ty: UInt<8>, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: UInt<8>, - }, - ], - .. - }, - }, - body: Scalar, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 7, len: 1 }, - }, - write: None, - }, - Instance { - name: ::memories3, - instantiated: Module { - name: memories3, - .. - }, - }.r.data[5]: CompiledValue { - layout: CompiledTypeLayout { - ty: UInt<8>, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: UInt<8>, - }, - ], - .. - }, - }, - body: Scalar, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 8, len: 1 }, - }, - write: None, - }, - Instance { - name: ::memories3, - instantiated: Module { - name: memories3, - .. - }, - }.r.data[6]: CompiledValue { - layout: CompiledTypeLayout { - ty: UInt<8>, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: UInt<8>, - }, - ], - .. - }, - }, - body: Scalar, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 9, len: 1 }, - }, - write: None, - }, - Instance { - name: ::memories3, - instantiated: Module { - name: memories3, - .. - }, - }.r.data[7]: CompiledValue { - layout: CompiledTypeLayout { - ty: UInt<8>, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: UInt<8>, - }, - ], - .. - }, - }, - body: Scalar, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 10, len: 1 }, - }, - write: None, - }, - Instance { - name: ::memories3, - instantiated: Module { - name: memories3, - .. - }, - }.r.en: CompiledValue { - layout: CompiledTypeLayout { - ty: Bool, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: Bool, - }, - ], - .. - }, - }, - body: Scalar, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 1, len: 1 }, - }, - write: None, - }, - Instance { - name: ::memories3, - instantiated: Module { - name: memories3, - .. - }, - }.w: CompiledValue { - layout: CompiledTypeLayout { - ty: Bundle { - /* offset = 0 */ - addr: UInt<3>, - /* offset = 3 */ - en: Bool, - /* offset = 4 */ - clk: Clock, - /* offset = 5 */ - data: Array, 8>, - /* offset = 69 */ - mask: Array, - }, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 19, - debug_data: [ - SlotDebugData { - name: "InstantiatedModule(memories3: memories3).memories3::w.addr", - ty: UInt<3>, - }, - SlotDebugData { - name: "InstantiatedModule(memories3: memories3).memories3::w.en", - ty: Bool, - }, - SlotDebugData { - name: "InstantiatedModule(memories3: memories3).memories3::w.clk", - ty: Clock, - }, - SlotDebugData { - name: "InstantiatedModule(memories3: memories3).memories3::w.data[0]", - ty: UInt<8>, - }, - SlotDebugData { - name: "InstantiatedModule(memories3: memories3).memories3::w.data[1]", - ty: UInt<8>, - }, - SlotDebugData { - name: "InstantiatedModule(memories3: memories3).memories3::w.data[2]", - ty: UInt<8>, - }, - SlotDebugData { - name: "InstantiatedModule(memories3: memories3).memories3::w.data[3]", - ty: UInt<8>, - }, - SlotDebugData { - name: "InstantiatedModule(memories3: memories3).memories3::w.data[4]", - ty: UInt<8>, - }, - SlotDebugData { - name: "InstantiatedModule(memories3: memories3).memories3::w.data[5]", - ty: UInt<8>, - }, - SlotDebugData { - name: "InstantiatedModule(memories3: memories3).memories3::w.data[6]", - ty: UInt<8>, - }, - SlotDebugData { - name: "InstantiatedModule(memories3: memories3).memories3::w.data[7]", - ty: UInt<8>, - }, - SlotDebugData { - name: "InstantiatedModule(memories3: memories3).memories3::w.mask[0]", - ty: Bool, - }, - SlotDebugData { - name: "InstantiatedModule(memories3: memories3).memories3::w.mask[1]", - ty: Bool, - }, - SlotDebugData { - name: "InstantiatedModule(memories3: memories3).memories3::w.mask[2]", - ty: Bool, - }, - SlotDebugData { - name: "InstantiatedModule(memories3: memories3).memories3::w.mask[3]", - ty: Bool, - }, - SlotDebugData { - name: "InstantiatedModule(memories3: memories3).memories3::w.mask[4]", - ty: Bool, - }, - SlotDebugData { - name: "InstantiatedModule(memories3: memories3).memories3::w.mask[5]", - ty: Bool, - }, - SlotDebugData { - name: "InstantiatedModule(memories3: memories3).memories3::w.mask[6]", - ty: Bool, - }, - SlotDebugData { - name: "InstantiatedModule(memories3: memories3).memories3::w.mask[7]", - ty: Bool, - }, - ], - .. - }, - }, - body: Bundle { - fields: [ - CompiledBundleField { - offset: TypeIndex { - small_slots: StatePartIndex(0), - big_slots: StatePartIndex(0), - }, - ty: CompiledTypeLayout { - ty: UInt<3>, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: UInt<3>, - }, - ], - .. - }, - }, - body: Scalar, - }, - }, - CompiledBundleField { - offset: TypeIndex { - small_slots: StatePartIndex(0), - big_slots: StatePartIndex(1), - }, - ty: CompiledTypeLayout { - ty: Bool, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: Bool, - }, - ], - .. - }, - }, - body: Scalar, - }, - }, - CompiledBundleField { - offset: TypeIndex { - small_slots: StatePartIndex(0), - big_slots: StatePartIndex(2), - }, - ty: CompiledTypeLayout { - ty: Clock, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: Clock, - }, - ], - .. - }, - }, - body: Scalar, - }, - }, - CompiledBundleField { - offset: TypeIndex { - small_slots: StatePartIndex(0), - big_slots: StatePartIndex(3), - }, - ty: CompiledTypeLayout { - ty: Array, 8>, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 8, - debug_data: [ - SlotDebugData { - name: "[0]", - ty: UInt<8>, - }, - SlotDebugData { - name: "[1]", - ty: UInt<8>, - }, - SlotDebugData { - name: "[2]", - ty: UInt<8>, - }, - SlotDebugData { - name: "[3]", - ty: UInt<8>, - }, - SlotDebugData { - name: "[4]", - ty: UInt<8>, - }, - SlotDebugData { - name: "[5]", - ty: UInt<8>, - }, - SlotDebugData { - name: "[6]", - ty: UInt<8>, - }, - SlotDebugData { - name: "[7]", - ty: UInt<8>, - }, - ], - .. - }, - }, - body: Array { - element: CompiledTypeLayout { - ty: UInt<8>, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: UInt<8>, - }, - ], - .. - }, - }, - body: Scalar, - }, - }, - }, - }, - CompiledBundleField { - offset: TypeIndex { - small_slots: StatePartIndex(0), - big_slots: StatePartIndex(11), - }, - ty: CompiledTypeLayout { - ty: Array, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 8, - debug_data: [ - SlotDebugData { - name: "[0]", - ty: Bool, - }, - SlotDebugData { - name: "[1]", - ty: Bool, - }, - SlotDebugData { - name: "[2]", - ty: Bool, - }, - SlotDebugData { - name: "[3]", - ty: Bool, - }, - SlotDebugData { - name: "[4]", - ty: Bool, - }, - SlotDebugData { - name: "[5]", - ty: Bool, - }, - SlotDebugData { - name: "[6]", - ty: Bool, - }, - SlotDebugData { - name: "[7]", - ty: Bool, - }, - ], - .. - }, - }, - body: Array { - element: CompiledTypeLayout { - ty: Bool, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: Bool, - }, - ], - .. - }, - }, - body: Scalar, - }, - }, - }, - }, - ], - }, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 11, len: 19 }, - }, - write: None, - }, - Instance { - name: ::memories3, - instantiated: Module { - name: memories3, - .. - }, - }.w.addr: CompiledValue { - layout: CompiledTypeLayout { - ty: UInt<3>, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: UInt<3>, - }, - ], - .. - }, - }, - body: Scalar, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 11, len: 1 }, - }, - write: None, - }, - Instance { - name: ::memories3, - instantiated: Module { - name: memories3, - .. - }, - }.w.clk: CompiledValue { - layout: CompiledTypeLayout { - ty: Clock, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: Clock, - }, - ], - .. - }, - }, - body: Scalar, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 13, len: 1 }, - }, - write: None, - }, - Instance { - name: ::memories3, - instantiated: Module { - name: memories3, - .. - }, - }.w.data: CompiledValue { - layout: CompiledTypeLayout { - ty: Array, 8>, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 8, - debug_data: [ - SlotDebugData { - name: "[0]", - ty: UInt<8>, - }, - SlotDebugData { - name: "[1]", - ty: UInt<8>, - }, - SlotDebugData { - name: "[2]", - ty: UInt<8>, - }, - SlotDebugData { - name: "[3]", - ty: UInt<8>, - }, - SlotDebugData { - name: "[4]", - ty: UInt<8>, - }, - SlotDebugData { - name: "[5]", - ty: UInt<8>, - }, - SlotDebugData { - name: "[6]", - ty: UInt<8>, - }, - SlotDebugData { - name: "[7]", - ty: UInt<8>, - }, - ], - .. - }, - }, - body: Array { - element: CompiledTypeLayout { - ty: UInt<8>, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: UInt<8>, - }, - ], - .. - }, - }, - body: Scalar, - }, - }, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 14, len: 8 }, - }, - write: None, - }, - Instance { - name: ::memories3, - instantiated: Module { - name: memories3, - .. - }, - }.w.data[0]: CompiledValue { - layout: CompiledTypeLayout { - ty: UInt<8>, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: UInt<8>, - }, - ], - .. - }, - }, - body: Scalar, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 14, len: 1 }, - }, - write: None, - }, - Instance { - name: ::memories3, - instantiated: Module { - name: memories3, - .. - }, - }.w.data[1]: CompiledValue { - layout: CompiledTypeLayout { - ty: UInt<8>, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: UInt<8>, - }, - ], - .. - }, - }, - body: Scalar, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 15, len: 1 }, - }, - write: None, - }, - Instance { - name: ::memories3, - instantiated: Module { - name: memories3, - .. - }, - }.w.data[2]: CompiledValue { - layout: CompiledTypeLayout { - ty: UInt<8>, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: UInt<8>, - }, - ], - .. - }, - }, - body: Scalar, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 16, len: 1 }, - }, - write: None, - }, - Instance { - name: ::memories3, - instantiated: Module { - name: memories3, - .. - }, - }.w.data[3]: CompiledValue { - layout: CompiledTypeLayout { - ty: UInt<8>, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: UInt<8>, - }, - ], - .. - }, - }, - body: Scalar, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 17, len: 1 }, - }, - write: None, - }, - Instance { - name: ::memories3, - instantiated: Module { - name: memories3, - .. - }, - }.w.data[4]: CompiledValue { - layout: CompiledTypeLayout { - ty: UInt<8>, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: UInt<8>, - }, - ], - .. - }, - }, - body: Scalar, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 18, len: 1 }, - }, - write: None, - }, - Instance { - name: ::memories3, - instantiated: Module { - name: memories3, - .. - }, - }.w.data[5]: CompiledValue { - layout: CompiledTypeLayout { - ty: UInt<8>, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: UInt<8>, - }, - ], - .. - }, - }, - body: Scalar, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 19, len: 1 }, - }, - write: None, - }, - Instance { - name: ::memories3, - instantiated: Module { - name: memories3, - .. - }, - }.w.data[6]: CompiledValue { - layout: CompiledTypeLayout { - ty: UInt<8>, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: UInt<8>, - }, - ], - .. - }, - }, - body: Scalar, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 20, len: 1 }, - }, - write: None, - }, - Instance { - name: ::memories3, - instantiated: Module { - name: memories3, - .. - }, - }.w.data[7]: CompiledValue { - layout: CompiledTypeLayout { - ty: UInt<8>, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: UInt<8>, - }, - ], - .. - }, - }, - body: Scalar, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 21, len: 1 }, - }, - write: None, - }, - Instance { - name: ::memories3, - instantiated: Module { - name: memories3, - .. - }, - }.w.en: CompiledValue { - layout: CompiledTypeLayout { - ty: Bool, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: Bool, - }, - ], - .. - }, - }, - body: Scalar, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 12, len: 1 }, - }, - write: None, - }, - Instance { - name: ::memories3, - instantiated: Module { - name: memories3, - .. - }, - }.w.mask: CompiledValue { - layout: CompiledTypeLayout { - ty: Array, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 8, - debug_data: [ - SlotDebugData { - name: "[0]", - ty: Bool, - }, - SlotDebugData { - name: "[1]", - ty: Bool, - }, - SlotDebugData { - name: "[2]", - ty: Bool, - }, - SlotDebugData { - name: "[3]", - ty: Bool, - }, - SlotDebugData { - name: "[4]", - ty: Bool, - }, - SlotDebugData { - name: "[5]", - ty: Bool, - }, - SlotDebugData { - name: "[6]", - ty: Bool, - }, - SlotDebugData { - name: "[7]", - ty: Bool, - }, - ], - .. - }, - }, - body: Array { - element: CompiledTypeLayout { - ty: Bool, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: Bool, - }, - ], - .. - }, - }, - body: Scalar, - }, - }, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 22, len: 8 }, - }, - write: None, - }, - Instance { - name: ::memories3, - instantiated: Module { - name: memories3, - .. - }, - }.w.mask[0]: CompiledValue { - layout: CompiledTypeLayout { - ty: Bool, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: Bool, - }, - ], - .. - }, - }, - body: Scalar, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 22, len: 1 }, - }, - write: None, - }, - Instance { - name: ::memories3, - instantiated: Module { - name: memories3, - .. - }, - }.w.mask[1]: CompiledValue { - layout: CompiledTypeLayout { - ty: Bool, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: Bool, - }, - ], - .. - }, - }, - body: Scalar, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 23, len: 1 }, - }, - write: None, - }, - Instance { - name: ::memories3, - instantiated: Module { - name: memories3, - .. - }, - }.w.mask[2]: CompiledValue { - layout: CompiledTypeLayout { - ty: Bool, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: Bool, - }, - ], - .. - }, - }, - body: Scalar, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 24, len: 1 }, - }, - write: None, - }, - Instance { - name: ::memories3, - instantiated: Module { - name: memories3, - .. - }, - }.w.mask[3]: CompiledValue { - layout: CompiledTypeLayout { - ty: Bool, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: Bool, - }, - ], - .. - }, - }, - body: Scalar, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 25, len: 1 }, - }, - write: None, - }, - Instance { - name: ::memories3, - instantiated: Module { - name: memories3, - .. - }, - }.w.mask[4]: CompiledValue { - layout: CompiledTypeLayout { - ty: Bool, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: Bool, - }, - ], - .. - }, - }, - body: Scalar, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 26, len: 1 }, - }, - write: None, - }, - Instance { - name: ::memories3, - instantiated: Module { - name: memories3, - .. - }, - }.w.mask[5]: CompiledValue { - layout: CompiledTypeLayout { - ty: Bool, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: Bool, - }, - ], - .. - }, - }, - body: Scalar, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 27, len: 1 }, - }, - write: None, - }, - Instance { - name: ::memories3, - instantiated: Module { - name: memories3, - .. - }, - }.w.mask[6]: CompiledValue { - layout: CompiledTypeLayout { - ty: Bool, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: Bool, - }, - ], - .. - }, - }, - body: Scalar, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 28, len: 1 }, - }, - write: None, - }, - Instance { - name: ::memories3, - instantiated: Module { - name: memories3, - .. - }, - }.w.mask[7]: CompiledValue { - layout: CompiledTypeLayout { - ty: Bool, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: Bool, - }, - ], - .. - }, - }, - body: Scalar, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 29, len: 1 }, - }, - write: None, + }.r, + Instance { + name: ::memories3, + instantiated: Module { + name: memories3, + .. + }, + }.r.addr, + Instance { + name: ::memories3, + instantiated: Module { + name: memories3, + .. + }, + }.r.clk, + Instance { + name: ::memories3, + instantiated: Module { + name: memories3, + .. + }, + }.r.data, + Instance { + name: ::memories3, + instantiated: Module { + name: memories3, + .. + }, + }.r.data[0], + Instance { + name: ::memories3, + instantiated: Module { + name: memories3, + .. + }, + }.r.data[1], + Instance { + name: ::memories3, + instantiated: Module { + name: memories3, + .. + }, + }.r.data[2], + Instance { + name: ::memories3, + instantiated: Module { + name: memories3, + .. + }, + }.r.data[3], + Instance { + name: ::memories3, + instantiated: Module { + name: memories3, + .. + }, + }.r.data[4], + Instance { + name: ::memories3, + instantiated: Module { + name: memories3, + .. + }, + }.r.data[5], + Instance { + name: ::memories3, + instantiated: Module { + name: memories3, + .. + }, + }.r.data[6], + Instance { + name: ::memories3, + instantiated: Module { + name: memories3, + .. + }, + }.r.data[7], + Instance { + name: ::memories3, + instantiated: Module { + name: memories3, + .. + }, + }.r.en, + Instance { + name: ::memories3, + instantiated: Module { + name: memories3, + .. + }, + }.w, + Instance { + name: ::memories3, + instantiated: Module { + name: memories3, + .. + }, + }.w.addr, + Instance { + name: ::memories3, + instantiated: Module { + name: memories3, + .. + }, + }.w.clk, + Instance { + name: ::memories3, + instantiated: Module { + name: memories3, + .. + }, + }.w.data, + Instance { + name: ::memories3, + instantiated: Module { + name: memories3, + .. + }, + }.w.data[0], + Instance { + name: ::memories3, + instantiated: Module { + name: memories3, + .. + }, + }.w.data[1], + Instance { + name: ::memories3, + instantiated: Module { + name: memories3, + .. + }, + }.w.data[2], + Instance { + name: ::memories3, + instantiated: Module { + name: memories3, + .. + }, + }.w.data[3], + Instance { + name: ::memories3, + instantiated: Module { + name: memories3, + .. + }, + }.w.data[4], + Instance { + name: ::memories3, + instantiated: Module { + name: memories3, + .. + }, + }.w.data[5], + Instance { + name: ::memories3, + instantiated: Module { + name: memories3, + .. + }, + }.w.data[6], + Instance { + name: ::memories3, + instantiated: Module { + name: memories3, + .. + }, + }.w.data[7], + Instance { + name: ::memories3, + instantiated: Module { + name: memories3, + .. + }, + }.w.en, + Instance { + name: ::memories3, + instantiated: Module { + name: memories3, + .. + }, + }.w.mask, + Instance { + name: ::memories3, + instantiated: Module { + name: memories3, + .. + }, + }.w.mask[0], + Instance { + name: ::memories3, + instantiated: Module { + name: memories3, + .. + }, + }.w.mask[1], + Instance { + name: ::memories3, + instantiated: Module { + name: memories3, + .. + }, + }.w.mask[2], + Instance { + name: ::memories3, + instantiated: Module { + name: memories3, + .. + }, + }.w.mask[3], + Instance { + name: ::memories3, + instantiated: Module { + name: memories3, + .. + }, + }.w.mask[4], + Instance { + name: ::memories3, + instantiated: Module { + name: memories3, + .. + }, + }.w.mask[5], + Instance { + name: ::memories3, + instantiated: Module { + name: memories3, + .. + }, + }.w.mask[6], + Instance { + name: ::memories3, + instantiated: Module { + name: memories3, + .. + }, + }.w.mask[7], }, + did_initial_settle: true, }, - made_initial_step: true, - needs_settle: false, + extern_modules: [], + state_ready_to_run: false, trace_decls: TraceModule { name: "memories3", children: [ diff --git a/crates/fayalite/tests/sim/expected/memories3.vcd b/crates/fayalite/tests/sim/expected/memories3.vcd index 328fcaa..5768560 100644 --- a/crates/fayalite/tests/sim/expected/memories3.vcd +++ b/crates/fayalite/tests/sim/expected/memories3.vcd @@ -42,7 +42,7 @@ $upscope $end $upscope $end $scope struct mem $end $scope struct contents $end -$scope struct [0] $end +$scope struct \[0] $end $scope struct mem $end $var reg 8 ] \[0] $end $var reg 8 e \[1] $end @@ -54,7 +54,7 @@ $var reg 8 /" \[6] $end $var reg 8 7" \[7] $end $upscope $end $upscope $end -$scope struct [1] $end +$scope struct \[1] $end $scope struct mem $end $var reg 8 ^ \[0] $end $var reg 8 f \[1] $end @@ -66,7 +66,7 @@ $var reg 8 0" \[6] $end $var reg 8 8" \[7] $end $upscope $end $upscope $end -$scope struct [2] $end +$scope struct \[2] $end $scope struct mem $end $var reg 8 _ \[0] $end $var reg 8 g \[1] $end @@ -78,7 +78,7 @@ $var reg 8 1" \[6] $end $var reg 8 9" \[7] $end $upscope $end $upscope $end -$scope struct [3] $end +$scope struct \[3] $end $scope struct mem $end $var reg 8 ` \[0] $end $var reg 8 h \[1] $end @@ -90,7 +90,7 @@ $var reg 8 2" \[6] $end $var reg 8 :" \[7] $end $upscope $end $upscope $end -$scope struct [4] $end +$scope struct \[4] $end $scope struct mem $end $var reg 8 a \[0] $end $var reg 8 i \[1] $end @@ -102,7 +102,7 @@ $var reg 8 3" \[6] $end $var reg 8 ;" \[7] $end $upscope $end $upscope $end -$scope struct [5] $end +$scope struct \[5] $end $scope struct mem $end $var reg 8 b \[0] $end $var reg 8 j \[1] $end @@ -114,7 +114,7 @@ $var reg 8 4" \[6] $end $var reg 8 <" \[7] $end $upscope $end $upscope $end -$scope struct [6] $end +$scope struct \[6] $end $scope struct mem $end $var reg 8 c \[0] $end $var reg 8 k \[1] $end @@ -126,7 +126,7 @@ $var reg 8 5" \[6] $end $var reg 8 =" \[7] $end $upscope $end $upscope $end -$scope struct [7] $end +$scope struct \[7] $end $scope struct mem $end $var reg 8 d \[0] $end $var reg 8 l \[1] $end diff --git a/crates/fayalite/tests/sim/expected/mod1.txt b/crates/fayalite/tests/sim/expected/mod1.txt index 5c2b7eb..4ef02b2 100644 --- a/crates/fayalite/tests/sim/expected/mod1.txt +++ b/crates/fayalite/tests/sim/expected/mod1.txt @@ -82,6 +82,12 @@ Simulation { ], .. }, + sim_only_slots: StatePartLayout { + len: 0, + debug_data: [], + layout_data: [], + .. + }, }, memories: StatePartLayout { len: 0, @@ -208,6 +214,9 @@ Simulation { 15, ], }, + sim_only_slots: StatePart { + value: [], + }, }, io: Instance { name: ::mod1, @@ -216,313 +225,58 @@ Simulation { .. }, }, - uninitialized_inputs: {}, - io_targets: { - Instance { - name: ::mod1, - instantiated: Module { - name: mod1, - .. - }, - }.o: CompiledValue { - layout: CompiledTypeLayout { - ty: Bundle { - #[hdl(flip)] /* offset = 0 */ - i: UInt<4>, - /* offset = 4 */ - o: SInt<2>, - #[hdl(flip)] /* offset = 6 */ - i2: SInt<2>, - /* offset = 8 */ - o2: UInt<4>, + main_module: SimulationModuleState { + base_targets: [ + Instance { + name: ::mod1, + instantiated: Module { + name: mod1, + .. }, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 4, - debug_data: [ - SlotDebugData { - name: "InstantiatedModule(mod1: mod1).mod1::o.i", - ty: UInt<4>, - }, - SlotDebugData { - name: "InstantiatedModule(mod1: mod1).mod1::o.o", - ty: SInt<2>, - }, - SlotDebugData { - name: "InstantiatedModule(mod1: mod1).mod1::o.i2", - ty: SInt<2>, - }, - SlotDebugData { - name: "InstantiatedModule(mod1: mod1).mod1::o.o2", - ty: UInt<4>, - }, - ], - .. - }, + }.o, + ], + uninitialized_ios: {}, + io_targets: { + Instance { + name: ::mod1, + instantiated: Module { + name: mod1, + .. }, - body: Bundle { - fields: [ - CompiledBundleField { - offset: TypeIndex { - small_slots: StatePartIndex(0), - big_slots: StatePartIndex(0), - }, - ty: CompiledTypeLayout { - ty: UInt<4>, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: UInt<4>, - }, - ], - .. - }, - }, - body: Scalar, - }, - }, - CompiledBundleField { - offset: TypeIndex { - small_slots: StatePartIndex(0), - big_slots: StatePartIndex(1), - }, - ty: CompiledTypeLayout { - ty: SInt<2>, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: SInt<2>, - }, - ], - .. - }, - }, - body: Scalar, - }, - }, - CompiledBundleField { - offset: TypeIndex { - small_slots: StatePartIndex(0), - big_slots: StatePartIndex(2), - }, - ty: CompiledTypeLayout { - ty: SInt<2>, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: SInt<2>, - }, - ], - .. - }, - }, - body: Scalar, - }, - }, - CompiledBundleField { - offset: TypeIndex { - small_slots: StatePartIndex(0), - big_slots: StatePartIndex(3), - }, - ty: CompiledTypeLayout { - ty: UInt<4>, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: UInt<4>, - }, - ], - .. - }, - }, - body: Scalar, - }, - }, - ], + }.o, + Instance { + name: ::mod1, + instantiated: Module { + name: mod1, + .. }, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 0, len: 4 }, - }, - write: None, - }, - Instance { - name: ::mod1, - instantiated: Module { - name: mod1, - .. - }, - }.o.i: CompiledValue { - layout: CompiledTypeLayout { - ty: UInt<4>, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: UInt<4>, - }, - ], - .. - }, - }, - body: Scalar, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 0, len: 1 }, - }, - write: None, - }, - Instance { - name: ::mod1, - instantiated: Module { - name: mod1, - .. - }, - }.o.i2: CompiledValue { - layout: CompiledTypeLayout { - ty: SInt<2>, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: SInt<2>, - }, - ], - .. - }, - }, - body: Scalar, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 2, len: 1 }, - }, - write: None, - }, - Instance { - name: ::mod1, - instantiated: Module { - name: mod1, - .. - }, - }.o.o: CompiledValue { - layout: CompiledTypeLayout { - ty: SInt<2>, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: SInt<2>, - }, - ], - .. - }, - }, - body: Scalar, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 1, len: 1 }, - }, - write: None, - }, - Instance { - name: ::mod1, - instantiated: Module { - name: mod1, - .. - }, - }.o.o2: CompiledValue { - layout: CompiledTypeLayout { - ty: UInt<4>, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: UInt<4>, - }, - ], - .. - }, - }, - body: Scalar, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 3, len: 1 }, - }, - write: None, + }.o.i, + Instance { + name: ::mod1, + instantiated: Module { + name: mod1, + .. + }, + }.o.i2, + Instance { + name: ::mod1, + instantiated: Module { + name: mod1, + .. + }, + }.o.o, + Instance { + name: ::mod1, + instantiated: Module { + name: mod1, + .. + }, + }.o.o2, }, + did_initial_settle: true, }, - made_initial_step: true, - needs_settle: false, + extern_modules: [], + state_ready_to_run: false, trace_decls: TraceModule { name: "mod1", children: [ diff --git a/crates/fayalite/tests/sim/expected/ripple_counter.txt b/crates/fayalite/tests/sim/expected/ripple_counter.txt new file mode 100644 index 0000000..9e4e0d1 --- /dev/null +++ b/crates/fayalite/tests/sim/expected/ripple_counter.txt @@ -0,0 +1,1603 @@ +Simulation { + state: State { + insns: Insns { + state_layout: StateLayout { + ty: TypeLayout { + small_slots: StatePartLayout { + len: 9, + debug_data: [ + SlotDebugData { + name: "", + ty: Bool, + }, + SlotDebugData { + name: "", + ty: Bool, + }, + SlotDebugData { + name: "", + ty: Bool, + }, + SlotDebugData { + name: "", + ty: Bool, + }, + SlotDebugData { + name: "", + ty: Bool, + }, + SlotDebugData { + name: "", + ty: Bool, + }, + SlotDebugData { + name: "", + ty: Bool, + }, + SlotDebugData { + name: "", + ty: Bool, + }, + SlotDebugData { + name: "", + ty: Bool, + }, + ], + .. + }, + big_slots: StatePartLayout { + len: 58, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(ripple_counter: ripple_counter).ripple_counter::clk", + ty: Clock, + }, + SlotDebugData { + name: "InstantiatedModule(ripple_counter: ripple_counter).ripple_counter::o", + ty: UInt<6>, + }, + SlotDebugData { + name: "InstantiatedModule(ripple_counter: ripple_counter).ripple_counter::bits[0]", + ty: Bool, + }, + SlotDebugData { + name: "InstantiatedModule(ripple_counter: ripple_counter).ripple_counter::bits[1]", + ty: Bool, + }, + SlotDebugData { + name: "InstantiatedModule(ripple_counter: ripple_counter).ripple_counter::bits[2]", + ty: Bool, + }, + SlotDebugData { + name: "InstantiatedModule(ripple_counter: ripple_counter).ripple_counter::bits[3]", + ty: Bool, + }, + SlotDebugData { + name: "InstantiatedModule(ripple_counter: ripple_counter).ripple_counter::bits[4]", + ty: Bool, + }, + SlotDebugData { + name: "InstantiatedModule(ripple_counter: ripple_counter).ripple_counter::bits[5]", + ty: Bool, + }, + SlotDebugData { + name: "", + ty: UInt<1>, + }, + SlotDebugData { + name: "", + ty: UInt<1>, + }, + SlotDebugData { + name: "", + ty: UInt<2>, + }, + SlotDebugData { + name: "", + ty: UInt<2>, + }, + SlotDebugData { + name: "", + ty: UInt<1>, + }, + SlotDebugData { + name: "", + ty: UInt<3>, + }, + SlotDebugData { + name: "", + ty: UInt<3>, + }, + SlotDebugData { + name: "", + ty: UInt<1>, + }, + SlotDebugData { + name: "", + ty: UInt<4>, + }, + SlotDebugData { + name: "", + ty: UInt<4>, + }, + SlotDebugData { + name: "", + ty: UInt<1>, + }, + SlotDebugData { + name: "", + ty: UInt<5>, + }, + SlotDebugData { + name: "", + ty: UInt<5>, + }, + SlotDebugData { + name: "", + ty: UInt<1>, + }, + SlotDebugData { + name: "", + ty: UInt<6>, + }, + SlotDebugData { + name: "", + ty: UInt<6>, + }, + SlotDebugData { + name: "InstantiatedModule(ripple_counter: ripple_counter).ripple_counter::bit_reg_0", + ty: Bool, + }, + SlotDebugData { + name: "InstantiatedModule(ripple_counter: ripple_counter).ripple_counter::bit_reg_0$next", + ty: Bool, + }, + SlotDebugData { + name: ".clk", + ty: Clock, + }, + SlotDebugData { + name: ".rst", + ty: SyncReset, + }, + SlotDebugData { + name: "", + ty: Bool, + }, + SlotDebugData { + name: "", + ty: SyncReset, + }, + SlotDebugData { + name: "", + ty: Bool, + }, + SlotDebugData { + name: "InstantiatedModule(ripple_counter: ripple_counter).ripple_counter::bit_reg_1.clk", + ty: Clock, + }, + SlotDebugData { + name: "InstantiatedModule(ripple_counter: ripple_counter).ripple_counter::bit_reg_1.o", + ty: Bool, + }, + SlotDebugData { + name: "InstantiatedModule(ripple_counter.bit_reg_1: sw_reg).sw_reg::clk", + ty: Clock, + }, + SlotDebugData { + name: "InstantiatedModule(ripple_counter.bit_reg_1: sw_reg).sw_reg::o", + ty: Bool, + }, + SlotDebugData { + name: "", + ty: Clock, + }, + SlotDebugData { + name: "InstantiatedModule(ripple_counter: ripple_counter).ripple_counter::bit_reg_2", + ty: Bool, + }, + SlotDebugData { + name: "InstantiatedModule(ripple_counter: ripple_counter).ripple_counter::bit_reg_2$next", + ty: Bool, + }, + SlotDebugData { + name: ".clk", + ty: Clock, + }, + SlotDebugData { + name: ".rst", + ty: SyncReset, + }, + SlotDebugData { + name: "", + ty: Clock, + }, + SlotDebugData { + name: "", + ty: Bool, + }, + SlotDebugData { + name: "InstantiatedModule(ripple_counter: ripple_counter).ripple_counter::bit_reg_3.clk", + ty: Clock, + }, + SlotDebugData { + name: "InstantiatedModule(ripple_counter: ripple_counter).ripple_counter::bit_reg_3.o", + ty: Bool, + }, + SlotDebugData { + name: "InstantiatedModule(ripple_counter.bit_reg_3: sw_reg).sw_reg::clk", + ty: Clock, + }, + SlotDebugData { + name: "InstantiatedModule(ripple_counter.bit_reg_3: sw_reg).sw_reg::o", + ty: Bool, + }, + SlotDebugData { + name: "", + ty: Clock, + }, + SlotDebugData { + name: "InstantiatedModule(ripple_counter: ripple_counter).ripple_counter::bit_reg_4", + ty: Bool, + }, + SlotDebugData { + name: "InstantiatedModule(ripple_counter: ripple_counter).ripple_counter::bit_reg_4$next", + ty: Bool, + }, + SlotDebugData { + name: ".clk", + ty: Clock, + }, + SlotDebugData { + name: ".rst", + ty: SyncReset, + }, + SlotDebugData { + name: "", + ty: Clock, + }, + SlotDebugData { + name: "", + ty: Bool, + }, + SlotDebugData { + name: "InstantiatedModule(ripple_counter: ripple_counter).ripple_counter::bit_reg_5.clk", + ty: Clock, + }, + SlotDebugData { + name: "InstantiatedModule(ripple_counter: ripple_counter).ripple_counter::bit_reg_5.o", + ty: Bool, + }, + SlotDebugData { + name: "InstantiatedModule(ripple_counter.bit_reg_5: sw_reg).sw_reg::clk", + ty: Clock, + }, + SlotDebugData { + name: "InstantiatedModule(ripple_counter.bit_reg_5: sw_reg).sw_reg::o", + ty: Bool, + }, + SlotDebugData { + name: "", + ty: Clock, + }, + ], + .. + }, + sim_only_slots: StatePartLayout { + len: 0, + debug_data: [], + layout_data: [], + .. + }, + }, + memories: StatePartLayout { + len: 0, + debug_data: [], + layout_data: [], + .. + }, + }, + insns: [ + // at: module-XXXXXXXXXX.rs:9:1 + 0: Copy { + dest: StatePartIndex(54), // (0x0) SlotDebugData { name: "InstantiatedModule(ripple_counter: ripple_counter).ripple_counter::bit_reg_5.o", ty: Bool }, + src: StatePartIndex(56), // (0x0) SlotDebugData { name: "InstantiatedModule(ripple_counter.bit_reg_5: sw_reg).sw_reg::o", ty: Bool }, + }, + // at: module-XXXXXXXXXX.rs:11:1 + 1: Copy { + dest: StatePartIndex(7), // (0x0) SlotDebugData { name: "InstantiatedModule(ripple_counter: ripple_counter).ripple_counter::bits[5]", ty: Bool }, + src: StatePartIndex(54), // (0x0) SlotDebugData { name: "InstantiatedModule(ripple_counter: ripple_counter).ripple_counter::bit_reg_5.o", ty: Bool }, + }, + // at: module-XXXXXXXXXX.rs:1:1 + 2: NotU { + dest: StatePartIndex(52), // (0x1) SlotDebugData { name: "", ty: Bool }, + src: StatePartIndex(47), // (0x0) SlotDebugData { name: "InstantiatedModule(ripple_counter: ripple_counter).ripple_counter::bit_reg_4", ty: Bool }, + width: 1, + }, + // at: module-XXXXXXXXXX.rs:8:1 + 3: Copy { + dest: StatePartIndex(48), // (0x1) SlotDebugData { name: "InstantiatedModule(ripple_counter: ripple_counter).ripple_counter::bit_reg_4$next", ty: Bool }, + src: StatePartIndex(52), // (0x1) SlotDebugData { name: "", ty: Bool }, + }, + // at: module-XXXXXXXXXX.rs:7:1 + 4: Copy { + dest: StatePartIndex(6), // (0x0) SlotDebugData { name: "InstantiatedModule(ripple_counter: ripple_counter).ripple_counter::bits[4]", ty: Bool }, + src: StatePartIndex(47), // (0x0) SlotDebugData { name: "InstantiatedModule(ripple_counter: ripple_counter).ripple_counter::bit_reg_4", ty: Bool }, + }, + // at: module-XXXXXXXXXX.rs:1:1 + 5: Copy { + dest: StatePartIndex(57), // (0x0) SlotDebugData { name: "", ty: Clock }, + src: StatePartIndex(6), // (0x0) SlotDebugData { name: "InstantiatedModule(ripple_counter: ripple_counter).ripple_counter::bits[4]", ty: Bool }, + }, + // at: module-XXXXXXXXXX.rs:10:1 + 6: Copy { + dest: StatePartIndex(53), // (0x0) SlotDebugData { name: "InstantiatedModule(ripple_counter: ripple_counter).ripple_counter::bit_reg_5.clk", ty: Clock }, + src: StatePartIndex(57), // (0x0) SlotDebugData { name: "", ty: Clock }, + }, + // at: module-XXXXXXXXXX.rs:9:1 + 7: Copy { + dest: StatePartIndex(55), // (0x0) SlotDebugData { name: "InstantiatedModule(ripple_counter.bit_reg_5: sw_reg).sw_reg::clk", ty: Clock }, + src: StatePartIndex(53), // (0x0) SlotDebugData { name: "InstantiatedModule(ripple_counter: ripple_counter).ripple_counter::bit_reg_5.clk", ty: Clock }, + }, + 8: Copy { + dest: StatePartIndex(43), // (0x0) SlotDebugData { name: "InstantiatedModule(ripple_counter: ripple_counter).ripple_counter::bit_reg_3.o", ty: Bool }, + src: StatePartIndex(45), // (0x0) SlotDebugData { name: "InstantiatedModule(ripple_counter.bit_reg_3: sw_reg).sw_reg::o", ty: Bool }, + }, + // at: module-XXXXXXXXXX.rs:11:1 + 9: Copy { + dest: StatePartIndex(5), // (0x0) SlotDebugData { name: "InstantiatedModule(ripple_counter: ripple_counter).ripple_counter::bits[3]", ty: Bool }, + src: StatePartIndex(43), // (0x0) SlotDebugData { name: "InstantiatedModule(ripple_counter: ripple_counter).ripple_counter::bit_reg_3.o", ty: Bool }, + }, + // at: module-XXXXXXXXXX.rs:1:1 + 10: Copy { + dest: StatePartIndex(51), // (0x0) SlotDebugData { name: "", ty: Clock }, + src: StatePartIndex(5), // (0x0) SlotDebugData { name: "InstantiatedModule(ripple_counter: ripple_counter).ripple_counter::bits[3]", ty: Bool }, + }, + 11: NotU { + dest: StatePartIndex(41), // (0x1) SlotDebugData { name: "", ty: Bool }, + src: StatePartIndex(36), // (0x0) SlotDebugData { name: "InstantiatedModule(ripple_counter: ripple_counter).ripple_counter::bit_reg_2", ty: Bool }, + width: 1, + }, + // at: module-XXXXXXXXXX.rs:8:1 + 12: Copy { + dest: StatePartIndex(37), // (0x1) SlotDebugData { name: "InstantiatedModule(ripple_counter: ripple_counter).ripple_counter::bit_reg_2$next", ty: Bool }, + src: StatePartIndex(41), // (0x1) SlotDebugData { name: "", ty: Bool }, + }, + // at: module-XXXXXXXXXX.rs:7:1 + 13: Copy { + dest: StatePartIndex(4), // (0x0) SlotDebugData { name: "InstantiatedModule(ripple_counter: ripple_counter).ripple_counter::bits[2]", ty: Bool }, + src: StatePartIndex(36), // (0x0) SlotDebugData { name: "InstantiatedModule(ripple_counter: ripple_counter).ripple_counter::bit_reg_2", ty: Bool }, + }, + // at: module-XXXXXXXXXX.rs:1:1 + 14: Copy { + dest: StatePartIndex(46), // (0x0) SlotDebugData { name: "", ty: Clock }, + src: StatePartIndex(4), // (0x0) SlotDebugData { name: "InstantiatedModule(ripple_counter: ripple_counter).ripple_counter::bits[2]", ty: Bool }, + }, + // at: module-XXXXXXXXXX.rs:10:1 + 15: Copy { + dest: StatePartIndex(42), // (0x0) SlotDebugData { name: "InstantiatedModule(ripple_counter: ripple_counter).ripple_counter::bit_reg_3.clk", ty: Clock }, + src: StatePartIndex(46), // (0x0) SlotDebugData { name: "", ty: Clock }, + }, + // at: module-XXXXXXXXXX.rs:9:1 + 16: Copy { + dest: StatePartIndex(44), // (0x0) SlotDebugData { name: "InstantiatedModule(ripple_counter.bit_reg_3: sw_reg).sw_reg::clk", ty: Clock }, + src: StatePartIndex(42), // (0x0) SlotDebugData { name: "InstantiatedModule(ripple_counter: ripple_counter).ripple_counter::bit_reg_3.clk", ty: Clock }, + }, + 17: Copy { + dest: StatePartIndex(32), // (0x0) SlotDebugData { name: "InstantiatedModule(ripple_counter: ripple_counter).ripple_counter::bit_reg_1.o", ty: Bool }, + src: StatePartIndex(34), // (0x0) SlotDebugData { name: "InstantiatedModule(ripple_counter.bit_reg_1: sw_reg).sw_reg::o", ty: Bool }, + }, + // at: module-XXXXXXXXXX.rs:11:1 + 18: Copy { + dest: StatePartIndex(3), // (0x0) SlotDebugData { name: "InstantiatedModule(ripple_counter: ripple_counter).ripple_counter::bits[1]", ty: Bool }, + src: StatePartIndex(32), // (0x0) SlotDebugData { name: "InstantiatedModule(ripple_counter: ripple_counter).ripple_counter::bit_reg_1.o", ty: Bool }, + }, + // at: module-XXXXXXXXXX.rs:1:1 + 19: Copy { + dest: StatePartIndex(40), // (0x0) SlotDebugData { name: "", ty: Clock }, + src: StatePartIndex(3), // (0x0) SlotDebugData { name: "InstantiatedModule(ripple_counter: ripple_counter).ripple_counter::bits[1]", ty: Bool }, + }, + 20: NotU { + dest: StatePartIndex(30), // (0x1) SlotDebugData { name: "", ty: Bool }, + src: StatePartIndex(24), // (0x0) SlotDebugData { name: "InstantiatedModule(ripple_counter: ripple_counter).ripple_counter::bit_reg_0", ty: Bool }, + width: 1, + }, + // at: module-XXXXXXXXXX.rs:8:1 + 21: Copy { + dest: StatePartIndex(25), // (0x1) SlotDebugData { name: "InstantiatedModule(ripple_counter: ripple_counter).ripple_counter::bit_reg_0$next", ty: Bool }, + src: StatePartIndex(30), // (0x1) SlotDebugData { name: "", ty: Bool }, + }, + // at: module-XXXXXXXXXX.rs:7:1 + 22: Copy { + dest: StatePartIndex(2), // (0x0) SlotDebugData { name: "InstantiatedModule(ripple_counter: ripple_counter).ripple_counter::bits[0]", ty: Bool }, + src: StatePartIndex(24), // (0x0) SlotDebugData { name: "InstantiatedModule(ripple_counter: ripple_counter).ripple_counter::bit_reg_0", ty: Bool }, + }, + // at: module-XXXXXXXXXX.rs:1:1 + 23: Copy { + dest: StatePartIndex(35), // (0x0) SlotDebugData { name: "", ty: Clock }, + src: StatePartIndex(2), // (0x0) SlotDebugData { name: "InstantiatedModule(ripple_counter: ripple_counter).ripple_counter::bits[0]", ty: Bool }, + }, + // at: module-XXXXXXXXXX.rs:10:1 + 24: Copy { + dest: StatePartIndex(31), // (0x0) SlotDebugData { name: "InstantiatedModule(ripple_counter: ripple_counter).ripple_counter::bit_reg_1.clk", ty: Clock }, + src: StatePartIndex(35), // (0x0) SlotDebugData { name: "", ty: Clock }, + }, + // at: module-XXXXXXXXXX.rs:9:1 + 25: Copy { + dest: StatePartIndex(33), // (0x0) SlotDebugData { name: "InstantiatedModule(ripple_counter.bit_reg_1: sw_reg).sw_reg::clk", ty: Clock }, + src: StatePartIndex(31), // (0x0) SlotDebugData { name: "InstantiatedModule(ripple_counter: ripple_counter).ripple_counter::bit_reg_1.clk", ty: Clock }, + }, + // at: module-XXXXXXXXXX.rs:1:1 + 26: Const { + dest: StatePartIndex(28), // (0x0) SlotDebugData { name: "", ty: Bool }, + value: 0x0, + }, + 27: Copy { + dest: StatePartIndex(29), // (0x0) SlotDebugData { name: "", ty: SyncReset }, + src: StatePartIndex(28), // (0x0) SlotDebugData { name: "", ty: Bool }, + }, + 28: Copy { + dest: StatePartIndex(26), // (0x1) SlotDebugData { name: ".clk", ty: Clock }, + src: StatePartIndex(0), // (0x1) SlotDebugData { name: "InstantiatedModule(ripple_counter: ripple_counter).ripple_counter::clk", ty: Clock }, + }, + 29: Copy { + dest: StatePartIndex(27), // (0x0) SlotDebugData { name: ".rst", ty: SyncReset }, + src: StatePartIndex(29), // (0x0) SlotDebugData { name: "", ty: SyncReset }, + }, + // at: module-XXXXXXXXXX.rs:6:1 + 30: IsNonZeroDestIsSmall { + dest: StatePartIndex(2), // (0x1 1) SlotDebugData { name: "", ty: Bool }, + src: StatePartIndex(26), // (0x1) SlotDebugData { name: ".clk", ty: Clock }, + }, + 31: AndSmall { + dest: StatePartIndex(1), // (0x0 0) SlotDebugData { name: "", ty: Bool }, + lhs: StatePartIndex(2), // (0x1 1) SlotDebugData { name: "", ty: Bool }, + rhs: StatePartIndex(0), // (0x0 0) SlotDebugData { name: "", ty: Bool }, + }, + // at: module-XXXXXXXXXX.rs:1:1 + 32: Copy { + dest: StatePartIndex(38), // (0x0) SlotDebugData { name: ".clk", ty: Clock }, + src: StatePartIndex(40), // (0x0) SlotDebugData { name: "", ty: Clock }, + }, + 33: Copy { + dest: StatePartIndex(39), // (0x0) SlotDebugData { name: ".rst", ty: SyncReset }, + src: StatePartIndex(29), // (0x0) SlotDebugData { name: "", ty: SyncReset }, + }, + // at: module-XXXXXXXXXX.rs:6:1 + 34: IsNonZeroDestIsSmall { + dest: StatePartIndex(5), // (0x0 0) SlotDebugData { name: "", ty: Bool }, + src: StatePartIndex(38), // (0x0) SlotDebugData { name: ".clk", ty: Clock }, + }, + 35: AndSmall { + dest: StatePartIndex(4), // (0x0 0) SlotDebugData { name: "", ty: Bool }, + lhs: StatePartIndex(5), // (0x0 0) SlotDebugData { name: "", ty: Bool }, + rhs: StatePartIndex(3), // (0x1 1) SlotDebugData { name: "", ty: Bool }, + }, + // at: module-XXXXXXXXXX.rs:1:1 + 36: Copy { + dest: StatePartIndex(49), // (0x0) SlotDebugData { name: ".clk", ty: Clock }, + src: StatePartIndex(51), // (0x0) SlotDebugData { name: "", ty: Clock }, + }, + 37: Copy { + dest: StatePartIndex(50), // (0x0) SlotDebugData { name: ".rst", ty: SyncReset }, + src: StatePartIndex(29), // (0x0) SlotDebugData { name: "", ty: SyncReset }, + }, + // at: module-XXXXXXXXXX.rs:6:1 + 38: IsNonZeroDestIsSmall { + dest: StatePartIndex(8), // (0x0 0) SlotDebugData { name: "", ty: Bool }, + src: StatePartIndex(49), // (0x0) SlotDebugData { name: ".clk", ty: Clock }, + }, + 39: AndSmall { + dest: StatePartIndex(7), // (0x0 0) SlotDebugData { name: "", ty: Bool }, + lhs: StatePartIndex(8), // (0x0 0) SlotDebugData { name: "", ty: Bool }, + rhs: StatePartIndex(6), // (0x1 1) SlotDebugData { name: "", ty: Bool }, + }, + // at: module-XXXXXXXXXX.rs:1:1 + 40: Copy { + dest: StatePartIndex(21), // (0x0) SlotDebugData { name: "", ty: UInt<1> }, + src: StatePartIndex(7), // (0x0) SlotDebugData { name: "InstantiatedModule(ripple_counter: ripple_counter).ripple_counter::bits[5]", ty: Bool }, + }, + 41: Shl { + dest: StatePartIndex(22), // (0x0) SlotDebugData { name: "", ty: UInt<6> }, + lhs: StatePartIndex(21), // (0x0) SlotDebugData { name: "", ty: UInt<1> }, + rhs: 5, + }, + 42: Copy { + dest: StatePartIndex(18), // (0x0) SlotDebugData { name: "", ty: UInt<1> }, + src: StatePartIndex(6), // (0x0) SlotDebugData { name: "InstantiatedModule(ripple_counter: ripple_counter).ripple_counter::bits[4]", ty: Bool }, + }, + 43: Shl { + dest: StatePartIndex(19), // (0x0) SlotDebugData { name: "", ty: UInt<5> }, + lhs: StatePartIndex(18), // (0x0) SlotDebugData { name: "", ty: UInt<1> }, + rhs: 4, + }, + 44: Copy { + dest: StatePartIndex(15), // (0x0) SlotDebugData { name: "", ty: UInt<1> }, + src: StatePartIndex(5), // (0x0) SlotDebugData { name: "InstantiatedModule(ripple_counter: ripple_counter).ripple_counter::bits[3]", ty: Bool }, + }, + 45: Shl { + dest: StatePartIndex(16), // (0x0) SlotDebugData { name: "", ty: UInt<4> }, + lhs: StatePartIndex(15), // (0x0) SlotDebugData { name: "", ty: UInt<1> }, + rhs: 3, + }, + 46: Copy { + dest: StatePartIndex(12), // (0x0) SlotDebugData { name: "", ty: UInt<1> }, + src: StatePartIndex(4), // (0x0) SlotDebugData { name: "InstantiatedModule(ripple_counter: ripple_counter).ripple_counter::bits[2]", ty: Bool }, + }, + 47: Shl { + dest: StatePartIndex(13), // (0x0) SlotDebugData { name: "", ty: UInt<3> }, + lhs: StatePartIndex(12), // (0x0) SlotDebugData { name: "", ty: UInt<1> }, + rhs: 2, + }, + 48: Copy { + dest: StatePartIndex(9), // (0x0) SlotDebugData { name: "", ty: UInt<1> }, + src: StatePartIndex(3), // (0x0) SlotDebugData { name: "InstantiatedModule(ripple_counter: ripple_counter).ripple_counter::bits[1]", ty: Bool }, + }, + 49: Shl { + dest: StatePartIndex(10), // (0x0) SlotDebugData { name: "", ty: UInt<2> }, + lhs: StatePartIndex(9), // (0x0) SlotDebugData { name: "", ty: UInt<1> }, + rhs: 1, + }, + 50: Copy { + dest: StatePartIndex(8), // (0x0) SlotDebugData { name: "", ty: UInt<1> }, + src: StatePartIndex(2), // (0x0) SlotDebugData { name: "InstantiatedModule(ripple_counter: ripple_counter).ripple_counter::bits[0]", ty: Bool }, + }, + 51: Or { + dest: StatePartIndex(11), // (0x0) SlotDebugData { name: "", ty: UInt<2> }, + lhs: StatePartIndex(8), // (0x0) SlotDebugData { name: "", ty: UInt<1> }, + rhs: StatePartIndex(10), // (0x0) SlotDebugData { name: "", ty: UInt<2> }, + }, + 52: Or { + dest: StatePartIndex(14), // (0x0) SlotDebugData { name: "", ty: UInt<3> }, + lhs: StatePartIndex(11), // (0x0) SlotDebugData { name: "", ty: UInt<2> }, + rhs: StatePartIndex(13), // (0x0) SlotDebugData { name: "", ty: UInt<3> }, + }, + 53: Or { + dest: StatePartIndex(17), // (0x0) SlotDebugData { name: "", ty: UInt<4> }, + lhs: StatePartIndex(14), // (0x0) SlotDebugData { name: "", ty: UInt<3> }, + rhs: StatePartIndex(16), // (0x0) SlotDebugData { name: "", ty: UInt<4> }, + }, + 54: Or { + dest: StatePartIndex(20), // (0x0) SlotDebugData { name: "", ty: UInt<5> }, + lhs: StatePartIndex(17), // (0x0) SlotDebugData { name: "", ty: UInt<4> }, + rhs: StatePartIndex(19), // (0x0) SlotDebugData { name: "", ty: UInt<5> }, + }, + 55: Or { + dest: StatePartIndex(23), // (0x0) SlotDebugData { name: "", ty: UInt<6> }, + lhs: StatePartIndex(20), // (0x0) SlotDebugData { name: "", ty: UInt<5> }, + rhs: StatePartIndex(22), // (0x0) SlotDebugData { name: "", ty: UInt<6> }, + }, + // at: module-XXXXXXXXXX.rs:5:1 + 56: Copy { + dest: StatePartIndex(1), // (0x0) SlotDebugData { name: "InstantiatedModule(ripple_counter: ripple_counter).ripple_counter::o", ty: UInt<6> }, + src: StatePartIndex(23), // (0x0) SlotDebugData { name: "", ty: UInt<6> }, + }, + // at: module-XXXXXXXXXX.rs:6:1 + 57: BranchIfSmallZero { + target: 59, + value: StatePartIndex(1), // (0x0 0) SlotDebugData { name: "", ty: Bool }, + }, + 58: Copy { + dest: StatePartIndex(24), // (0x0) SlotDebugData { name: "InstantiatedModule(ripple_counter: ripple_counter).ripple_counter::bit_reg_0", ty: Bool }, + src: StatePartIndex(25), // (0x1) SlotDebugData { name: "InstantiatedModule(ripple_counter: ripple_counter).ripple_counter::bit_reg_0$next", ty: Bool }, + }, + 59: BranchIfSmallZero { + target: 61, + value: StatePartIndex(4), // (0x0 0) SlotDebugData { name: "", ty: Bool }, + }, + 60: Copy { + dest: StatePartIndex(36), // (0x0) SlotDebugData { name: "InstantiatedModule(ripple_counter: ripple_counter).ripple_counter::bit_reg_2", ty: Bool }, + src: StatePartIndex(37), // (0x1) SlotDebugData { name: "InstantiatedModule(ripple_counter: ripple_counter).ripple_counter::bit_reg_2$next", ty: Bool }, + }, + 61: BranchIfSmallZero { + target: 63, + value: StatePartIndex(7), // (0x0 0) SlotDebugData { name: "", ty: Bool }, + }, + 62: Copy { + dest: StatePartIndex(47), // (0x0) SlotDebugData { name: "InstantiatedModule(ripple_counter: ripple_counter).ripple_counter::bit_reg_4", ty: Bool }, + src: StatePartIndex(48), // (0x1) SlotDebugData { name: "InstantiatedModule(ripple_counter: ripple_counter).ripple_counter::bit_reg_4$next", ty: Bool }, + }, + 63: XorSmallImmediate { + dest: StatePartIndex(0), // (0x0 0) SlotDebugData { name: "", ty: Bool }, + lhs: StatePartIndex(2), // (0x1 1) SlotDebugData { name: "", ty: Bool }, + rhs: 0x1, + }, + 64: XorSmallImmediate { + dest: StatePartIndex(3), // (0x1 1) SlotDebugData { name: "", ty: Bool }, + lhs: StatePartIndex(5), // (0x0 0) SlotDebugData { name: "", ty: Bool }, + rhs: 0x1, + }, + 65: XorSmallImmediate { + dest: StatePartIndex(6), // (0x1 1) SlotDebugData { name: "", ty: Bool }, + lhs: StatePartIndex(8), // (0x0 0) SlotDebugData { name: "", ty: Bool }, + rhs: 0x1, + }, + // at: module-XXXXXXXXXX.rs:1:1 + 66: Return, + ], + .. + }, + pc: 66, + memory_write_log: [], + memories: StatePart { + value: [], + }, + small_slots: StatePart { + value: [ + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + ], + }, + big_slots: StatePart { + value: [ + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + ], + }, + sim_only_slots: StatePart { + value: [], + }, + }, + io: Instance { + name: ::ripple_counter, + instantiated: Module { + name: ripple_counter, + .. + }, + }, + main_module: SimulationModuleState { + base_targets: [ + Instance { + name: ::ripple_counter, + instantiated: Module { + name: ripple_counter, + .. + }, + }.clk, + Instance { + name: ::ripple_counter, + instantiated: Module { + name: ripple_counter, + .. + }, + }.o, + ], + uninitialized_ios: {}, + io_targets: { + Instance { + name: ::ripple_counter, + instantiated: Module { + name: ripple_counter, + .. + }, + }.clk, + Instance { + name: ::ripple_counter, + instantiated: Module { + name: ripple_counter, + .. + }, + }.o, + }, + did_initial_settle: true, + }, + extern_modules: [ + SimulationExternModuleState { + module_state: SimulationModuleState { + base_targets: [ + ModuleIO { + name: sw_reg::clk, + is_input: true, + ty: Clock, + .. + }, + ModuleIO { + name: sw_reg::o, + is_input: false, + ty: Bool, + .. + }, + ], + uninitialized_ios: {}, + io_targets: { + ModuleIO { + name: sw_reg::clk, + is_input: true, + ty: Clock, + .. + }, + ModuleIO { + name: sw_reg::o, + is_input: false, + ty: Bool, + .. + }, + }, + did_initial_settle: true, + }, + sim: ExternModuleSimulation { + generator: SimGeneratorFn { + args: ( + ModuleIO { + name: sw_reg::clk, + is_input: true, + ty: Clock, + .. + }, + ModuleIO { + name: sw_reg::o, + is_input: false, + ty: Bool, + .. + }, + ), + f: ..., + }, + sim_io_to_generator_map: { + ModuleIO { + name: sw_reg::clk, + is_input: true, + ty: Clock, + .. + }: ModuleIO { + name: sw_reg::clk, + is_input: true, + ty: Clock, + .. + }, + ModuleIO { + name: sw_reg::o, + is_input: false, + ty: Bool, + .. + }: ModuleIO { + name: sw_reg::o, + is_input: false, + ty: Bool, + .. + }, + }, + source_location: SourceLocation( + module-XXXXXXXXXX-2.rs:4:1, + ), + }, + running_generator: Some( + ..., + ), + wait_targets: { + Change { + key: CompiledValue { + layout: CompiledTypeLayout { + ty: Clock, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(ripple_counter.bit_reg_1: sw_reg).sw_reg::clk", + ty: Clock, + }, + ], + .. + }, + sim_only_slots: StatePartLayout { + len: 0, + debug_data: [], + layout_data: [], + .. + }, + }, + body: Scalar, + }, + range: TypeIndexRange { + small_slots: StatePartIndexRange { start: 3, len: 0 }, + big_slots: StatePartIndexRange { start: 33, len: 1 }, + sim_only_slots: StatePartIndexRange { start: 0, len: 0 }, + }, + write: None, + }, + value: SimValue { + ty: Clock, + value: OpaqueSimValue { + bits: 0x0_u1, + sim_only_values: [], + }, + }, + }, + }, + }, + SimulationExternModuleState { + module_state: SimulationModuleState { + base_targets: [ + ModuleIO { + name: sw_reg::clk, + is_input: true, + ty: Clock, + .. + }, + ModuleIO { + name: sw_reg::o, + is_input: false, + ty: Bool, + .. + }, + ], + uninitialized_ios: {}, + io_targets: { + ModuleIO { + name: sw_reg::clk, + is_input: true, + ty: Clock, + .. + }, + ModuleIO { + name: sw_reg::o, + is_input: false, + ty: Bool, + .. + }, + }, + did_initial_settle: true, + }, + sim: ExternModuleSimulation { + generator: SimGeneratorFn { + args: ( + ModuleIO { + name: sw_reg::clk, + is_input: true, + ty: Clock, + .. + }, + ModuleIO { + name: sw_reg::o, + is_input: false, + ty: Bool, + .. + }, + ), + f: ..., + }, + sim_io_to_generator_map: { + ModuleIO { + name: sw_reg::clk, + is_input: true, + ty: Clock, + .. + }: ModuleIO { + name: sw_reg::clk, + is_input: true, + ty: Clock, + .. + }, + ModuleIO { + name: sw_reg::o, + is_input: false, + ty: Bool, + .. + }: ModuleIO { + name: sw_reg::o, + is_input: false, + ty: Bool, + .. + }, + }, + source_location: SourceLocation( + module-XXXXXXXXXX-2.rs:4:1, + ), + }, + running_generator: Some( + ..., + ), + wait_targets: { + Change { + key: CompiledValue { + layout: CompiledTypeLayout { + ty: Clock, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(ripple_counter.bit_reg_3: sw_reg).sw_reg::clk", + ty: Clock, + }, + ], + .. + }, + sim_only_slots: StatePartLayout { + len: 0, + debug_data: [], + layout_data: [], + .. + }, + }, + body: Scalar, + }, + range: TypeIndexRange { + small_slots: StatePartIndexRange { start: 6, len: 0 }, + big_slots: StatePartIndexRange { start: 44, len: 1 }, + sim_only_slots: StatePartIndexRange { start: 0, len: 0 }, + }, + write: None, + }, + value: SimValue { + ty: Clock, + value: OpaqueSimValue { + bits: 0x0_u1, + sim_only_values: [], + }, + }, + }, + }, + }, + SimulationExternModuleState { + module_state: SimulationModuleState { + base_targets: [ + ModuleIO { + name: sw_reg::clk, + is_input: true, + ty: Clock, + .. + }, + ModuleIO { + name: sw_reg::o, + is_input: false, + ty: Bool, + .. + }, + ], + uninitialized_ios: {}, + io_targets: { + ModuleIO { + name: sw_reg::clk, + is_input: true, + ty: Clock, + .. + }, + ModuleIO { + name: sw_reg::o, + is_input: false, + ty: Bool, + .. + }, + }, + did_initial_settle: true, + }, + sim: ExternModuleSimulation { + generator: SimGeneratorFn { + args: ( + ModuleIO { + name: sw_reg::clk, + is_input: true, + ty: Clock, + .. + }, + ModuleIO { + name: sw_reg::o, + is_input: false, + ty: Bool, + .. + }, + ), + f: ..., + }, + sim_io_to_generator_map: { + ModuleIO { + name: sw_reg::clk, + is_input: true, + ty: Clock, + .. + }: ModuleIO { + name: sw_reg::clk, + is_input: true, + ty: Clock, + .. + }, + ModuleIO { + name: sw_reg::o, + is_input: false, + ty: Bool, + .. + }: ModuleIO { + name: sw_reg::o, + is_input: false, + ty: Bool, + .. + }, + }, + source_location: SourceLocation( + module-XXXXXXXXXX-2.rs:4:1, + ), + }, + running_generator: Some( + ..., + ), + wait_targets: { + Change { + key: CompiledValue { + layout: CompiledTypeLayout { + ty: Clock, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(ripple_counter.bit_reg_5: sw_reg).sw_reg::clk", + ty: Clock, + }, + ], + .. + }, + sim_only_slots: StatePartLayout { + len: 0, + debug_data: [], + layout_data: [], + .. + }, + }, + body: Scalar, + }, + range: TypeIndexRange { + small_slots: StatePartIndexRange { start: 9, len: 0 }, + big_slots: StatePartIndexRange { start: 55, len: 1 }, + sim_only_slots: StatePartIndexRange { start: 0, len: 0 }, + }, + write: None, + }, + value: SimValue { + ty: Clock, + value: OpaqueSimValue { + bits: 0x0_u1, + sim_only_values: [], + }, + }, + }, + }, + }, + ], + state_ready_to_run: false, + trace_decls: TraceModule { + name: "ripple_counter", + children: [ + TraceModuleIO { + name: "clk", + child: TraceClock { + location: TraceScalarId(0), + name: "clk", + flow: Source, + }, + ty: Clock, + flow: Source, + }, + TraceModuleIO { + name: "o", + child: TraceUInt { + location: TraceScalarId(1), + name: "o", + ty: UInt<6>, + flow: Sink, + }, + ty: UInt<6>, + flow: Sink, + }, + TraceWire { + name: "bits", + child: TraceArray { + name: "bits", + elements: [ + TraceBool { + location: TraceScalarId(2), + name: "[0]", + flow: Duplex, + }, + TraceBool { + location: TraceScalarId(3), + name: "[1]", + flow: Duplex, + }, + TraceBool { + location: TraceScalarId(4), + name: "[2]", + flow: Duplex, + }, + TraceBool { + location: TraceScalarId(5), + name: "[3]", + flow: Duplex, + }, + TraceBool { + location: TraceScalarId(6), + name: "[4]", + flow: Duplex, + }, + TraceBool { + location: TraceScalarId(7), + name: "[5]", + flow: Duplex, + }, + ], + ty: Array, + flow: Duplex, + }, + ty: Array, + }, + TraceReg { + name: "bit_reg_0", + child: TraceBool { + location: TraceScalarId(8), + name: "bit_reg_0", + flow: Duplex, + }, + ty: Bool, + }, + TraceInstance { + name: "bit_reg_1", + instance_io: TraceBundle { + name: "bit_reg_1", + fields: [ + TraceClock { + location: TraceScalarId(11), + name: "clk", + flow: Sink, + }, + TraceBool { + location: TraceScalarId(12), + name: "o", + flow: Source, + }, + ], + ty: Bundle { + #[hdl(flip)] /* offset = 0 */ + clk: Clock, + /* offset = 1 */ + o: Bool, + }, + flow: Source, + }, + module: TraceModule { + name: "sw_reg", + children: [ + TraceModuleIO { + name: "clk", + child: TraceClock { + location: TraceScalarId(9), + name: "clk", + flow: Source, + }, + ty: Clock, + flow: Source, + }, + TraceModuleIO { + name: "o", + child: TraceBool { + location: TraceScalarId(10), + name: "o", + flow: Sink, + }, + ty: Bool, + flow: Sink, + }, + ], + }, + ty: Bundle { + #[hdl(flip)] /* offset = 0 */ + clk: Clock, + /* offset = 1 */ + o: Bool, + }, + }, + TraceReg { + name: "bit_reg_2", + child: TraceBool { + location: TraceScalarId(13), + name: "bit_reg_2", + flow: Duplex, + }, + ty: Bool, + }, + TraceInstance { + name: "bit_reg_3", + instance_io: TraceBundle { + name: "bit_reg_3", + fields: [ + TraceClock { + location: TraceScalarId(16), + name: "clk", + flow: Sink, + }, + TraceBool { + location: TraceScalarId(17), + name: "o", + flow: Source, + }, + ], + ty: Bundle { + #[hdl(flip)] /* offset = 0 */ + clk: Clock, + /* offset = 1 */ + o: Bool, + }, + flow: Source, + }, + module: TraceModule { + name: "sw_reg", + children: [ + TraceModuleIO { + name: "clk", + child: TraceClock { + location: TraceScalarId(14), + name: "clk", + flow: Source, + }, + ty: Clock, + flow: Source, + }, + TraceModuleIO { + name: "o", + child: TraceBool { + location: TraceScalarId(15), + name: "o", + flow: Sink, + }, + ty: Bool, + flow: Sink, + }, + ], + }, + ty: Bundle { + #[hdl(flip)] /* offset = 0 */ + clk: Clock, + /* offset = 1 */ + o: Bool, + }, + }, + TraceReg { + name: "bit_reg_4", + child: TraceBool { + location: TraceScalarId(18), + name: "bit_reg_4", + flow: Duplex, + }, + ty: Bool, + }, + TraceInstance { + name: "bit_reg_5", + instance_io: TraceBundle { + name: "bit_reg_5", + fields: [ + TraceClock { + location: TraceScalarId(21), + name: "clk", + flow: Sink, + }, + TraceBool { + location: TraceScalarId(22), + name: "o", + flow: Source, + }, + ], + ty: Bundle { + #[hdl(flip)] /* offset = 0 */ + clk: Clock, + /* offset = 1 */ + o: Bool, + }, + flow: Source, + }, + module: TraceModule { + name: "sw_reg", + children: [ + TraceModuleIO { + name: "clk", + child: TraceClock { + location: TraceScalarId(19), + name: "clk", + flow: Source, + }, + ty: Clock, + flow: Source, + }, + TraceModuleIO { + name: "o", + child: TraceBool { + location: TraceScalarId(20), + name: "o", + flow: Sink, + }, + ty: Bool, + flow: Sink, + }, + ], + }, + ty: Bundle { + #[hdl(flip)] /* offset = 0 */ + clk: Clock, + /* offset = 1 */ + o: Bool, + }, + }, + ], + }, + traces: [ + SimTrace { + id: TraceScalarId(0), + kind: BigClock { + index: StatePartIndex(0), + }, + state: 0x1, + last_state: 0x1, + }, + SimTrace { + id: TraceScalarId(1), + kind: BigUInt { + index: StatePartIndex(1), + ty: UInt<6>, + }, + state: 0x00, + last_state: 0x00, + }, + SimTrace { + id: TraceScalarId(2), + kind: BigBool { + index: StatePartIndex(2), + }, + state: 0x0, + last_state: 0x0, + }, + SimTrace { + id: TraceScalarId(3), + kind: BigBool { + index: StatePartIndex(3), + }, + state: 0x0, + last_state: 0x0, + }, + SimTrace { + id: TraceScalarId(4), + kind: BigBool { + index: StatePartIndex(4), + }, + state: 0x0, + last_state: 0x0, + }, + SimTrace { + id: TraceScalarId(5), + kind: BigBool { + index: StatePartIndex(5), + }, + state: 0x0, + last_state: 0x0, + }, + SimTrace { + id: TraceScalarId(6), + kind: BigBool { + index: StatePartIndex(6), + }, + state: 0x0, + last_state: 0x0, + }, + SimTrace { + id: TraceScalarId(7), + kind: BigBool { + index: StatePartIndex(7), + }, + state: 0x0, + last_state: 0x0, + }, + SimTrace { + id: TraceScalarId(8), + kind: BigBool { + index: StatePartIndex(24), + }, + state: 0x0, + last_state: 0x0, + }, + SimTrace { + id: TraceScalarId(9), + kind: BigClock { + index: StatePartIndex(33), + }, + state: 0x0, + last_state: 0x0, + }, + SimTrace { + id: TraceScalarId(10), + kind: BigBool { + index: StatePartIndex(34), + }, + state: 0x0, + last_state: 0x0, + }, + SimTrace { + id: TraceScalarId(11), + kind: BigClock { + index: StatePartIndex(31), + }, + state: 0x0, + last_state: 0x0, + }, + SimTrace { + id: TraceScalarId(12), + kind: BigBool { + index: StatePartIndex(32), + }, + state: 0x0, + last_state: 0x0, + }, + SimTrace { + id: TraceScalarId(13), + kind: BigBool { + index: StatePartIndex(36), + }, + state: 0x0, + last_state: 0x0, + }, + SimTrace { + id: TraceScalarId(14), + kind: BigClock { + index: StatePartIndex(44), + }, + state: 0x0, + last_state: 0x0, + }, + SimTrace { + id: TraceScalarId(15), + kind: BigBool { + index: StatePartIndex(45), + }, + state: 0x0, + last_state: 0x0, + }, + SimTrace { + id: TraceScalarId(16), + kind: BigClock { + index: StatePartIndex(42), + }, + state: 0x0, + last_state: 0x0, + }, + SimTrace { + id: TraceScalarId(17), + kind: BigBool { + index: StatePartIndex(43), + }, + state: 0x0, + last_state: 0x0, + }, + SimTrace { + id: TraceScalarId(18), + kind: BigBool { + index: StatePartIndex(47), + }, + state: 0x0, + last_state: 0x0, + }, + SimTrace { + id: TraceScalarId(19), + kind: BigClock { + index: StatePartIndex(55), + }, + state: 0x0, + last_state: 0x0, + }, + SimTrace { + id: TraceScalarId(20), + kind: BigBool { + index: StatePartIndex(56), + }, + state: 0x0, + last_state: 0x0, + }, + SimTrace { + id: TraceScalarId(21), + kind: BigClock { + index: StatePartIndex(53), + }, + state: 0x0, + last_state: 0x0, + }, + SimTrace { + id: TraceScalarId(22), + kind: BigBool { + index: StatePartIndex(54), + }, + state: 0x0, + last_state: 0x0, + }, + ], + trace_memories: {}, + trace_writers: [ + Running( + VcdWriter { + finished_init: true, + timescale: 1 ps, + .. + }, + ), + ], + instant: 256 μs, + clocks_triggered: [ + StatePartIndex(1), + StatePartIndex(4), + StatePartIndex(7), + ], + .. +} \ No newline at end of file diff --git a/crates/fayalite/tests/sim/expected/ripple_counter.vcd b/crates/fayalite/tests/sim/expected/ripple_counter.vcd new file mode 100644 index 0000000..6f14a8e --- /dev/null +++ b/crates/fayalite/tests/sim/expected/ripple_counter.vcd @@ -0,0 +1,1753 @@ +$timescale 1 ps $end +$scope module ripple_counter $end +$var wire 1 ! clk $end +$var wire 6 " o $end +$scope struct bits $end +$var wire 1 # \[0] $end +$var wire 1 $ \[1] $end +$var wire 1 % \[2] $end +$var wire 1 & \[3] $end +$var wire 1 ' \[4] $end +$var wire 1 ( \[5] $end +$upscope $end +$var reg 1 ) bit_reg_0 $end +$scope struct bit_reg_1 $end +$var wire 1 , clk $end +$var wire 1 - o $end +$upscope $end +$scope module sw_reg $end +$var wire 1 * clk $end +$var wire 1 + o $end +$upscope $end +$var reg 1 . bit_reg_2 $end +$scope struct bit_reg_3 $end +$var wire 1 1 clk $end +$var wire 1 2 o $end +$upscope $end +$scope module sw_reg_2 $end +$var wire 1 / clk $end +$var wire 1 0 o $end +$upscope $end +$var reg 1 3 bit_reg_4 $end +$scope struct bit_reg_5 $end +$var wire 1 6 clk $end +$var wire 1 7 o $end +$upscope $end +$scope module sw_reg_3 $end +$var wire 1 4 clk $end +$var wire 1 5 o $end +$upscope $end +$upscope $end +$enddefinitions $end +$dumpvars +0! +b0 " +0# +0$ +0% +0& +0' +0( +0) +0* +0+ +0, +0- +0. +0/ +00 +01 +02 +03 +04 +05 +06 +07 +$end +#1000000 +1! +1) +b1 " +1# +1* +1, +1+ +b11 " +1$ +1- +1. +b111 " +1% +1/ +11 +10 +b1111 " +1& +12 +13 +b11111 " +1' +14 +16 +15 +b111111 " +1( +17 +#2000000 +0! +#3000000 +1! +0) +b111110 " +0# +0* +0, +#4000000 +0! +#5000000 +1! +1) +b111111 " +1# +1* +1, +0+ +b111101 " +0$ +0- +#6000000 +0! +#7000000 +1! +0) +b111100 " +0# +0* +0, +#8000000 +0! +#9000000 +1! +1) +b111101 " +1# +1* +1, +1+ +b111111 " +1$ +1- +0. +b111011 " +0% +0/ +01 +#10000000 +0! +#11000000 +1! +0) +b111010 " +0# +0* +0, +#12000000 +0! +#13000000 +1! +1) +b111011 " +1# +1* +1, +0+ +b111001 " +0$ +0- +#14000000 +0! +#15000000 +1! +0) +b111000 " +0# +0* +0, +#16000000 +0! +#17000000 +1! +1) +b111001 " +1# +1* +1, +1+ +b111011 " +1$ +1- +1. +b111111 " +1% +1/ +11 +00 +b110111 " +0& +02 +#18000000 +0! +#19000000 +1! +0) +b110110 " +0# +0* +0, +#20000000 +0! +#21000000 +1! +1) +b110111 " +1# +1* +1, +0+ +b110101 " +0$ +0- +#22000000 +0! +#23000000 +1! +0) +b110100 " +0# +0* +0, +#24000000 +0! +#25000000 +1! +1) +b110101 " +1# +1* +1, +1+ +b110111 " +1$ +1- +0. +b110011 " +0% +0/ +01 +#26000000 +0! +#27000000 +1! +0) +b110010 " +0# +0* +0, +#28000000 +0! +#29000000 +1! +1) +b110011 " +1# +1* +1, +0+ +b110001 " +0$ +0- +#30000000 +0! +#31000000 +1! +0) +b110000 " +0# +0* +0, +#32000000 +0! +#33000000 +1! +1) +b110001 " +1# +1* +1, +1+ +b110011 " +1$ +1- +1. +b110111 " +1% +1/ +11 +10 +b111111 " +1& +12 +03 +b101111 " +0' +04 +06 +#34000000 +0! +#35000000 +1! +0) +b101110 " +0# +0* +0, +#36000000 +0! +#37000000 +1! +1) +b101111 " +1# +1* +1, +0+ +b101101 " +0$ +0- +#38000000 +0! +#39000000 +1! +0) +b101100 " +0# +0* +0, +#40000000 +0! +#41000000 +1! +1) +b101101 " +1# +1* +1, +1+ +b101111 " +1$ +1- +0. +b101011 " +0% +0/ +01 +#42000000 +0! +#43000000 +1! +0) +b101010 " +0# +0* +0, +#44000000 +0! +#45000000 +1! +1) +b101011 " +1# +1* +1, +0+ +b101001 " +0$ +0- +#46000000 +0! +#47000000 +1! +0) +b101000 " +0# +0* +0, +#48000000 +0! +#49000000 +1! +1) +b101001 " +1# +1* +1, +1+ +b101011 " +1$ +1- +1. +b101111 " +1% +1/ +11 +00 +b100111 " +0& +02 +#50000000 +0! +#51000000 +1! +0) +b100110 " +0# +0* +0, +#52000000 +0! +#53000000 +1! +1) +b100111 " +1# +1* +1, +0+ +b100101 " +0$ +0- +#54000000 +0! +#55000000 +1! +0) +b100100 " +0# +0* +0, +#56000000 +0! +#57000000 +1! +1) +b100101 " +1# +1* +1, +1+ +b100111 " +1$ +1- +0. +b100011 " +0% +0/ +01 +#58000000 +0! +#59000000 +1! +0) +b100010 " +0# +0* +0, +#60000000 +0! +#61000000 +1! +1) +b100011 " +1# +1* +1, +0+ +b100001 " +0$ +0- +#62000000 +0! +#63000000 +1! +0) +b100000 " +0# +0* +0, +#64000000 +0! +#65000000 +1! +1) +b100001 " +1# +1* +1, +1+ +b100011 " +1$ +1- +1. +b100111 " +1% +1/ +11 +10 +b101111 " +1& +12 +13 +b111111 " +1' +14 +16 +05 +b11111 " +0( +07 +#66000000 +0! +#67000000 +1! +0) +b11110 " +0# +0* +0, +#68000000 +0! +#69000000 +1! +1) +b11111 " +1# +1* +1, +0+ +b11101 " +0$ +0- +#70000000 +0! +#71000000 +1! +0) +b11100 " +0# +0* +0, +#72000000 +0! +#73000000 +1! +1) +b11101 " +1# +1* +1, +1+ +b11111 " +1$ +1- +0. +b11011 " +0% +0/ +01 +#74000000 +0! +#75000000 +1! +0) +b11010 " +0# +0* +0, +#76000000 +0! +#77000000 +1! +1) +b11011 " +1# +1* +1, +0+ +b11001 " +0$ +0- +#78000000 +0! +#79000000 +1! +0) +b11000 " +0# +0* +0, +#80000000 +0! +#81000000 +1! +1) +b11001 " +1# +1* +1, +1+ +b11011 " +1$ +1- +1. +b11111 " +1% +1/ +11 +00 +b10111 " +0& +02 +#82000000 +0! +#83000000 +1! +0) +b10110 " +0# +0* +0, +#84000000 +0! +#85000000 +1! +1) +b10111 " +1# +1* +1, +0+ +b10101 " +0$ +0- +#86000000 +0! +#87000000 +1! +0) +b10100 " +0# +0* +0, +#88000000 +0! +#89000000 +1! +1) +b10101 " +1# +1* +1, +1+ +b10111 " +1$ +1- +0. +b10011 " +0% +0/ +01 +#90000000 +0! +#91000000 +1! +0) +b10010 " +0# +0* +0, +#92000000 +0! +#93000000 +1! +1) +b10011 " +1# +1* +1, +0+ +b10001 " +0$ +0- +#94000000 +0! +#95000000 +1! +0) +b10000 " +0# +0* +0, +#96000000 +0! +#97000000 +1! +1) +b10001 " +1# +1* +1, +1+ +b10011 " +1$ +1- +1. +b10111 " +1% +1/ +11 +10 +b11111 " +1& +12 +03 +b1111 " +0' +04 +06 +#98000000 +0! +#99000000 +1! +0) +b1110 " +0# +0* +0, +#100000000 +0! +#101000000 +1! +1) +b1111 " +1# +1* +1, +0+ +b1101 " +0$ +0- +#102000000 +0! +#103000000 +1! +0) +b1100 " +0# +0* +0, +#104000000 +0! +#105000000 +1! +1) +b1101 " +1# +1* +1, +1+ +b1111 " +1$ +1- +0. +b1011 " +0% +0/ +01 +#106000000 +0! +#107000000 +1! +0) +b1010 " +0# +0* +0, +#108000000 +0! +#109000000 +1! +1) +b1011 " +1# +1* +1, +0+ +b1001 " +0$ +0- +#110000000 +0! +#111000000 +1! +0) +b1000 " +0# +0* +0, +#112000000 +0! +#113000000 +1! +1) +b1001 " +1# +1* +1, +1+ +b1011 " +1$ +1- +1. +b1111 " +1% +1/ +11 +00 +b111 " +0& +02 +#114000000 +0! +#115000000 +1! +0) +b110 " +0# +0* +0, +#116000000 +0! +#117000000 +1! +1) +b111 " +1# +1* +1, +0+ +b101 " +0$ +0- +#118000000 +0! +#119000000 +1! +0) +b100 " +0# +0* +0, +#120000000 +0! +#121000000 +1! +1) +b101 " +1# +1* +1, +1+ +b111 " +1$ +1- +0. +b11 " +0% +0/ +01 +#122000000 +0! +#123000000 +1! +0) +b10 " +0# +0* +0, +#124000000 +0! +#125000000 +1! +1) +b11 " +1# +1* +1, +0+ +b1 " +0$ +0- +#126000000 +0! +#127000000 +1! +0) +b0 " +0# +0* +0, +#128000000 +0! +#129000000 +1! +1) +b1 " +1# +1* +1, +1+ +b11 " +1$ +1- +1. +b111 " +1% +1/ +11 +10 +b1111 " +1& +12 +13 +b11111 " +1' +14 +16 +15 +b111111 " +1( +17 +#130000000 +0! +#131000000 +1! +0) +b111110 " +0# +0* +0, +#132000000 +0! +#133000000 +1! +1) +b111111 " +1# +1* +1, +0+ +b111101 " +0$ +0- +#134000000 +0! +#135000000 +1! +0) +b111100 " +0# +0* +0, +#136000000 +0! +#137000000 +1! +1) +b111101 " +1# +1* +1, +1+ +b111111 " +1$ +1- +0. +b111011 " +0% +0/ +01 +#138000000 +0! +#139000000 +1! +0) +b111010 " +0# +0* +0, +#140000000 +0! +#141000000 +1! +1) +b111011 " +1# +1* +1, +0+ +b111001 " +0$ +0- +#142000000 +0! +#143000000 +1! +0) +b111000 " +0# +0* +0, +#144000000 +0! +#145000000 +1! +1) +b111001 " +1# +1* +1, +1+ +b111011 " +1$ +1- +1. +b111111 " +1% +1/ +11 +00 +b110111 " +0& +02 +#146000000 +0! +#147000000 +1! +0) +b110110 " +0# +0* +0, +#148000000 +0! +#149000000 +1! +1) +b110111 " +1# +1* +1, +0+ +b110101 " +0$ +0- +#150000000 +0! +#151000000 +1! +0) +b110100 " +0# +0* +0, +#152000000 +0! +#153000000 +1! +1) +b110101 " +1# +1* +1, +1+ +b110111 " +1$ +1- +0. +b110011 " +0% +0/ +01 +#154000000 +0! +#155000000 +1! +0) +b110010 " +0# +0* +0, +#156000000 +0! +#157000000 +1! +1) +b110011 " +1# +1* +1, +0+ +b110001 " +0$ +0- +#158000000 +0! +#159000000 +1! +0) +b110000 " +0# +0* +0, +#160000000 +0! +#161000000 +1! +1) +b110001 " +1# +1* +1, +1+ +b110011 " +1$ +1- +1. +b110111 " +1% +1/ +11 +10 +b111111 " +1& +12 +03 +b101111 " +0' +04 +06 +#162000000 +0! +#163000000 +1! +0) +b101110 " +0# +0* +0, +#164000000 +0! +#165000000 +1! +1) +b101111 " +1# +1* +1, +0+ +b101101 " +0$ +0- +#166000000 +0! +#167000000 +1! +0) +b101100 " +0# +0* +0, +#168000000 +0! +#169000000 +1! +1) +b101101 " +1# +1* +1, +1+ +b101111 " +1$ +1- +0. +b101011 " +0% +0/ +01 +#170000000 +0! +#171000000 +1! +0) +b101010 " +0# +0* +0, +#172000000 +0! +#173000000 +1! +1) +b101011 " +1# +1* +1, +0+ +b101001 " +0$ +0- +#174000000 +0! +#175000000 +1! +0) +b101000 " +0# +0* +0, +#176000000 +0! +#177000000 +1! +1) +b101001 " +1# +1* +1, +1+ +b101011 " +1$ +1- +1. +b101111 " +1% +1/ +11 +00 +b100111 " +0& +02 +#178000000 +0! +#179000000 +1! +0) +b100110 " +0# +0* +0, +#180000000 +0! +#181000000 +1! +1) +b100111 " +1# +1* +1, +0+ +b100101 " +0$ +0- +#182000000 +0! +#183000000 +1! +0) +b100100 " +0# +0* +0, +#184000000 +0! +#185000000 +1! +1) +b100101 " +1# +1* +1, +1+ +b100111 " +1$ +1- +0. +b100011 " +0% +0/ +01 +#186000000 +0! +#187000000 +1! +0) +b100010 " +0# +0* +0, +#188000000 +0! +#189000000 +1! +1) +b100011 " +1# +1* +1, +0+ +b100001 " +0$ +0- +#190000000 +0! +#191000000 +1! +0) +b100000 " +0# +0* +0, +#192000000 +0! +#193000000 +1! +1) +b100001 " +1# +1* +1, +1+ +b100011 " +1$ +1- +1. +b100111 " +1% +1/ +11 +10 +b101111 " +1& +12 +13 +b111111 " +1' +14 +16 +05 +b11111 " +0( +07 +#194000000 +0! +#195000000 +1! +0) +b11110 " +0# +0* +0, +#196000000 +0! +#197000000 +1! +1) +b11111 " +1# +1* +1, +0+ +b11101 " +0$ +0- +#198000000 +0! +#199000000 +1! +0) +b11100 " +0# +0* +0, +#200000000 +0! +#201000000 +1! +1) +b11101 " +1# +1* +1, +1+ +b11111 " +1$ +1- +0. +b11011 " +0% +0/ +01 +#202000000 +0! +#203000000 +1! +0) +b11010 " +0# +0* +0, +#204000000 +0! +#205000000 +1! +1) +b11011 " +1# +1* +1, +0+ +b11001 " +0$ +0- +#206000000 +0! +#207000000 +1! +0) +b11000 " +0# +0* +0, +#208000000 +0! +#209000000 +1! +1) +b11001 " +1# +1* +1, +1+ +b11011 " +1$ +1- +1. +b11111 " +1% +1/ +11 +00 +b10111 " +0& +02 +#210000000 +0! +#211000000 +1! +0) +b10110 " +0# +0* +0, +#212000000 +0! +#213000000 +1! +1) +b10111 " +1# +1* +1, +0+ +b10101 " +0$ +0- +#214000000 +0! +#215000000 +1! +0) +b10100 " +0# +0* +0, +#216000000 +0! +#217000000 +1! +1) +b10101 " +1# +1* +1, +1+ +b10111 " +1$ +1- +0. +b10011 " +0% +0/ +01 +#218000000 +0! +#219000000 +1! +0) +b10010 " +0# +0* +0, +#220000000 +0! +#221000000 +1! +1) +b10011 " +1# +1* +1, +0+ +b10001 " +0$ +0- +#222000000 +0! +#223000000 +1! +0) +b10000 " +0# +0* +0, +#224000000 +0! +#225000000 +1! +1) +b10001 " +1# +1* +1, +1+ +b10011 " +1$ +1- +1. +b10111 " +1% +1/ +11 +10 +b11111 " +1& +12 +03 +b1111 " +0' +04 +06 +#226000000 +0! +#227000000 +1! +0) +b1110 " +0# +0* +0, +#228000000 +0! +#229000000 +1! +1) +b1111 " +1# +1* +1, +0+ +b1101 " +0$ +0- +#230000000 +0! +#231000000 +1! +0) +b1100 " +0# +0* +0, +#232000000 +0! +#233000000 +1! +1) +b1101 " +1# +1* +1, +1+ +b1111 " +1$ +1- +0. +b1011 " +0% +0/ +01 +#234000000 +0! +#235000000 +1! +0) +b1010 " +0# +0* +0, +#236000000 +0! +#237000000 +1! +1) +b1011 " +1# +1* +1, +0+ +b1001 " +0$ +0- +#238000000 +0! +#239000000 +1! +0) +b1000 " +0# +0* +0, +#240000000 +0! +#241000000 +1! +1) +b1001 " +1# +1* +1, +1+ +b1011 " +1$ +1- +1. +b1111 " +1% +1/ +11 +00 +b111 " +0& +02 +#242000000 +0! +#243000000 +1! +0) +b110 " +0# +0* +0, +#244000000 +0! +#245000000 +1! +1) +b111 " +1# +1* +1, +0+ +b101 " +0$ +0- +#246000000 +0! +#247000000 +1! +0) +b100 " +0# +0* +0, +#248000000 +0! +#249000000 +1! +1) +b101 " +1# +1* +1, +1+ +b111 " +1$ +1- +0. +b11 " +0% +0/ +01 +#250000000 +0! +#251000000 +1! +0) +b10 " +0# +0* +0, +#252000000 +0! +#253000000 +1! +1) +b11 " +1# +1* +1, +0+ +b1 " +0$ +0- +#254000000 +0! +#255000000 +1! +0) +b0 " +0# +0* +0, +#256000000 diff --git a/crates/fayalite/tests/sim/expected/shift_register.txt b/crates/fayalite/tests/sim/expected/shift_register.txt index 73f6263..9bab424 100644 --- a/crates/fayalite/tests/sim/expected/shift_register.txt +++ b/crates/fayalite/tests/sim/expected/shift_register.txt @@ -83,6 +83,12 @@ Simulation { ], .. }, + sim_only_slots: StatePartLayout { + len: 0, + debug_data: [], + layout_data: [], + .. + }, }, memories: StatePartLayout { len: 0, @@ -257,6 +263,9 @@ Simulation { 0, ], }, + sim_only_slots: StatePart { + value: [], + }, }, io: Instance { name: ::shift_register, @@ -265,247 +274,72 @@ Simulation { .. }, }, - uninitialized_inputs: {}, - io_targets: { - Instance { - name: ::shift_register, - instantiated: Module { - name: shift_register, - .. - }, - }.cd: CompiledValue { - layout: CompiledTypeLayout { - ty: Bundle { - /* offset = 0 */ - clk: Clock, - /* offset = 1 */ - rst: SyncReset, + main_module: SimulationModuleState { + base_targets: [ + Instance { + name: ::shift_register, + instantiated: Module { + name: shift_register, + .. }, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 2, - debug_data: [ - SlotDebugData { - name: "InstantiatedModule(shift_register: shift_register).shift_register::cd.clk", - ty: Clock, - }, - SlotDebugData { - name: "InstantiatedModule(shift_register: shift_register).shift_register::cd.rst", - ty: SyncReset, - }, - ], - .. - }, + }.cd, + Instance { + name: ::shift_register, + instantiated: Module { + name: shift_register, + .. }, - body: Bundle { - fields: [ - CompiledBundleField { - offset: TypeIndex { - small_slots: StatePartIndex(0), - big_slots: StatePartIndex(0), - }, - ty: CompiledTypeLayout { - ty: Clock, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: Clock, - }, - ], - .. - }, - }, - body: Scalar, - }, - }, - CompiledBundleField { - offset: TypeIndex { - small_slots: StatePartIndex(0), - big_slots: StatePartIndex(1), - }, - ty: CompiledTypeLayout { - ty: SyncReset, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: SyncReset, - }, - ], - .. - }, - }, - body: Scalar, - }, - }, - ], + }.d, + Instance { + name: ::shift_register, + instantiated: Module { + name: shift_register, + .. }, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 0, len: 2 }, - }, - write: None, - }, - Instance { - name: ::shift_register, - instantiated: Module { - name: shift_register, - .. - }, - }.cd.clk: CompiledValue { - layout: CompiledTypeLayout { - ty: Clock, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: Clock, - }, - ], - .. - }, - }, - body: Scalar, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 0, len: 1 }, - }, - write: None, - }, - Instance { - name: ::shift_register, - instantiated: Module { - name: shift_register, - .. - }, - }.cd.rst: CompiledValue { - layout: CompiledTypeLayout { - ty: SyncReset, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "", - ty: SyncReset, - }, - ], - .. - }, - }, - body: Scalar, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 1, len: 1 }, - }, - write: None, - }, - Instance { - name: ::shift_register, - instantiated: Module { - name: shift_register, - .. - }, - }.d: CompiledValue { - layout: CompiledTypeLayout { - ty: Bool, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "InstantiatedModule(shift_register: shift_register).shift_register::d", - ty: Bool, - }, - ], - .. - }, - }, - body: Scalar, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 2, len: 1 }, - }, - write: None, - }, - Instance { - name: ::shift_register, - instantiated: Module { - name: shift_register, - .. - }, - }.q: CompiledValue { - layout: CompiledTypeLayout { - ty: Bool, - layout: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 1, - debug_data: [ - SlotDebugData { - name: "InstantiatedModule(shift_register: shift_register).shift_register::q", - ty: Bool, - }, - ], - .. - }, - }, - body: Scalar, - }, - range: TypeIndexRange { - small_slots: StatePartIndexRange { start: 0, len: 0 }, - big_slots: StatePartIndexRange { start: 3, len: 1 }, - }, - write: None, + }.q, + ], + uninitialized_ios: {}, + io_targets: { + Instance { + name: ::shift_register, + instantiated: Module { + name: shift_register, + .. + }, + }.cd, + Instance { + name: ::shift_register, + instantiated: Module { + name: shift_register, + .. + }, + }.cd.clk, + Instance { + name: ::shift_register, + instantiated: Module { + name: shift_register, + .. + }, + }.cd.rst, + Instance { + name: ::shift_register, + instantiated: Module { + name: shift_register, + .. + }, + }.d, + Instance { + name: ::shift_register, + instantiated: Module { + name: shift_register, + .. + }, + }.q, }, + did_initial_settle: true, }, - made_initial_step: true, - needs_settle: false, + extern_modules: [], + state_ready_to_run: false, trace_decls: TraceModule { name: "shift_register", children: [ diff --git a/crates/fayalite/tests/sim/expected/sim_only_connects.txt b/crates/fayalite/tests/sim/expected/sim_only_connects.txt new file mode 100644 index 0000000..114b313 --- /dev/null +++ b/crates/fayalite/tests/sim/expected/sim_only_connects.txt @@ -0,0 +1,1636 @@ +Simulation { + state: State { + insns: Insns { + state_layout: StateLayout { + ty: TypeLayout { + small_slots: StatePartLayout { + len: 4, + debug_data: [ + SlotDebugData { + name: "", + ty: Bool, + }, + SlotDebugData { + name: "", + ty: Bool, + }, + SlotDebugData { + name: "", + ty: Bool, + }, + SlotDebugData { + name: "", + ty: Bool, + }, + ], + .. + }, + big_slots: StatePartLayout { + len: 14, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_only_connects: sim_only_connects).sim_only_connects::cd.clk", + ty: Clock, + }, + SlotDebugData { + name: "InstantiatedModule(sim_only_connects: sim_only_connects).sim_only_connects::cd.rst", + ty: SyncReset, + }, + SlotDebugData { + name: "InstantiatedModule(sim_only_connects: sim_only_connects).sim_only_connects::helper1.cd.clk", + ty: Clock, + }, + SlotDebugData { + name: "InstantiatedModule(sim_only_connects: sim_only_connects).sim_only_connects::helper1.cd.rst", + ty: SyncReset, + }, + SlotDebugData { + name: "InstantiatedModule(sim_only_connects.helper1: sim_only_connects_helper).sim_only_connects_helper::cd.clk", + ty: Clock, + }, + SlotDebugData { + name: "InstantiatedModule(sim_only_connects.helper1: sim_only_connects_helper).sim_only_connects_helper::cd.rst", + ty: SyncReset, + }, + SlotDebugData { + name: "InstantiatedModule(sim_only_connects: sim_only_connects).sim_only_connects::delay1_empty", + ty: Bool, + }, + SlotDebugData { + name: "InstantiatedModule(sim_only_connects: sim_only_connects).sim_only_connects::delay1_empty$next", + ty: Bool, + }, + SlotDebugData { + name: "", + ty: Bool, + }, + SlotDebugData { + name: "", + ty: Bool, + }, + SlotDebugData { + name: "InstantiatedModule(sim_only_connects: sim_only_connects).sim_only_connects::helper2.cd.clk", + ty: Clock, + }, + SlotDebugData { + name: "InstantiatedModule(sim_only_connects: sim_only_connects).sim_only_connects::helper2.cd.rst", + ty: SyncReset, + }, + SlotDebugData { + name: "InstantiatedModule(sim_only_connects.helper2: sim_only_connects_helper).sim_only_connects_helper::cd.clk", + ty: Clock, + }, + SlotDebugData { + name: "InstantiatedModule(sim_only_connects.helper2: sim_only_connects_helper).sim_only_connects_helper::cd.rst", + ty: SyncReset, + }, + ], + .. + }, + sim_only_slots: StatePartLayout { + len: 15, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_only_connects: sim_only_connects).sim_only_connects::inp", + ty: SimOnly>>, + }, + SlotDebugData { + name: "InstantiatedModule(sim_only_connects: sim_only_connects).sim_only_connects::out1", + ty: SimOnly>>, + }, + SlotDebugData { + name: "InstantiatedModule(sim_only_connects: sim_only_connects).sim_only_connects::out2", + ty: SimOnly>>, + }, + SlotDebugData { + name: "InstantiatedModule(sim_only_connects: sim_only_connects).sim_only_connects::out3", + ty: SimOnly>>, + }, + SlotDebugData { + name: "InstantiatedModule(sim_only_connects: sim_only_connects).sim_only_connects::helper1.inp", + ty: SimOnly>>, + }, + SlotDebugData { + name: "InstantiatedModule(sim_only_connects: sim_only_connects).sim_only_connects::helper1.out", + ty: SimOnly>>, + }, + SlotDebugData { + name: "InstantiatedModule(sim_only_connects.helper1: sim_only_connects_helper).sim_only_connects_helper::inp", + ty: SimOnly>>, + }, + SlotDebugData { + name: "InstantiatedModule(sim_only_connects.helper1: sim_only_connects_helper).sim_only_connects_helper::out", + ty: SimOnly>>, + }, + SlotDebugData { + name: "InstantiatedModule(sim_only_connects: sim_only_connects).sim_only_connects::delay1", + ty: SimOnly>>, + }, + SlotDebugData { + name: "InstantiatedModule(sim_only_connects: sim_only_connects).sim_only_connects::delay1$next", + ty: SimOnly>>, + }, + SlotDebugData { + name: "", + ty: SimOnly>>, + }, + SlotDebugData { + name: "InstantiatedModule(sim_only_connects: sim_only_connects).sim_only_connects::helper2.inp", + ty: SimOnly>>, + }, + SlotDebugData { + name: "InstantiatedModule(sim_only_connects: sim_only_connects).sim_only_connects::helper2.out", + ty: SimOnly>>, + }, + SlotDebugData { + name: "InstantiatedModule(sim_only_connects.helper2: sim_only_connects_helper).sim_only_connects_helper::inp", + ty: SimOnly>>, + }, + SlotDebugData { + name: "InstantiatedModule(sim_only_connects.helper2: sim_only_connects_helper).sim_only_connects_helper::out", + ty: SimOnly>>, + }, + ], + layout_data: [ + SimOnly>>, + SimOnly>>, + SimOnly>>, + SimOnly>>, + SimOnly>>, + SimOnly>>, + SimOnly>>, + SimOnly>>, + SimOnly>>, + SimOnly>>, + SimOnly>>, + SimOnly>>, + SimOnly>>, + SimOnly>>, + SimOnly>>, + ], + .. + }, + }, + memories: StatePartLayout { + len: 0, + debug_data: [], + layout_data: [], + .. + }, + }, + insns: [ + // at: module-XXXXXXXXXX.rs:20:1 + 0: Copy { + dest: StatePartIndex(10), // (0x1) SlotDebugData { name: "InstantiatedModule(sim_only_connects: sim_only_connects).sim_only_connects::helper2.cd.clk", ty: Clock }, + src: StatePartIndex(0), // (0x1) SlotDebugData { name: "InstantiatedModule(sim_only_connects: sim_only_connects).sim_only_connects::cd.clk", ty: Clock }, + }, + 1: Copy { + dest: StatePartIndex(11), // (0x0) SlotDebugData { name: "InstantiatedModule(sim_only_connects: sim_only_connects).sim_only_connects::helper2.cd.rst", ty: SyncReset }, + src: StatePartIndex(1), // (0x0) SlotDebugData { name: "InstantiatedModule(sim_only_connects: sim_only_connects).sim_only_connects::cd.rst", ty: SyncReset }, + }, + // at: module-XXXXXXXXXX.rs:19:1 + 2: CloneSimOnly { + dest: StatePartIndex(12), // ({"bar": "baz", "extra": "value", "foo": "baz"}) SlotDebugData { name: "InstantiatedModule(sim_only_connects: sim_only_connects).sim_only_connects::helper2.out", ty: SimOnly>> }, + src: StatePartIndex(14), // ({"bar": "baz", "extra": "value", "foo": "baz"}) SlotDebugData { name: "InstantiatedModule(sim_only_connects.helper2: sim_only_connects_helper).sim_only_connects_helper::out", ty: SimOnly>> }, + }, + // at: module-XXXXXXXXXX.rs:22:1 + 3: CloneSimOnly { + dest: StatePartIndex(3), // ({"bar": "baz", "extra": "value", "foo": "baz"}) SlotDebugData { name: "InstantiatedModule(sim_only_connects: sim_only_connects).sim_only_connects::out3", ty: SimOnly>> }, + src: StatePartIndex(12), // ({"bar": "baz", "extra": "value", "foo": "baz"}) SlotDebugData { name: "InstantiatedModule(sim_only_connects: sim_only_connects).sim_only_connects::helper2.out", ty: SimOnly>> }, + }, + // at: module-XXXXXXXXXX.rs:19:1 + 4: Copy { + dest: StatePartIndex(12), // (0x1) SlotDebugData { name: "InstantiatedModule(sim_only_connects.helper2: sim_only_connects_helper).sim_only_connects_helper::cd.clk", ty: Clock }, + src: StatePartIndex(10), // (0x1) SlotDebugData { name: "InstantiatedModule(sim_only_connects: sim_only_connects).sim_only_connects::helper2.cd.clk", ty: Clock }, + }, + 5: Copy { + dest: StatePartIndex(13), // (0x0) SlotDebugData { name: "InstantiatedModule(sim_only_connects.helper2: sim_only_connects_helper).sim_only_connects_helper::cd.rst", ty: SyncReset }, + src: StatePartIndex(11), // (0x0) SlotDebugData { name: "InstantiatedModule(sim_only_connects: sim_only_connects).sim_only_connects::helper2.cd.rst", ty: SyncReset }, + }, + // at: module-XXXXXXXXXX.rs:1:1 + 6: Const { + dest: StatePartIndex(9), // (0x0) SlotDebugData { name: "", ty: Bool }, + value: 0x0, + }, + // at: module-XXXXXXXXXX.rs:17:1 + 7: Copy { + dest: StatePartIndex(7), // (0x0) SlotDebugData { name: "InstantiatedModule(sim_only_connects: sim_only_connects).sim_only_connects::delay1_empty$next", ty: Bool }, + src: StatePartIndex(9), // (0x0) SlotDebugData { name: "", ty: Bool }, + }, + // at: module-XXXXXXXXXX.rs:16:1 + 8: CloneSimOnly { + dest: StatePartIndex(9), // ({"extra": "value"}) SlotDebugData { name: "InstantiatedModule(sim_only_connects: sim_only_connects).sim_only_connects::delay1$next", ty: SimOnly>> }, + src: StatePartIndex(0), // ({"extra": "value"}) SlotDebugData { name: "InstantiatedModule(sim_only_connects: sim_only_connects).sim_only_connects::inp", ty: SimOnly>> }, + }, + // at: module-XXXXXXXXXX.rs:12:1 + 9: CloneSimOnly { + dest: StatePartIndex(1), // ({"extra": "value"}) SlotDebugData { name: "InstantiatedModule(sim_only_connects: sim_only_connects).sim_only_connects::out1", ty: SimOnly>> }, + src: StatePartIndex(8), // ({"extra": "value"}) SlotDebugData { name: "InstantiatedModule(sim_only_connects: sim_only_connects).sim_only_connects::delay1", ty: SimOnly>> }, + }, + // at: module-XXXXXXXXXX.rs:13:1 + 10: BranchIfZero { + target: 12, + value: StatePartIndex(6), // (0x0) SlotDebugData { name: "InstantiatedModule(sim_only_connects: sim_only_connects).sim_only_connects::delay1_empty", ty: Bool }, + }, + // at: module-XXXXXXXXXX.rs:15:1 + 11: CloneSimOnly { + dest: StatePartIndex(1), // ({"extra": "value"}) SlotDebugData { name: "InstantiatedModule(sim_only_connects: sim_only_connects).sim_only_connects::out1", ty: SimOnly>> }, + src: StatePartIndex(0), // ({"extra": "value"}) SlotDebugData { name: "InstantiatedModule(sim_only_connects: sim_only_connects).sim_only_connects::inp", ty: SimOnly>> }, + }, + // at: module-XXXXXXXXXX.rs:11:1 + 12: CloneSimOnly { + dest: StatePartIndex(4), // ({"extra": "value"}) SlotDebugData { name: "InstantiatedModule(sim_only_connects: sim_only_connects).sim_only_connects::helper1.inp", ty: SimOnly>> }, + src: StatePartIndex(8), // ({"extra": "value"}) SlotDebugData { name: "InstantiatedModule(sim_only_connects: sim_only_connects).sim_only_connects::delay1", ty: SimOnly>> }, + }, + // at: module-XXXXXXXXXX.rs:13:1 + 13: BranchIfZero { + target: 15, + value: StatePartIndex(6), // (0x0) SlotDebugData { name: "InstantiatedModule(sim_only_connects: sim_only_connects).sim_only_connects::delay1_empty", ty: Bool }, + }, + // at: module-XXXXXXXXXX.rs:14:1 + 14: CloneSimOnly { + dest: StatePartIndex(4), // ({"extra": "value"}) SlotDebugData { name: "InstantiatedModule(sim_only_connects: sim_only_connects).sim_only_connects::helper1.inp", ty: SimOnly>> }, + src: StatePartIndex(0), // ({"extra": "value"}) SlotDebugData { name: "InstantiatedModule(sim_only_connects: sim_only_connects).sim_only_connects::inp", ty: SimOnly>> }, + }, + // at: module-XXXXXXXXXX.rs:10:1 + 15: Copy { + dest: StatePartIndex(2), // (0x1) SlotDebugData { name: "InstantiatedModule(sim_only_connects: sim_only_connects).sim_only_connects::helper1.cd.clk", ty: Clock }, + src: StatePartIndex(0), // (0x1) SlotDebugData { name: "InstantiatedModule(sim_only_connects: sim_only_connects).sim_only_connects::cd.clk", ty: Clock }, + }, + 16: Copy { + dest: StatePartIndex(3), // (0x0) SlotDebugData { name: "InstantiatedModule(sim_only_connects: sim_only_connects).sim_only_connects::helper1.cd.rst", ty: SyncReset }, + src: StatePartIndex(1), // (0x0) SlotDebugData { name: "InstantiatedModule(sim_only_connects: sim_only_connects).sim_only_connects::cd.rst", ty: SyncReset }, + }, + // at: module-XXXXXXXXXX.rs:1:1 + 17: Const { + dest: StatePartIndex(8), // (0x1) SlotDebugData { name: "", ty: Bool }, + value: 0x1, + }, + // at: module-XXXXXXXXXX.rs:8:1 + 18: IsNonZeroDestIsSmall { + dest: StatePartIndex(3), // (0x0 0) SlotDebugData { name: "", ty: Bool }, + src: StatePartIndex(1), // (0x0) SlotDebugData { name: "InstantiatedModule(sim_only_connects: sim_only_connects).sim_only_connects::cd.rst", ty: SyncReset }, + }, + 19: IsNonZeroDestIsSmall { + dest: StatePartIndex(2), // (0x1 1) SlotDebugData { name: "", ty: Bool }, + src: StatePartIndex(0), // (0x1) SlotDebugData { name: "InstantiatedModule(sim_only_connects: sim_only_connects).sim_only_connects::cd.clk", ty: Clock }, + }, + 20: AndSmall { + dest: StatePartIndex(1), // (0x0 0) SlotDebugData { name: "", ty: Bool }, + lhs: StatePartIndex(2), // (0x1 1) SlotDebugData { name: "", ty: Bool }, + rhs: StatePartIndex(0), // (0x0 0) SlotDebugData { name: "", ty: Bool }, + }, + // at: module-XXXXXXXXXX.rs:7:1 + 21: CloneSimOnly { + dest: StatePartIndex(5), // ({"bar": "", "extra": "value", "foo": "baz"}) SlotDebugData { name: "InstantiatedModule(sim_only_connects: sim_only_connects).sim_only_connects::helper1.out", ty: SimOnly>> }, + src: StatePartIndex(7), // ({"bar": "", "extra": "value", "foo": "baz"}) SlotDebugData { name: "InstantiatedModule(sim_only_connects.helper1: sim_only_connects_helper).sim_only_connects_helper::out", ty: SimOnly>> }, + }, + // at: module-XXXXXXXXXX.rs:18:1 + 22: CloneSimOnly { + dest: StatePartIndex(2), // ({"bar": "", "extra": "value", "foo": "baz"}) SlotDebugData { name: "InstantiatedModule(sim_only_connects: sim_only_connects).sim_only_connects::out2", ty: SimOnly>> }, + src: StatePartIndex(5), // ({"bar": "", "extra": "value", "foo": "baz"}) SlotDebugData { name: "InstantiatedModule(sim_only_connects: sim_only_connects).sim_only_connects::helper1.out", ty: SimOnly>> }, + }, + // at: module-XXXXXXXXXX.rs:21:1 + 23: CloneSimOnly { + dest: StatePartIndex(11), // ({"bar": "", "extra": "value", "foo": "baz"}) SlotDebugData { name: "InstantiatedModule(sim_only_connects: sim_only_connects).sim_only_connects::helper2.inp", ty: SimOnly>> }, + src: StatePartIndex(2), // ({"bar": "", "extra": "value", "foo": "baz"}) SlotDebugData { name: "InstantiatedModule(sim_only_connects: sim_only_connects).sim_only_connects::out2", ty: SimOnly>> }, + }, + // at: module-XXXXXXXXXX.rs:19:1 + 24: CloneSimOnly { + dest: StatePartIndex(13), // ({"bar": "", "extra": "value", "foo": "baz"}) SlotDebugData { name: "InstantiatedModule(sim_only_connects.helper2: sim_only_connects_helper).sim_only_connects_helper::inp", ty: SimOnly>> }, + src: StatePartIndex(11), // ({"bar": "", "extra": "value", "foo": "baz"}) SlotDebugData { name: "InstantiatedModule(sim_only_connects: sim_only_connects).sim_only_connects::helper2.inp", ty: SimOnly>> }, + }, + // at: module-XXXXXXXXXX.rs:7:1 + 25: CloneSimOnly { + dest: StatePartIndex(6), // ({"extra": "value"}) SlotDebugData { name: "InstantiatedModule(sim_only_connects.helper1: sim_only_connects_helper).sim_only_connects_helper::inp", ty: SimOnly>> }, + src: StatePartIndex(4), // ({"extra": "value"}) SlotDebugData { name: "InstantiatedModule(sim_only_connects: sim_only_connects).sim_only_connects::helper1.inp", ty: SimOnly>> }, + }, + 26: Copy { + dest: StatePartIndex(4), // (0x1) SlotDebugData { name: "InstantiatedModule(sim_only_connects.helper1: sim_only_connects_helper).sim_only_connects_helper::cd.clk", ty: Clock }, + src: StatePartIndex(2), // (0x1) SlotDebugData { name: "InstantiatedModule(sim_only_connects: sim_only_connects).sim_only_connects::helper1.cd.clk", ty: Clock }, + }, + 27: Copy { + dest: StatePartIndex(5), // (0x0) SlotDebugData { name: "InstantiatedModule(sim_only_connects.helper1: sim_only_connects_helper).sim_only_connects_helper::cd.rst", ty: SyncReset }, + src: StatePartIndex(3), // (0x0) SlotDebugData { name: "InstantiatedModule(sim_only_connects: sim_only_connects).sim_only_connects::helper1.cd.rst", ty: SyncReset }, + }, + // at: module-XXXXXXXXXX.rs:8:1 + 28: BranchIfSmallZero { + target: 33, + value: StatePartIndex(1), // (0x0 0) SlotDebugData { name: "", ty: Bool }, + }, + 29: BranchIfSmallNonZero { + target: 32, + value: StatePartIndex(3), // (0x0 0) SlotDebugData { name: "", ty: Bool }, + }, + 30: CloneSimOnly { + dest: StatePartIndex(8), // ({"extra": "value"}) SlotDebugData { name: "InstantiatedModule(sim_only_connects: sim_only_connects).sim_only_connects::delay1", ty: SimOnly>> }, + src: StatePartIndex(9), // ({"extra": "value"}) SlotDebugData { name: "InstantiatedModule(sim_only_connects: sim_only_connects).sim_only_connects::delay1$next", ty: SimOnly>> }, + }, + 31: Branch { + target: 33, + }, + 32: CloneSimOnly { + dest: StatePartIndex(8), // ({"extra": "value"}) SlotDebugData { name: "InstantiatedModule(sim_only_connects: sim_only_connects).sim_only_connects::delay1", ty: SimOnly>> }, + src: StatePartIndex(10), // ({}) SlotDebugData { name: "", ty: SimOnly>> }, + }, + // at: module-XXXXXXXXXX.rs:9:1 + 33: BranchIfSmallZero { + target: 38, + value: StatePartIndex(1), // (0x0 0) SlotDebugData { name: "", ty: Bool }, + }, + 34: BranchIfSmallNonZero { + target: 37, + value: StatePartIndex(3), // (0x0 0) SlotDebugData { name: "", ty: Bool }, + }, + 35: Copy { + dest: StatePartIndex(6), // (0x0) SlotDebugData { name: "InstantiatedModule(sim_only_connects: sim_only_connects).sim_only_connects::delay1_empty", ty: Bool }, + src: StatePartIndex(7), // (0x0) SlotDebugData { name: "InstantiatedModule(sim_only_connects: sim_only_connects).sim_only_connects::delay1_empty$next", ty: Bool }, + }, + 36: Branch { + target: 38, + }, + 37: Copy { + dest: StatePartIndex(6), // (0x0) SlotDebugData { name: "InstantiatedModule(sim_only_connects: sim_only_connects).sim_only_connects::delay1_empty", ty: Bool }, + src: StatePartIndex(8), // (0x1) SlotDebugData { name: "", ty: Bool }, + }, + // at: module-XXXXXXXXXX.rs:8:1 + 38: XorSmallImmediate { + dest: StatePartIndex(0), // (0x0 0) SlotDebugData { name: "", ty: Bool }, + lhs: StatePartIndex(2), // (0x1 1) SlotDebugData { name: "", ty: Bool }, + rhs: 0x1, + }, + // at: module-XXXXXXXXXX.rs:1:1 + 39: Return, + ], + .. + }, + pc: 39, + memory_write_log: [], + memories: StatePart { + value: [], + }, + small_slots: StatePart { + value: [ + 0, + 0, + 1, + 0, + ], + }, + big_slots: StatePart { + value: [ + 1, + 0, + 1, + 0, + 1, + 0, + 0, + 0, + 1, + 0, + 1, + 0, + 1, + 0, + ], + }, + sim_only_slots: StatePart { + value: [ + { + "extra": "value", + }, + { + "extra": "value", + }, + { + "bar": "", + "extra": "value", + "foo": "baz", + }, + { + "bar": "baz", + "extra": "value", + "foo": "baz", + }, + { + "extra": "value", + }, + { + "bar": "", + "extra": "value", + "foo": "baz", + }, + { + "extra": "value", + }, + { + "bar": "", + "extra": "value", + "foo": "baz", + }, + { + "extra": "value", + }, + { + "extra": "value", + }, + {}, + { + "bar": "", + "extra": "value", + "foo": "baz", + }, + { + "bar": "baz", + "extra": "value", + "foo": "baz", + }, + { + "bar": "", + "extra": "value", + "foo": "baz", + }, + { + "bar": "baz", + "extra": "value", + "foo": "baz", + }, + ], + }, + }, + io: Instance { + name: ::sim_only_connects, + instantiated: Module { + name: sim_only_connects, + .. + }, + }, + main_module: SimulationModuleState { + base_targets: [ + Instance { + name: ::sim_only_connects, + instantiated: Module { + name: sim_only_connects, + .. + }, + }.cd, + Instance { + name: ::sim_only_connects, + instantiated: Module { + name: sim_only_connects, + .. + }, + }.inp, + Instance { + name: ::sim_only_connects, + instantiated: Module { + name: sim_only_connects, + .. + }, + }.out1, + Instance { + name: ::sim_only_connects, + instantiated: Module { + name: sim_only_connects, + .. + }, + }.out2, + Instance { + name: ::sim_only_connects, + instantiated: Module { + name: sim_only_connects, + .. + }, + }.out3, + ], + uninitialized_ios: {}, + io_targets: { + Instance { + name: ::sim_only_connects, + instantiated: Module { + name: sim_only_connects, + .. + }, + }.cd, + Instance { + name: ::sim_only_connects, + instantiated: Module { + name: sim_only_connects, + .. + }, + }.cd.clk, + Instance { + name: ::sim_only_connects, + instantiated: Module { + name: sim_only_connects, + .. + }, + }.cd.rst, + Instance { + name: ::sim_only_connects, + instantiated: Module { + name: sim_only_connects, + .. + }, + }.inp, + Instance { + name: ::sim_only_connects, + instantiated: Module { + name: sim_only_connects, + .. + }, + }.out1, + Instance { + name: ::sim_only_connects, + instantiated: Module { + name: sim_only_connects, + .. + }, + }.out2, + Instance { + name: ::sim_only_connects, + instantiated: Module { + name: sim_only_connects, + .. + }, + }.out3, + }, + did_initial_settle: true, + }, + extern_modules: [ + SimulationExternModuleState { + module_state: SimulationModuleState { + base_targets: [ + ModuleIO { + name: sim_only_connects_helper::cd, + is_input: true, + ty: Bundle { + /* offset = 0 */ + clk: Clock, + /* offset = 1 */ + rst: Reset, + }, + .. + }, + ModuleIO { + name: sim_only_connects_helper::inp, + is_input: true, + ty: SimOnly>>, + .. + }, + ModuleIO { + name: sim_only_connects_helper::out, + is_input: false, + ty: SimOnly>>, + .. + }, + ], + uninitialized_ios: {}, + io_targets: { + ModuleIO { + name: sim_only_connects_helper::cd, + is_input: true, + ty: Bundle { + /* offset = 0 */ + clk: Clock, + /* offset = 1 */ + rst: Reset, + }, + .. + }, + ModuleIO { + name: sim_only_connects_helper::cd, + is_input: true, + ty: Bundle { + /* offset = 0 */ + clk: Clock, + /* offset = 1 */ + rst: Reset, + }, + .. + }.clk, + ModuleIO { + name: sim_only_connects_helper::cd, + is_input: true, + ty: Bundle { + /* offset = 0 */ + clk: Clock, + /* offset = 1 */ + rst: Reset, + }, + .. + }.rst, + ModuleIO { + name: sim_only_connects_helper::inp, + is_input: true, + ty: SimOnly>>, + .. + }, + ModuleIO { + name: sim_only_connects_helper::out, + is_input: false, + ty: SimOnly>>, + .. + }, + }, + did_initial_settle: true, + }, + sim: ExternModuleSimulation { + generator: SimGeneratorFn { + args: ( + ModuleIO { + name: sim_only_connects_helper::cd, + is_input: true, + ty: Bundle { + /* offset = 0 */ + clk: Clock, + /* offset = 1 */ + rst: Reset, + }, + .. + }, + ModuleIO { + name: sim_only_connects_helper::inp, + is_input: true, + ty: SimOnly>>, + .. + }, + ModuleIO { + name: sim_only_connects_helper::out, + is_input: false, + ty: SimOnly>>, + .. + }, + ), + f: ..., + }, + sim_io_to_generator_map: { + ModuleIO { + name: sim_only_connects_helper::cd, + is_input: true, + ty: Bundle { + /* offset = 0 */ + clk: Clock, + /* offset = 1 */ + rst: SyncReset, + }, + .. + }: ModuleIO { + name: sim_only_connects_helper::cd, + is_input: true, + ty: Bundle { + /* offset = 0 */ + clk: Clock, + /* offset = 1 */ + rst: Reset, + }, + .. + }, + ModuleIO { + name: sim_only_connects_helper::inp, + is_input: true, + ty: SimOnly>>, + .. + }: ModuleIO { + name: sim_only_connects_helper::inp, + is_input: true, + ty: SimOnly>>, + .. + }, + ModuleIO { + name: sim_only_connects_helper::out, + is_input: false, + ty: SimOnly>>, + .. + }: ModuleIO { + name: sim_only_connects_helper::out, + is_input: false, + ty: SimOnly>>, + .. + }, + }, + source_location: SourceLocation( + module-XXXXXXXXXX-2.rs:5:1, + ), + }, + running_generator: Some( + ..., + ), + wait_targets: { + Change { + key: CompiledValue { + layout: CompiledTypeLayout { + ty: Clock, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "", + ty: Clock, + }, + ], + .. + }, + sim_only_slots: StatePartLayout { + len: 0, + debug_data: [], + layout_data: [], + .. + }, + }, + body: Scalar, + }, + range: TypeIndexRange { + small_slots: StatePartIndexRange { start: 0, len: 0 }, + big_slots: StatePartIndexRange { start: 4, len: 1 }, + sim_only_slots: StatePartIndexRange { start: 6, len: 0 }, + }, + write: None, + }, + value: SimValue { + ty: Clock, + value: OpaqueSimValue { + bits: 0x1_u1, + sim_only_values: [], + }, + }, + }, + }, + }, + SimulationExternModuleState { + module_state: SimulationModuleState { + base_targets: [ + ModuleIO { + name: sim_only_connects_helper::cd, + is_input: true, + ty: Bundle { + /* offset = 0 */ + clk: Clock, + /* offset = 1 */ + rst: Reset, + }, + .. + }, + ModuleIO { + name: sim_only_connects_helper::inp, + is_input: true, + ty: SimOnly>>, + .. + }, + ModuleIO { + name: sim_only_connects_helper::out, + is_input: false, + ty: SimOnly>>, + .. + }, + ], + uninitialized_ios: {}, + io_targets: { + ModuleIO { + name: sim_only_connects_helper::cd, + is_input: true, + ty: Bundle { + /* offset = 0 */ + clk: Clock, + /* offset = 1 */ + rst: Reset, + }, + .. + }, + ModuleIO { + name: sim_only_connects_helper::cd, + is_input: true, + ty: Bundle { + /* offset = 0 */ + clk: Clock, + /* offset = 1 */ + rst: Reset, + }, + .. + }.clk, + ModuleIO { + name: sim_only_connects_helper::cd, + is_input: true, + ty: Bundle { + /* offset = 0 */ + clk: Clock, + /* offset = 1 */ + rst: Reset, + }, + .. + }.rst, + ModuleIO { + name: sim_only_connects_helper::inp, + is_input: true, + ty: SimOnly>>, + .. + }, + ModuleIO { + name: sim_only_connects_helper::out, + is_input: false, + ty: SimOnly>>, + .. + }, + }, + did_initial_settle: true, + }, + sim: ExternModuleSimulation { + generator: SimGeneratorFn { + args: ( + ModuleIO { + name: sim_only_connects_helper::cd, + is_input: true, + ty: Bundle { + /* offset = 0 */ + clk: Clock, + /* offset = 1 */ + rst: Reset, + }, + .. + }, + ModuleIO { + name: sim_only_connects_helper::inp, + is_input: true, + ty: SimOnly>>, + .. + }, + ModuleIO { + name: sim_only_connects_helper::out, + is_input: false, + ty: SimOnly>>, + .. + }, + ), + f: ..., + }, + sim_io_to_generator_map: { + ModuleIO { + name: sim_only_connects_helper::cd, + is_input: true, + ty: Bundle { + /* offset = 0 */ + clk: Clock, + /* offset = 1 */ + rst: SyncReset, + }, + .. + }: ModuleIO { + name: sim_only_connects_helper::cd, + is_input: true, + ty: Bundle { + /* offset = 0 */ + clk: Clock, + /* offset = 1 */ + rst: Reset, + }, + .. + }, + ModuleIO { + name: sim_only_connects_helper::inp, + is_input: true, + ty: SimOnly>>, + .. + }: ModuleIO { + name: sim_only_connects_helper::inp, + is_input: true, + ty: SimOnly>>, + .. + }, + ModuleIO { + name: sim_only_connects_helper::out, + is_input: false, + ty: SimOnly>>, + .. + }: ModuleIO { + name: sim_only_connects_helper::out, + is_input: false, + ty: SimOnly>>, + .. + }, + }, + source_location: SourceLocation( + module-XXXXXXXXXX-2.rs:5:1, + ), + }, + running_generator: Some( + ..., + ), + wait_targets: { + Change { + key: CompiledValue { + layout: CompiledTypeLayout { + ty: Clock, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "", + ty: Clock, + }, + ], + .. + }, + sim_only_slots: StatePartLayout { + len: 0, + debug_data: [], + layout_data: [], + .. + }, + }, + body: Scalar, + }, + range: TypeIndexRange { + small_slots: StatePartIndexRange { start: 4, len: 0 }, + big_slots: StatePartIndexRange { start: 12, len: 1 }, + sim_only_slots: StatePartIndexRange { start: 13, len: 0 }, + }, + write: None, + }, + value: SimValue { + ty: Clock, + value: OpaqueSimValue { + bits: 0x1_u1, + sim_only_values: [], + }, + }, + }, + }, + }, + ], + state_ready_to_run: false, + trace_decls: TraceModule { + name: "sim_only_connects", + children: [ + TraceModuleIO { + name: "cd", + child: TraceBundle { + name: "cd", + fields: [ + TraceClock { + location: TraceScalarId(0), + name: "clk", + flow: Source, + }, + TraceSyncReset { + location: TraceScalarId(1), + name: "rst", + flow: Source, + }, + ], + ty: Bundle { + /* offset = 0 */ + clk: Clock, + /* offset = 1 */ + rst: SyncReset, + }, + flow: Source, + }, + ty: Bundle { + /* offset = 0 */ + clk: Clock, + /* offset = 1 */ + rst: SyncReset, + }, + flow: Source, + }, + TraceModuleIO { + name: "inp", + child: TraceSimOnly { + location: TraceScalarId(2), + name: "inp", + ty: SimOnly>>, + flow: Source, + }, + ty: SimOnly>>, + flow: Source, + }, + TraceModuleIO { + name: "out1", + child: TraceSimOnly { + location: TraceScalarId(3), + name: "out1", + ty: SimOnly>>, + flow: Sink, + }, + ty: SimOnly>>, + flow: Sink, + }, + TraceModuleIO { + name: "out2", + child: TraceSimOnly { + location: TraceScalarId(4), + name: "out2", + ty: SimOnly>>, + flow: Sink, + }, + ty: SimOnly>>, + flow: Sink, + }, + TraceModuleIO { + name: "out3", + child: TraceSimOnly { + location: TraceScalarId(5), + name: "out3", + ty: SimOnly>>, + flow: Sink, + }, + ty: SimOnly>>, + flow: Sink, + }, + TraceInstance { + name: "helper1", + instance_io: TraceBundle { + name: "helper1", + fields: [ + TraceBundle { + name: "cd", + fields: [ + TraceClock { + location: TraceScalarId(10), + name: "clk", + flow: Sink, + }, + TraceSyncReset { + location: TraceScalarId(11), + name: "rst", + flow: Sink, + }, + ], + ty: Bundle { + /* offset = 0 */ + clk: Clock, + /* offset = 1 */ + rst: SyncReset, + }, + flow: Sink, + }, + TraceSimOnly { + location: TraceScalarId(12), + name: "inp", + ty: SimOnly>>, + flow: Sink, + }, + TraceSimOnly { + location: TraceScalarId(13), + name: "out", + ty: SimOnly>>, + flow: Source, + }, + ], + ty: Bundle { + #[hdl(flip)] /* offset = 0 */ + cd: Bundle { + /* offset = 0 */ + clk: Clock, + /* offset = 1 */ + rst: SyncReset, + }, + #[hdl(flip)] /* offset = 2 */ + inp: SimOnly>>, + /* offset = 2 */ + out: SimOnly>>, + }, + flow: Source, + }, + module: TraceModule { + name: "sim_only_connects_helper", + children: [ + TraceModuleIO { + name: "cd", + child: TraceBundle { + name: "cd", + fields: [ + TraceClock { + location: TraceScalarId(6), + name: "clk", + flow: Source, + }, + TraceSyncReset { + location: TraceScalarId(7), + name: "rst", + flow: Source, + }, + ], + ty: Bundle { + /* offset = 0 */ + clk: Clock, + /* offset = 1 */ + rst: SyncReset, + }, + flow: Source, + }, + ty: Bundle { + /* offset = 0 */ + clk: Clock, + /* offset = 1 */ + rst: SyncReset, + }, + flow: Source, + }, + TraceModuleIO { + name: "inp", + child: TraceSimOnly { + location: TraceScalarId(8), + name: "inp", + ty: SimOnly>>, + flow: Source, + }, + ty: SimOnly>>, + flow: Source, + }, + TraceModuleIO { + name: "out", + child: TraceSimOnly { + location: TraceScalarId(9), + name: "out", + ty: SimOnly>>, + flow: Sink, + }, + ty: SimOnly>>, + flow: Sink, + }, + ], + }, + ty: Bundle { + #[hdl(flip)] /* offset = 0 */ + cd: Bundle { + /* offset = 0 */ + clk: Clock, + /* offset = 1 */ + rst: SyncReset, + }, + #[hdl(flip)] /* offset = 2 */ + inp: SimOnly>>, + /* offset = 2 */ + out: SimOnly>>, + }, + }, + TraceReg { + name: "delay1", + child: TraceSimOnly { + location: TraceScalarId(14), + name: "delay1", + ty: SimOnly>>, + flow: Duplex, + }, + ty: SimOnly>>, + }, + TraceReg { + name: "delay1_empty", + child: TraceBool { + location: TraceScalarId(15), + name: "delay1_empty", + flow: Duplex, + }, + ty: Bool, + }, + TraceInstance { + name: "helper2", + instance_io: TraceBundle { + name: "helper2", + fields: [ + TraceBundle { + name: "cd", + fields: [ + TraceClock { + location: TraceScalarId(20), + name: "clk", + flow: Sink, + }, + TraceSyncReset { + location: TraceScalarId(21), + name: "rst", + flow: Sink, + }, + ], + ty: Bundle { + /* offset = 0 */ + clk: Clock, + /* offset = 1 */ + rst: SyncReset, + }, + flow: Sink, + }, + TraceSimOnly { + location: TraceScalarId(22), + name: "inp", + ty: SimOnly>>, + flow: Sink, + }, + TraceSimOnly { + location: TraceScalarId(23), + name: "out", + ty: SimOnly>>, + flow: Source, + }, + ], + ty: Bundle { + #[hdl(flip)] /* offset = 0 */ + cd: Bundle { + /* offset = 0 */ + clk: Clock, + /* offset = 1 */ + rst: SyncReset, + }, + #[hdl(flip)] /* offset = 2 */ + inp: SimOnly>>, + /* offset = 2 */ + out: SimOnly>>, + }, + flow: Source, + }, + module: TraceModule { + name: "sim_only_connects_helper", + children: [ + TraceModuleIO { + name: "cd", + child: TraceBundle { + name: "cd", + fields: [ + TraceClock { + location: TraceScalarId(16), + name: "clk", + flow: Source, + }, + TraceSyncReset { + location: TraceScalarId(17), + name: "rst", + flow: Source, + }, + ], + ty: Bundle { + /* offset = 0 */ + clk: Clock, + /* offset = 1 */ + rst: SyncReset, + }, + flow: Source, + }, + ty: Bundle { + /* offset = 0 */ + clk: Clock, + /* offset = 1 */ + rst: SyncReset, + }, + flow: Source, + }, + TraceModuleIO { + name: "inp", + child: TraceSimOnly { + location: TraceScalarId(18), + name: "inp", + ty: SimOnly>>, + flow: Source, + }, + ty: SimOnly>>, + flow: Source, + }, + TraceModuleIO { + name: "out", + child: TraceSimOnly { + location: TraceScalarId(19), + name: "out", + ty: SimOnly>>, + flow: Sink, + }, + ty: SimOnly>>, + flow: Sink, + }, + ], + }, + ty: Bundle { + #[hdl(flip)] /* offset = 0 */ + cd: Bundle { + /* offset = 0 */ + clk: Clock, + /* offset = 1 */ + rst: SyncReset, + }, + #[hdl(flip)] /* offset = 2 */ + inp: SimOnly>>, + /* offset = 2 */ + out: SimOnly>>, + }, + }, + ], + }, + traces: [ + SimTrace { + id: TraceScalarId(0), + kind: BigClock { + index: StatePartIndex(0), + }, + state: 0x1, + last_state: 0x1, + }, + SimTrace { + id: TraceScalarId(1), + kind: BigSyncReset { + index: StatePartIndex(1), + }, + state: 0x0, + last_state: 0x0, + }, + SimTrace { + id: TraceScalarId(2), + kind: SimOnly { + index: StatePartIndex(0), + ty: SimOnly>>, + }, + state: { + "extra": "value", + }, + last_state: { + "extra": "value", + }, + }, + SimTrace { + id: TraceScalarId(3), + kind: SimOnly { + index: StatePartIndex(1), + ty: SimOnly>>, + }, + state: { + "extra": "value", + }, + last_state: { + "extra": "value", + }, + }, + SimTrace { + id: TraceScalarId(4), + kind: SimOnly { + index: StatePartIndex(2), + ty: SimOnly>>, + }, + state: { + "bar": "", + "extra": "value", + "foo": "baz", + }, + last_state: { + "bar": "", + "extra": "value", + "foo": "baz", + }, + }, + SimTrace { + id: TraceScalarId(5), + kind: SimOnly { + index: StatePartIndex(3), + ty: SimOnly>>, + }, + state: { + "bar": "baz", + "extra": "value", + "foo": "baz", + }, + last_state: { + "bar": "baz", + "extra": "value", + "foo": "baz", + }, + }, + SimTrace { + id: TraceScalarId(6), + kind: BigClock { + index: StatePartIndex(4), + }, + state: 0x1, + last_state: 0x1, + }, + SimTrace { + id: TraceScalarId(7), + kind: BigSyncReset { + index: StatePartIndex(5), + }, + state: 0x0, + last_state: 0x0, + }, + SimTrace { + id: TraceScalarId(8), + kind: SimOnly { + index: StatePartIndex(6), + ty: SimOnly>>, + }, + state: { + "extra": "value", + }, + last_state: { + "extra": "value", + }, + }, + SimTrace { + id: TraceScalarId(9), + kind: SimOnly { + index: StatePartIndex(7), + ty: SimOnly>>, + }, + state: { + "bar": "", + "extra": "value", + "foo": "baz", + }, + last_state: { + "bar": "", + "extra": "value", + "foo": "baz", + }, + }, + SimTrace { + id: TraceScalarId(10), + kind: BigClock { + index: StatePartIndex(2), + }, + state: 0x1, + last_state: 0x1, + }, + SimTrace { + id: TraceScalarId(11), + kind: BigSyncReset { + index: StatePartIndex(3), + }, + state: 0x0, + last_state: 0x0, + }, + SimTrace { + id: TraceScalarId(12), + kind: SimOnly { + index: StatePartIndex(4), + ty: SimOnly>>, + }, + state: { + "extra": "value", + }, + last_state: { + "extra": "value", + }, + }, + SimTrace { + id: TraceScalarId(13), + kind: SimOnly { + index: StatePartIndex(5), + ty: SimOnly>>, + }, + state: { + "bar": "", + "extra": "value", + "foo": "baz", + }, + last_state: { + "bar": "", + "extra": "value", + "foo": "baz", + }, + }, + SimTrace { + id: TraceScalarId(14), + kind: SimOnly { + index: StatePartIndex(8), + ty: SimOnly>>, + }, + state: { + "extra": "value", + }, + last_state: { + "extra": "value", + }, + }, + SimTrace { + id: TraceScalarId(15), + kind: BigBool { + index: StatePartIndex(6), + }, + state: 0x0, + last_state: 0x0, + }, + SimTrace { + id: TraceScalarId(16), + kind: BigClock { + index: StatePartIndex(12), + }, + state: 0x1, + last_state: 0x1, + }, + SimTrace { + id: TraceScalarId(17), + kind: BigSyncReset { + index: StatePartIndex(13), + }, + state: 0x0, + last_state: 0x0, + }, + SimTrace { + id: TraceScalarId(18), + kind: SimOnly { + index: StatePartIndex(13), + ty: SimOnly>>, + }, + state: { + "bar": "", + "extra": "value", + "foo": "baz", + }, + last_state: { + "bar": "", + "extra": "value", + "foo": "baz", + }, + }, + SimTrace { + id: TraceScalarId(19), + kind: SimOnly { + index: StatePartIndex(14), + ty: SimOnly>>, + }, + state: { + "bar": "baz", + "extra": "value", + "foo": "baz", + }, + last_state: { + "bar": "baz", + "extra": "value", + "foo": "baz", + }, + }, + SimTrace { + id: TraceScalarId(20), + kind: BigClock { + index: StatePartIndex(10), + }, + state: 0x1, + last_state: 0x1, + }, + SimTrace { + id: TraceScalarId(21), + kind: BigSyncReset { + index: StatePartIndex(11), + }, + state: 0x0, + last_state: 0x0, + }, + SimTrace { + id: TraceScalarId(22), + kind: SimOnly { + index: StatePartIndex(11), + ty: SimOnly>>, + }, + state: { + "bar": "", + "extra": "value", + "foo": "baz", + }, + last_state: { + "bar": "", + "extra": "value", + "foo": "baz", + }, + }, + SimTrace { + id: TraceScalarId(23), + kind: SimOnly { + index: StatePartIndex(12), + ty: SimOnly>>, + }, + state: { + "bar": "baz", + "extra": "value", + "foo": "baz", + }, + last_state: { + "bar": "baz", + "extra": "value", + "foo": "baz", + }, + }, + ], + trace_memories: {}, + trace_writers: [ + Running( + VcdWriter { + finished_init: true, + timescale: 1 ps, + .. + }, + ), + ], + instant: 16 μs, + clocks_triggered: [ + StatePartIndex(1), + ], + .. +} \ No newline at end of file diff --git a/crates/fayalite/tests/sim/expected/sim_only_connects.vcd b/crates/fayalite/tests/sim/expected/sim_only_connects.vcd new file mode 100644 index 0000000..2f464c0 --- /dev/null +++ b/crates/fayalite/tests/sim/expected/sim_only_connects.vcd @@ -0,0 +1,185 @@ +$timescale 1 ps $end +$scope module sim_only_connects $end +$scope struct cd $end +$var wire 1 ! clk $end +$var wire 1 " rst $end +$upscope $end +$var string 1 # inp $end +$var string 1 $ out1 $end +$var string 1 % out2 $end +$var string 1 & out3 $end +$scope struct helper1 $end +$scope struct cd $end +$var wire 1 + clk $end +$var wire 1 , rst $end +$upscope $end +$var string 1 - inp $end +$var string 1 . out $end +$upscope $end +$scope module sim_only_connects_helper $end +$scope struct cd $end +$var wire 1 ' clk $end +$var wire 1 ( rst $end +$upscope $end +$var string 1 ) inp $end +$var string 1 * out $end +$upscope $end +$var string 1 / delay1 $end +$var reg 1 0 delay1_empty $end +$scope struct helper2 $end +$scope struct cd $end +$var wire 1 5 clk $end +$var wire 1 6 rst $end +$upscope $end +$var string 1 7 inp $end +$var string 1 8 out $end +$upscope $end +$scope module sim_only_connects_helper_2 $end +$scope struct cd $end +$var wire 1 1 clk $end +$var wire 1 2 rst $end +$upscope $end +$var string 1 3 inp $end +$var string 1 4 out $end +$upscope $end +$upscope $end +$enddefinitions $end +$dumpvars +0! +1" +s{\"extra\":\x20\"value\"} # +s{} $ +s{} % +s{} & +0' +1( +s{} ) +s{} * +0+ +1, +s{} - +s{} . +s{} / +00 +01 +12 +s{} 3 +s{} 4 +05 +16 +s{} 7 +s{} 8 +$end +#1000000 +1! +1' +1+ +10 +11 +15 +s{\"extra\":\x20\"value\"} $ +s{\"extra\":\x20\"value\"} ) +s{\"extra\":\x20\"value\"} - +s{\"bar\":\x20\"\",\x20\"extra\":\x20\"value\",\x20\"foo\":\x20\"baz\"} * +s{\"bar\":\x20\"\",\x20\"foo\":\x20\"baz\"} 4 +s{\"bar\":\x20\"\",\x20\"extra\":\x20\"value\",\x20\"foo\":\x20\"baz\"} % +s{\"bar\":\x20\"\",\x20\"foo\":\x20\"baz\"} & +s{\"bar\":\x20\"\",\x20\"extra\":\x20\"value\",\x20\"foo\":\x20\"baz\"} . +s{\"bar\":\x20\"\",\x20\"extra\":\x20\"value\",\x20\"foo\":\x20\"baz\"} 3 +s{\"bar\":\x20\"\",\x20\"extra\":\x20\"value\",\x20\"foo\":\x20\"baz\"} 7 +s{\"bar\":\x20\"\",\x20\"foo\":\x20\"baz\"} 8 +#2000000 +0! +0" +0' +0( +0+ +0, +01 +02 +05 +06 +#3000000 +1! +1' +1+ +s{\"extra\":\x20\"value\"} / +00 +11 +15 +s{\"bar\":\x20\"baz\",\x20\"extra\":\x20\"value\",\x20\"foo\":\x20\"baz\"} 4 +s{\"bar\":\x20\"baz\",\x20\"extra\":\x20\"value\",\x20\"foo\":\x20\"baz\"} & +s{\"bar\":\x20\"baz\",\x20\"extra\":\x20\"value\",\x20\"foo\":\x20\"baz\"} 8 +#4000000 +0! +0' +0+ +01 +05 +#5000000 +1! +1' +1+ +11 +15 +#6000000 +0! +0' +0+ +01 +05 +#7000000 +1! +1' +1+ +11 +15 +#8000000 +0! +0' +0+ +01 +05 +#9000000 +1! +1' +1+ +11 +15 +#10000000 +0! +0' +0+ +01 +05 +#11000000 +1! +1' +1+ +11 +15 +#12000000 +0! +0' +0+ +01 +05 +#13000000 +1! +1' +1+ +11 +15 +#14000000 +0! +0' +0+ +01 +05 +#15000000 +1! +1' +1+ +11 +15 +#16000000 diff --git a/crates/fayalite/tests/ui/simvalue_is_not_internable.rs b/crates/fayalite/tests/ui/simvalue_is_not_internable.rs new file mode 100644 index 0000000..d40990f --- /dev/null +++ b/crates/fayalite/tests/ui/simvalue_is_not_internable.rs @@ -0,0 +1,15 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// See Notices.txt for copyright information + +//! check that SimValue can't be interned, since equality may ignore types + +use fayalite::{ + intern::{Intern, Interned}, + sim::value::SimValue, +}; + +fn f(v: SimValue<()>) -> Interned> { + Intern::intern_sized(v) +} + +fn main() {} diff --git a/crates/fayalite/tests/ui/simvalue_is_not_internable.stderr b/crates/fayalite/tests/ui/simvalue_is_not_internable.stderr new file mode 100644 index 0000000..03c62bf --- /dev/null +++ b/crates/fayalite/tests/ui/simvalue_is_not_internable.stderr @@ -0,0 +1,369 @@ +error[E0277]: `Cell` cannot be shared between threads safely + --> tests/ui/simvalue_is_not_internable.rs:11:26 + | +11 | fn f(v: SimValue<()>) -> Interned> { + | ^^^^^^^^^^^^^^^^^^^^^^ `Cell` cannot be shared between threads safely + | + = help: within `SimValue<()>`, the trait `Sync` is not implemented for `Cell` + = note: if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` +note: required because it appears within the type `util::alternating_cell::AlternatingCell>` + --> src/util/alternating_cell.rs + | + | pub(crate) struct AlternatingCell { + | ^^^^^^^^^^^^^^^ +note: required because it appears within the type `SimValue<()>` + --> src/sim/value.rs + | + | pub struct SimValue { + | ^^^^^^^^ +note: required by a bound in `fayalite::intern::Interned` + --> src/intern.rs + | + | pub struct Interned { + | ^^^^ required by this bound in `Interned` + +error[E0277]: `UnsafeCell>` cannot be shared between threads safely + --> tests/ui/simvalue_is_not_internable.rs:11:26 + | +11 | fn f(v: SimValue<()>) -> Interned> { + | ^^^^^^^^^^^^^^^^^^^^^^ `UnsafeCell>` cannot be shared between threads safely + | + = help: within `SimValue<()>`, the trait `Sync` is not implemented for `UnsafeCell>` +note: required because it appears within the type `util::alternating_cell::AlternatingCell>` + --> src/util/alternating_cell.rs + | + | pub(crate) struct AlternatingCell { + | ^^^^^^^^^^^^^^^ +note: required because it appears within the type `SimValue<()>` + --> src/sim/value.rs + | + | pub struct SimValue { + | ^^^^^^^^ +note: required by a bound in `fayalite::intern::Interned` + --> src/intern.rs + | + | pub struct Interned { + | ^^^^ required by this bound in `Interned` + +error[E0277]: `Rc<(dyn value::sim_only_value_unsafe::DynSimOnlyValueTrait + 'static)>` cannot be sent between threads safely + --> tests/ui/simvalue_is_not_internable.rs:11:26 + | +11 | fn f(v: SimValue<()>) -> Interned> { + | ^^^^^^^^^^^^^^^^^^^^^^ `Rc<(dyn value::sim_only_value_unsafe::DynSimOnlyValueTrait + 'static)>` cannot be sent between threads safely + | + = help: within `SimValue<()>`, the trait `Send` is not implemented for `Rc<(dyn value::sim_only_value_unsafe::DynSimOnlyValueTrait + 'static)>` +note: required because it appears within the type `DynSimOnlyValue` + --> src/sim/value/sim_only_value_unsafe.rs + | + | pub struct DynSimOnlyValue(Rc); + | ^^^^^^^^^^^^^^^ +note: required because it appears within the type `PhantomData` + --> $RUST/core/src/marker.rs + | + | pub struct PhantomData; + | ^^^^^^^^^^^ +note: required because it appears within the type `alloc::raw_vec::RawVec` + --> $RUST/alloc/src/raw_vec/mod.rs + | + | pub(crate) struct RawVec { + | ^^^^^^ +note: required because it appears within the type `Vec` + --> $RUST/alloc/src/vec/mod.rs + | + | pub struct Vec { + | ^^^ +note: required because it appears within the type `OpaqueSimValue` + --> src/ty.rs + | + | pub struct OpaqueSimValue { + | ^^^^^^^^^^^^^^ +note: required because it appears within the type `value::SimValueInner<()>` + --> src/sim/value.rs + | + | struct SimValueInner { + | ^^^^^^^^^^^^^ +note: required because it appears within the type `UnsafeCell>` + --> $RUST/core/src/cell.rs + | + | pub struct UnsafeCell { + | ^^^^^^^^^^ +note: required because it appears within the type `util::alternating_cell::AlternatingCell>` + --> src/util/alternating_cell.rs + | + | pub(crate) struct AlternatingCell { + | ^^^^^^^^^^^^^^^ +note: required because it appears within the type `SimValue<()>` + --> src/sim/value.rs + | + | pub struct SimValue { + | ^^^^^^^^ +note: required by a bound in `fayalite::intern::Interned` + --> src/intern.rs + | + | pub struct Interned { + | ^^^^ required by this bound in `Interned` + +error[E0277]: the trait bound `SimValue<()>: Intern` is not satisfied + --> tests/ui/simvalue_is_not_internable.rs:12:26 + | +12 | Intern::intern_sized(v) + | -------------------- ^ the trait `Hash` is not implemented for `SimValue<()>` + | | + | required by a bound introduced by this call + | + = note: required for `SimValue<()>` to implement `Intern` +help: consider dereferencing here + | +12 | Intern::intern_sized(*v) + | + + +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<()>` + | | + | required by a bound introduced by this call + | + = note: required for `SimValue<()>` to implement `Intern` +help: consider dereferencing here + | +12 | Intern::intern_sized(*v) + | + + +error[E0277]: `Cell` cannot be shared between threads safely + --> tests/ui/simvalue_is_not_internable.rs:12:26 + | +12 | Intern::intern_sized(v) + | -------------------- ^ `Cell` cannot be shared between threads safely + | | + | required by a bound introduced by this call + | + = help: within `SimValue<()>`, the trait `Sync` is not implemented for `Cell` + = note: if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` +note: required because it appears within the type `util::alternating_cell::AlternatingCell>` + --> src/util/alternating_cell.rs + | + | pub(crate) struct AlternatingCell { + | ^^^^^^^^^^^^^^^ +note: required because it appears within the type `SimValue<()>` + --> src/sim/value.rs + | + | pub struct SimValue { + | ^^^^^^^^ +note: required by a bound in `intern_sized` + --> src/intern.rs + | + | pub trait Intern: Any + Send + Sync { + | ^^^^ required by this bound in `Intern::intern_sized` + | fn intern(&self) -> Interned; + | fn intern_sized(self) -> Interned + | ------------ required by a bound in this associated function +help: consider dereferencing here + | +12 | Intern::intern_sized(*v) + | + + +error[E0277]: `UnsafeCell>` cannot be shared between threads safely + --> tests/ui/simvalue_is_not_internable.rs:12:26 + | +12 | Intern::intern_sized(v) + | -------------------- ^ `UnsafeCell>` cannot be shared between threads safely + | | + | required by a bound introduced by this call + | + = help: within `SimValue<()>`, the trait `Sync` is not implemented for `UnsafeCell>` +note: required because it appears within the type `util::alternating_cell::AlternatingCell>` + --> src/util/alternating_cell.rs + | + | pub(crate) struct AlternatingCell { + | ^^^^^^^^^^^^^^^ +note: required because it appears within the type `SimValue<()>` + --> src/sim/value.rs + | + | pub struct SimValue { + | ^^^^^^^^ +note: required by a bound in `intern_sized` + --> src/intern.rs + | + | pub trait Intern: Any + Send + Sync { + | ^^^^ required by this bound in `Intern::intern_sized` + | fn intern(&self) -> Interned; + | fn intern_sized(self) -> Interned + | ------------ required by a bound in this associated function +help: consider dereferencing here + | +12 | Intern::intern_sized(*v) + | + + +error[E0277]: `Rc<(dyn value::sim_only_value_unsafe::DynSimOnlyValueTrait + 'static)>` cannot be sent between threads safely + --> tests/ui/simvalue_is_not_internable.rs:12:26 + | +12 | Intern::intern_sized(v) + | -------------------- ^ `Rc<(dyn value::sim_only_value_unsafe::DynSimOnlyValueTrait + 'static)>` cannot be sent between threads safely + | | + | required by a bound introduced by this call + | + = help: within `SimValue<()>`, the trait `Send` is not implemented for `Rc<(dyn value::sim_only_value_unsafe::DynSimOnlyValueTrait + 'static)>` +note: required because it appears within the type `DynSimOnlyValue` + --> src/sim/value/sim_only_value_unsafe.rs + | + | pub struct DynSimOnlyValue(Rc); + | ^^^^^^^^^^^^^^^ +note: required because it appears within the type `PhantomData` + --> $RUST/core/src/marker.rs + | + | pub struct PhantomData; + | ^^^^^^^^^^^ +note: required because it appears within the type `alloc::raw_vec::RawVec` + --> $RUST/alloc/src/raw_vec/mod.rs + | + | pub(crate) struct RawVec { + | ^^^^^^ +note: required because it appears within the type `Vec` + --> $RUST/alloc/src/vec/mod.rs + | + | pub struct Vec { + | ^^^ +note: required because it appears within the type `OpaqueSimValue` + --> src/ty.rs + | + | pub struct OpaqueSimValue { + | ^^^^^^^^^^^^^^ +note: required because it appears within the type `value::SimValueInner<()>` + --> src/sim/value.rs + | + | struct SimValueInner { + | ^^^^^^^^^^^^^ +note: required because it appears within the type `UnsafeCell>` + --> $RUST/core/src/cell.rs + | + | pub struct UnsafeCell { + | ^^^^^^^^^^ +note: required because it appears within the type `util::alternating_cell::AlternatingCell>` + --> src/util/alternating_cell.rs + | + | pub(crate) struct AlternatingCell { + | ^^^^^^^^^^^^^^^ +note: required because it appears within the type `SimValue<()>` + --> src/sim/value.rs + | + | pub struct SimValue { + | ^^^^^^^^ +note: required by a bound in `intern_sized` + --> src/intern.rs + | + | pub trait Intern: Any + Send + Sync { + | ^^^^ required by this bound in `Intern::intern_sized` + | fn intern(&self) -> Interned; + | fn intern_sized(self) -> Interned + | ------------ required by a bound in this associated function +help: consider dereferencing here + | +12 | Intern::intern_sized(*v) + | + + +error[E0277]: `Cell` cannot be shared between threads safely + --> tests/ui/simvalue_is_not_internable.rs:12:5 + | +12 | Intern::intern_sized(v) + | ^^^^^^^^^^^^^^^^^^^^^^^ `Cell` cannot be shared between threads safely + | + = help: within `SimValue<()>`, the trait `Sync` is not implemented for `Cell` + = note: if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` +note: required because it appears within the type `util::alternating_cell::AlternatingCell>` + --> src/util/alternating_cell.rs + | + | pub(crate) struct AlternatingCell { + | ^^^^^^^^^^^^^^^ +note: required because it appears within the type `SimValue<()>` + --> src/sim/value.rs + | + | pub struct SimValue { + | ^^^^^^^^ +note: required by a bound in `fayalite::intern::Interned` + --> src/intern.rs + | + | pub struct Interned { + | ^^^^ required by this bound in `Interned` + +error[E0277]: `UnsafeCell>` cannot be shared between threads safely + --> tests/ui/simvalue_is_not_internable.rs:12:5 + | +12 | Intern::intern_sized(v) + | ^^^^^^^^^^^^^^^^^^^^^^^ `UnsafeCell>` cannot be shared between threads safely + | + = help: within `SimValue<()>`, the trait `Sync` is not implemented for `UnsafeCell>` +note: required because it appears within the type `util::alternating_cell::AlternatingCell>` + --> src/util/alternating_cell.rs + | + | pub(crate) struct AlternatingCell { + | ^^^^^^^^^^^^^^^ +note: required because it appears within the type `SimValue<()>` + --> src/sim/value.rs + | + | pub struct SimValue { + | ^^^^^^^^ +note: required by a bound in `fayalite::intern::Interned` + --> src/intern.rs + | + | pub struct Interned { + | ^^^^ required by this bound in `Interned` + +error[E0277]: `Rc<(dyn value::sim_only_value_unsafe::DynSimOnlyValueTrait + 'static)>` cannot be sent between threads safely + --> tests/ui/simvalue_is_not_internable.rs:12:5 + | +12 | Intern::intern_sized(v) + | ^^^^^^^^^^^^^^^^^^^^^^^ `Rc<(dyn value::sim_only_value_unsafe::DynSimOnlyValueTrait + 'static)>` cannot be sent between threads safely + | + = help: within `SimValue<()>`, the trait `Send` is not implemented for `Rc<(dyn value::sim_only_value_unsafe::DynSimOnlyValueTrait + 'static)>` +note: required because it appears within the type `DynSimOnlyValue` + --> src/sim/value/sim_only_value_unsafe.rs + | + | pub struct DynSimOnlyValue(Rc); + | ^^^^^^^^^^^^^^^ +note: required because it appears within the type `PhantomData` + --> $RUST/core/src/marker.rs + | + | pub struct PhantomData; + | ^^^^^^^^^^^ +note: required because it appears within the type `alloc::raw_vec::RawVec` + --> $RUST/alloc/src/raw_vec/mod.rs + | + | pub(crate) struct RawVec { + | ^^^^^^ +note: required because it appears within the type `Vec` + --> $RUST/alloc/src/vec/mod.rs + | + | pub struct Vec { + | ^^^ +note: required because it appears within the type `OpaqueSimValue` + --> src/ty.rs + | + | pub struct OpaqueSimValue { + | ^^^^^^^^^^^^^^ +note: required because it appears within the type `value::SimValueInner<()>` + --> src/sim/value.rs + | + | struct SimValueInner { + | ^^^^^^^^^^^^^ +note: required because it appears within the type `UnsafeCell>` + --> $RUST/core/src/cell.rs + | + | pub struct UnsafeCell { + | ^^^^^^^^^^ +note: required because it appears within the type `util::alternating_cell::AlternatingCell>` + --> src/util/alternating_cell.rs + | + | pub(crate) struct AlternatingCell { + | ^^^^^^^^^^^^^^^ +note: required because it appears within the type `SimValue<()>` + --> src/sim/value.rs + | + | pub struct SimValue { + | ^^^^^^^^ +note: required by a bound in `fayalite::intern::Interned` + --> src/intern.rs + | + | pub struct Interned { + | ^^^^ required by this bound in `Interned` diff --git a/crates/fayalite/visit_types.json b/crates/fayalite/visit_types.json index 3eff1f5..effbd82 100644 --- a/crates/fayalite/visit_types.json +++ b/crates/fayalite/visit_types.json @@ -49,7 +49,9 @@ "AsyncReset": "Visible", "SyncReset": "Visible", "Reset": "Visible", - "Clock": "Visible" + "Clock": "Visible", + "PhantomConst": "Visible", + "DynSimOnly": "Visible" } }, "Bundle": { @@ -159,7 +161,8 @@ "data": { "$kind": "Struct", "verilog_name": "Visible", - "parameters": "Visible" + "parameters": "Visible", + "simulation": "Visible" } }, "ExternModuleParameter": { @@ -1262,6 +1265,22 @@ "ArrayElement": "Visible", "DynArrayElement": "Visible" } + }, + "PhantomConst": { + "data": { + "$kind": "Opaque" + }, + "generics": "" + }, + "DynSimOnly": { + "data": { + "$kind": "Opaque" + } + }, + "ExternModuleSimulation": { + "data": { + "$kind": "ManualImpl" + } } } } \ No newline at end of file